mtd: spi-nor: spansion: use CLPEF as an alternative to CLSR

Infineon S28Hx (SEMPER Octal) and S25FS256T (SEMPER Nano) support Clear
Program and Erase Failure Flags (CLPEF, 82h) instead of CLSR(30h).
Introduce a new mfr_flag together with the infrastructure to allow
manufacturer private data in the core. With this we remove the need
to have if checks in the code at runtime and instead set the correct
opcodes at probe time. S25Hx (SEMPER QSPI) supports CLSR but it may
be disabled by CFR3x[2] while CLPEF is always available. Therefore,
the mfr_flag is also applied to S25Hx for safety.

Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
Link: https://lore.kernel.org/r/20230726075257.12985-2-tudor.ambarus@linaro.org
Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
This commit is contained in:
Takahiro Kuwano 2023-07-26 10:52:47 +03:00 committed by Tudor Ambarus
parent abfac0f3a4
commit d534fd9787
No known key found for this signature in database
GPG Key ID: 4B554F47A58D14E9
10 changed files with 103 additions and 32 deletions

View File

@ -48,9 +48,11 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
.is_locked = at25fs_nor_is_locked, .is_locked = at25fs_nor_is_locked,
}; };
static void at25fs_nor_late_init(struct spi_nor *nor) static int at25fs_nor_late_init(struct spi_nor *nor)
{ {
nor->params->locking_ops = &at25fs_nor_locking_ops; nor->params->locking_ops = &at25fs_nor_locking_ops;
return 0;
} }
static const struct spi_nor_fixups at25fs_nor_fixups = { static const struct spi_nor_fixups at25fs_nor_fixups = {
@ -149,9 +151,11 @@ static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
.is_locked = atmel_nor_is_global_protected, .is_locked = atmel_nor_is_global_protected,
}; };
static void atmel_nor_global_protection_late_init(struct spi_nor *nor) static int atmel_nor_global_protection_late_init(struct spi_nor *nor)
{ {
nor->params->locking_ops = &atmel_nor_global_protection_ops; nor->params->locking_ops = &atmel_nor_global_protection_ops;
return 0;
} }
static const struct spi_nor_fixups atmel_nor_global_protection_fixups = { static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {

View File

@ -2900,16 +2900,23 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
* SFDP standard, or where SFDP tables are not defined at all. * SFDP standard, or where SFDP tables are not defined at all.
* Will replace the spi_nor_manufacturer_init_params() method. * Will replace the spi_nor_manufacturer_init_params() method.
*/ */
static void spi_nor_late_init_params(struct spi_nor *nor) static int spi_nor_late_init_params(struct spi_nor *nor)
{ {
struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_flash_parameter *params = nor->params;
int ret;
if (nor->manufacturer && nor->manufacturer->fixups && if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->late_init) nor->manufacturer->fixups->late_init) {
nor->manufacturer->fixups->late_init(nor); ret = nor->manufacturer->fixups->late_init(nor);
if (ret)
return ret;
}
if (nor->info->fixups && nor->info->fixups->late_init) if (nor->info->fixups && nor->info->fixups->late_init) {
nor->info->fixups->late_init(nor); ret = nor->info->fixups->late_init(nor);
if (ret)
return ret;
}
/* Default method kept for backward compatibility. */ /* Default method kept for backward compatibility. */
if (!params->set_4byte_addr_mode) if (!params->set_4byte_addr_mode)
@ -2927,6 +2934,8 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
if (nor->info->n_banks > 1) if (nor->info->n_banks > 1)
params->bank_size = div64_u64(params->size, nor->info->n_banks); params->bank_size = div64_u64(params->size, nor->info->n_banks);
return 0;
} }
/** /**
@ -3085,9 +3094,7 @@ static int spi_nor_init_params(struct spi_nor *nor)
spi_nor_init_params_deprecated(nor); spi_nor_init_params_deprecated(nor);
} }
spi_nor_late_init_params(nor); return spi_nor_late_init_params(nor);
return 0;
} }
/** spi_nor_set_octal_dtr() - enable or disable Octal DTR I/O. /** spi_nor_set_octal_dtr() - enable or disable Octal DTR I/O.

View File

@ -378,6 +378,7 @@ struct spi_nor_otp {
* than reading the status register to indicate they * than reading the status register to indicate they
* are ready for a new command * are ready for a new command
* @locking_ops: SPI NOR locking methods. * @locking_ops: SPI NOR locking methods.
* @priv: flash's private data.
*/ */
struct spi_nor_flash_parameter { struct spi_nor_flash_parameter {
u64 bank_size; u64 bank_size;
@ -406,6 +407,7 @@ struct spi_nor_flash_parameter {
int (*ready)(struct spi_nor *nor); int (*ready)(struct spi_nor *nor);
const struct spi_nor_locking_ops *locking_ops; const struct spi_nor_locking_ops *locking_ops;
void *priv;
}; };
/** /**
@ -432,7 +434,7 @@ struct spi_nor_fixups {
const struct sfdp_parameter_header *bfpt_header, const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt); const struct sfdp_bfpt *bfpt);
int (*post_sfdp)(struct spi_nor *nor); int (*post_sfdp)(struct spi_nor *nor);
void (*late_init)(struct spi_nor *nor); int (*late_init)(struct spi_nor *nor);
}; };
/** /**

View File

@ -29,7 +29,7 @@ static const struct spi_nor_fixups is25lp256_fixups = {
.post_bfpt = is25lp256_post_bfpt_fixups, .post_bfpt = is25lp256_post_bfpt_fixups,
}; };
static void pm25lv_nor_late_init(struct spi_nor *nor) static int pm25lv_nor_late_init(struct spi_nor *nor)
{ {
struct spi_nor_erase_map *map = &nor->params->erase_map; struct spi_nor_erase_map *map = &nor->params->erase_map;
int i; int i;
@ -38,6 +38,8 @@ static void pm25lv_nor_late_init(struct spi_nor *nor)
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
if (map->erase_type[i].size == 4096) if (map->erase_type[i].size == 4096)
map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC; map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC;
return 0;
} }
static const struct spi_nor_fixups pm25lv_nor_fixups = { static const struct spi_nor_fixups pm25lv_nor_fixups = {

View File

@ -110,10 +110,12 @@ static void macronix_nor_default_init(struct spi_nor *nor)
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
} }
static void macronix_nor_late_init(struct spi_nor *nor) static int macronix_nor_late_init(struct spi_nor *nor)
{ {
if (!nor->params->set_4byte_addr_mode) if (!nor->params->set_4byte_addr_mode)
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
return 0;
} }
static const struct spi_nor_fixups macronix_nor_fixups = { static const struct spi_nor_fixups macronix_nor_fixups = {

View File

@ -429,7 +429,7 @@ static void micron_st_nor_default_init(struct spi_nor *nor)
nor->params->quad_enable = NULL; nor->params->quad_enable = NULL;
} }
static void micron_st_nor_late_init(struct spi_nor *nor) static int micron_st_nor_late_init(struct spi_nor *nor)
{ {
struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_flash_parameter *params = nor->params;
@ -438,6 +438,8 @@ static void micron_st_nor_late_init(struct spi_nor *nor)
if (!params->set_4byte_addr_mode) if (!params->set_4byte_addr_mode)
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b; params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
return 0;
} }
static const struct spi_nor_fixups micron_st_nor_fixups = { static const struct spi_nor_fixups micron_st_nor_fixups = {

View File

@ -4,14 +4,17 @@
* Copyright (C) 2014, Freescale Semiconductor, Inc. * Copyright (C) 2014, Freescale Semiconductor, Inc.
*/ */
#include <linux/device.h>
#include <linux/mtd/spi-nor.h> #include <linux/mtd/spi-nor.h>
#include "core.h" #include "core.h"
/* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */ /* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */
#define USE_CLSR BIT(0) #define USE_CLSR BIT(0)
#define USE_CLPEF BIT(1)
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
#define SPINOR_REG_CYPRESS_VREG 0x00800000 #define SPINOR_REG_CYPRESS_VREG 0x00800000
@ -57,22 +60,32 @@
SPI_MEM_OP_DUMMY(ndummy, 0), \ SPI_MEM_OP_DUMMY(ndummy, 0), \
SPI_MEM_OP_DATA_IN(1, buf, 0)) SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPANSION_CLSR_OP \ #define SPANSION_OP(opcode) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \ SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA) SPI_MEM_OP_NO_DATA)
/**
* struct spansion_nor_params - Spansion private parameters.
* @clsr: Clear Status Register or Clear Program and Erase Failure Flag
* opcode.
*/
struct spansion_nor_params {
u8 clsr;
};
/** /**
* spansion_nor_clear_sr() - Clear the Status Register. * spansion_nor_clear_sr() - Clear the Status Register.
* @nor: pointer to 'struct spi_nor'. * @nor: pointer to 'struct spi_nor'.
*/ */
static void spansion_nor_clear_sr(struct spi_nor *nor) static void spansion_nor_clear_sr(struct spi_nor *nor)
{ {
const struct spansion_nor_params *priv_params = nor->params->priv;
int ret; int ret;
if (nor->spimem) { if (nor->spimem) {
struct spi_mem_op op = SPANSION_CLSR_OP; struct spi_mem_op op = SPANSION_OP(priv_params->clsr);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -528,9 +541,11 @@ static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor)
return 0; return 0;
} }
static void s25fs256t_late_init(struct spi_nor *nor) static int s25fs256t_late_init(struct spi_nor *nor)
{ {
cypress_nor_ecc_init(nor); cypress_nor_ecc_init(nor);
return 0;
} }
static struct spi_nor_fixups s25fs256t_fixups = { static struct spi_nor_fixups s25fs256t_fixups = {
@ -586,7 +601,7 @@ static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor)
return cypress_nor_get_page_size(nor); return cypress_nor_get_page_size(nor);
} }
static void s25hx_t_late_init(struct spi_nor *nor) static int s25hx_t_late_init(struct spi_nor *nor)
{ {
struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_flash_parameter *params = nor->params;
@ -598,6 +613,8 @@ static void s25hx_t_late_init(struct spi_nor *nor)
/* Replace ready() with multi die version */ /* Replace ready() with multi die version */
if (params->n_dice) if (params->n_dice)
params->ready = cypress_nor_sr_ready_and_clear; params->ready = cypress_nor_sr_ready_and_clear;
return 0;
} }
static struct spi_nor_fixups s25hx_t_fixups = { static struct spi_nor_fixups s25hx_t_fixups = {
@ -659,10 +676,12 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
return cypress_nor_set_addr_mode_nbytes(nor); return cypress_nor_set_addr_mode_nbytes(nor);
} }
static void s28hx_t_late_init(struct spi_nor *nor) static int s28hx_t_late_init(struct spi_nor *nor)
{ {
nor->params->set_octal_dtr = cypress_nor_set_octal_dtr; nor->params->set_octal_dtr = cypress_nor_set_octal_dtr;
cypress_nor_ecc_init(nor); cypress_nor_ecc_init(nor);
return 0;
} }
static const struct spi_nor_fixups s28hx_t_fixups = { static const struct spi_nor_fixups s28hx_t_fixups = {
@ -786,47 +805,54 @@ static const struct flash_info spansion_nor_parts[] = {
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
{ "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0) { "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s25fs256t_fixups }, .fixups = &s25fs256t_fixups },
{ "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256) { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLSR) MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512) { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLSR) MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0) { "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
FLAGS(NO_CHIP_ERASE) FLAGS(NO_CHIP_ERASE)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256) { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLSR) MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512) { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLSR) MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0) { "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
FLAGS(NO_CHIP_ERASE) FLAGS(NO_CHIP_ERASE)
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE) }, FLAGS(SPI_NOR_NO_ERASE) },
{ "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256) { "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups, .fixups = &s28hx_t_fixups,
}, },
{ "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512) { "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups, .fixups = &s28hx_t_fixups,
}, },
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups, .fixups = &s28hx_t_fixups,
}, },
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512) { "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
PARSE_SFDP PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups, .fixups = &s28hx_t_fixups,
}, },
}; };
@ -870,17 +896,35 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor)
return !(nor->bouncebuf[0] & SR_WIP); return !(nor->bouncebuf[0] & SR_WIP);
} }
static void spansion_nor_late_init(struct spi_nor *nor) static int spansion_nor_late_init(struct spi_nor *nor)
{ {
if (nor->params->size > SZ_16M) { struct spi_nor_flash_parameter *params = nor->params;
struct spansion_nor_params *priv_params;
u8 mfr_flags = nor->info->mfr_flags;
if (params->size > SZ_16M) {
nor->flags |= SNOR_F_4B_OPCODES; nor->flags |= SNOR_F_4B_OPCODES;
/* No small sector erase for 4-byte command set */ /* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE; nor->erase_opcode = SPINOR_OP_SE;
nor->mtd.erasesize = nor->info->sector_size; nor->mtd.erasesize = nor->info->sector_size;
} }
if (nor->info->mfr_flags & USE_CLSR) if (mfr_flags & (USE_CLSR | USE_CLPEF)) {
nor->params->ready = spansion_nor_sr_ready_and_clear; priv_params = devm_kmalloc(nor->dev, sizeof(*priv_params),
GFP_KERNEL);
if (!priv_params)
return -ENOMEM;
if (mfr_flags & USE_CLSR)
priv_params->clsr = SPINOR_OP_CLSR;
else if (mfr_flags & USE_CLPEF)
priv_params->clsr = SPINOR_OP_CLPEF;
params->priv = priv_params;
params->ready = spansion_nor_sr_ready_and_clear;
}
return 0;
} }
static const struct spi_nor_fixups spansion_nor_fixups = { static const struct spi_nor_fixups spansion_nor_fixups = {

View File

@ -49,9 +49,11 @@ static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = {
.is_locked = sst26vf_nor_is_locked, .is_locked = sst26vf_nor_is_locked,
}; };
static void sst26vf_nor_late_init(struct spi_nor *nor) static int sst26vf_nor_late_init(struct spi_nor *nor)
{ {
nor->params->locking_ops = &sst26vf_nor_locking_ops; nor->params->locking_ops = &sst26vf_nor_locking_ops;
return 0;
} }
static const struct spi_nor_fixups sst26vf_nor_fixups = { static const struct spi_nor_fixups sst26vf_nor_fixups = {
@ -203,10 +205,12 @@ out:
return ret; return ret;
} }
static void sst_nor_late_init(struct spi_nor *nor) static int sst_nor_late_init(struct spi_nor *nor)
{ {
if (nor->info->mfr_flags & SST_WRITE) if (nor->info->mfr_flags & SST_WRITE)
nor->mtd._write = sst_nor_write; nor->mtd._write = sst_nor_write;
return 0;
} }
static const struct spi_nor_fixups sst_nor_fixups = { static const struct spi_nor_fixups sst_nor_fixups = {

View File

@ -217,7 +217,7 @@ static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
.is_locked = spi_nor_otp_is_locked_sr2, .is_locked = spi_nor_otp_is_locked_sr2,
}; };
static void winbond_nor_late_init(struct spi_nor *nor) static int winbond_nor_late_init(struct spi_nor *nor)
{ {
struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_flash_parameter *params = nor->params;
@ -233,6 +233,8 @@ static void winbond_nor_late_init(struct spi_nor *nor)
* from BFPT, if any. * from BFPT, if any.
*/ */
params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
return 0;
} }
static const struct spi_nor_fixups winbond_nor_fixups = { static const struct spi_nor_fixups winbond_nor_fixups = {

View File

@ -155,10 +155,12 @@ static int xilinx_nor_setup(struct spi_nor *nor,
return 0; return 0;
} }
static void xilinx_nor_late_init(struct spi_nor *nor) static int xilinx_nor_late_init(struct spi_nor *nor)
{ {
nor->params->setup = xilinx_nor_setup; nor->params->setup = xilinx_nor_setup;
nor->params->ready = xilinx_nor_sr_ready; nor->params->ready = xilinx_nor_sr_ready;
return 0;
} }
static const struct spi_nor_fixups xilinx_nor_fixups = { static const struct spi_nor_fixups xilinx_nor_fixups = {