SPI NOR core changes:

* fix assumption on enabling quad mode in
   spi_nor_write_16bit_sr_and_check()
 * avoid setting SRWD bit in SR if WP# signal not connected as it will
   configure the SR permanently as read only. Add "no-wp" dt property.
 * clarify the need for spi-nor compatibles in dt-bindings
 
 SPI NOR manufacturer drivers changes:
 * spansion:
   - add support for S28HS02GT
   - switch methods to use vreg_offset from SFDP instead of hardcoding
     the register value
 * microchip/sst:
   - add support for sst26vf032b flash
 * winbond:
   - correct flags for Winbond w25q128
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEHUIqys8OyG1eHf7fS1VPR6WNFOkFAmTfS0cACgkQS1VPR6WN
 FOmCKwf/UJEfKyMsWlgO5WRqxzYsPow5SjjMY+XRLS+V6zpQZb//+COdESEbm5bL
 1Lmo8e1J/I17sVn80KwHvnen3z+Nd6HqpgIRIlMWkitieiWgnGsAVqOpiR+5yY/n
 ki0gz4ne2nrLxB3223zqV5wYr6s1A7ZCOqAZQNPUjOkbHDkBVeex9p36jXStjR4w
 exbRNijsEYMrtthsuUY3rJd1n1a6yvaYkubclT/H6myzPgzeyaeWRw5cHjVBKh9D
 SQ1NUBEdljciyosJ4ugfsG6jnG7DpVacr7Zxl5F6caUmeiNBG/dgUvkIZzIxffkB
 BRvg0czpomB6sKEdY0j1QDPt0rtrdw==
 =uEXl
 -----END PGP SIGNATURE-----

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

SPI NOR core changes:
* fix assumption on enabling quad mode in
  spi_nor_write_16bit_sr_and_check()
* avoid setting SRWD bit in SR if WP# signal not connected as it will
  configure the SR permanently as read only. Add "no-wp" dt property.
* clarify the need for spi-nor compatibles in dt-bindings

SPI NOR manufacturer drivers changes:
* spansion:
  - add support for S28HS02GT
  - switch methods to use vreg_offset from SFDP instead of hardcoding
    the register value
* microchip/sst:
  - add support for sst26vf032b flash
* winbond:
  - correct flags for Winbond w25q128

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
Miquel Raynal 2023-08-18 16:39:53 +02:00
commit f7091fb725
14 changed files with 313 additions and 186 deletions

View File

@ -43,8 +43,10 @@ properties:
- const: jedec,spi-nor
- const: jedec,spi-nor
description:
Must also include "jedec,spi-nor" for any SPI NOR flash that can be
identified by the JEDEC READ ID opcode (0x9F).
SPI NOR flashes compatible with the JEDEC SFDP standard or which may be
identified with the READ ID opcode (0x9F) do not deserve a specific
compatible. They should instead only be matched against the generic
"jedec,spi-nor" compatible.
reg:
minItems: 1
@ -70,6 +72,21 @@ properties:
be used on such systems, to denote the absence of a reliable reset
mechanism.
no-wp:
type: boolean
description:
The status register write disable (SRWD) bit in status register, combined
with the WP# signal, provides hardware data protection for the device. When
the SRWD bit is set to 1, and the WP# signal is either driven LOW or hard
strapped to LOW, the status register nonvolatile bits become read-only and
the WRITE STATUS REGISTER operation will not execute. The only way to exit
this hardware-protected mode is to drive WP# HIGH. If the WP# signal of the
flash device is not connected or is wrongly tied to GND (that includes internal
pull-downs) then status register permanently becomes read-only as the SRWD bit
cannot be reset. This boolean flag can be used on such systems to avoid setting
the SRWD bit while writing the status register. WP# signal hard strapped to GND
can be a valid use case.
reset-gpios:
description:
A GPIO line connected to the RESET (active low) signal of the device.

View File

@ -48,9 +48,11 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
.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;
return 0;
}
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,
};
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;
return 0;
}
static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {

View File

@ -394,30 +394,18 @@ static int nxp_spifi_probe(struct platform_device *pdev)
if (IS_ERR(spifi->flash_base))
return PTR_ERR(spifi->flash_base);
spifi->clk_spifi = devm_clk_get(&pdev->dev, "spifi");
spifi->clk_spifi = devm_clk_get_enabled(&pdev->dev, "spifi");
if (IS_ERR(spifi->clk_spifi)) {
dev_err(&pdev->dev, "spifi clock not found\n");
dev_err(&pdev->dev, "spifi clock not found or unable to enable\n");
return PTR_ERR(spifi->clk_spifi);
}
spifi->clk_reg = devm_clk_get(&pdev->dev, "reg");
spifi->clk_reg = devm_clk_get_enabled(&pdev->dev, "reg");
if (IS_ERR(spifi->clk_reg)) {
dev_err(&pdev->dev, "reg clock not found\n");
dev_err(&pdev->dev, "reg clock not found or unable to enable\n");
return PTR_ERR(spifi->clk_reg);
}
ret = clk_prepare_enable(spifi->clk_reg);
if (ret) {
dev_err(&pdev->dev, "unable to enable reg clock\n");
return ret;
}
ret = clk_prepare_enable(spifi->clk_spifi);
if (ret) {
dev_err(&pdev->dev, "unable to enable spifi clock\n");
goto dis_clk_reg;
}
spifi->dev = &pdev->dev;
platform_set_drvdata(pdev, spifi);
@ -430,24 +418,17 @@ static int nxp_spifi_probe(struct platform_device *pdev)
flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
if (!flash_np) {
dev_err(&pdev->dev, "no SPI flash device to configure\n");
ret = -ENODEV;
goto dis_clks;
return -ENODEV;
}
ret = nxp_spifi_setup_flash(spifi, flash_np);
of_node_put(flash_np);
if (ret) {
dev_err(&pdev->dev, "unable to setup flash chip\n");
goto dis_clks;
return ret;
}
return 0;
dis_clks:
clk_disable_unprepare(spifi->clk_spifi);
dis_clk_reg:
clk_disable_unprepare(spifi->clk_reg);
return ret;
}
static int nxp_spifi_remove(struct platform_device *pdev)
@ -455,8 +436,6 @@ static int nxp_spifi_remove(struct platform_device *pdev)
struct nxp_spifi *spifi = platform_get_drvdata(pdev);
mtd_device_unregister(&spifi->nor.mtd);
clk_disable_unprepare(spifi->clk_spifi);
clk_disable_unprepare(spifi->clk_reg);
return 0;
}

View File

@ -870,21 +870,22 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
ret = spi_nor_read_cr(nor, &sr_cr[1]);
if (ret)
return ret;
} else if (nor->params->quad_enable) {
} 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.
*
* We can safely assume that when the Quad Enable method is
* set, the value of the QE bit is one, as a consequence of the
* nor->params->quad_enable() call.
* 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.
*
* We can safely assume that the Quad Enable bit is present in
* the Status Register 2 at BIT(1). 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).
* 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 {
@ -2844,6 +2845,9 @@ static void spi_nor_init_flags(struct spi_nor *nor)
if (of_property_read_bool(np, "broken-flash-reset"))
nor->flags |= SNOR_F_BROKEN_RESET;
if (of_property_read_bool(np, "no-wp"))
nor->flags |= SNOR_F_NO_WP;
if (flags & SPI_NOR_SWP_IS_VOLATILE)
nor->flags |= SNOR_F_SWP_IS_VOLATILE;
@ -2897,16 +2901,23 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
* SFDP standard, or where SFDP tables are not defined at all.
* 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;
int ret;
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->late_init)
nor->manufacturer->fixups->late_init(nor);
nor->manufacturer->fixups->late_init) {
ret = nor->manufacturer->fixups->late_init(nor);
if (ret)
return ret;
}
if (nor->info->fixups && nor->info->fixups->late_init)
nor->info->fixups->late_init(nor);
if (nor->info->fixups && nor->info->fixups->late_init) {
ret = nor->info->fixups->late_init(nor);
if (ret)
return ret;
}
/* Default method kept for backward compatibility. */
if (!params->set_4byte_addr_mode)
@ -2924,6 +2935,8 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
if (nor->info->n_banks > 1)
params->bank_size = div64_u64(params->size, nor->info->n_banks);
return 0;
}
/**
@ -3082,22 +3095,20 @@ static int spi_nor_init_params(struct spi_nor *nor)
spi_nor_init_params_deprecated(nor);
}
spi_nor_late_init_params(nor);
return 0;
return spi_nor_late_init_params(nor);
}
/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
/** spi_nor_set_octal_dtr() - enable or disable Octal DTR I/O.
* @nor: pointer to a 'struct spi_nor'
* @enable: whether to enable or disable Octal DTR
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
static int spi_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
{
int ret;
if (!nor->params->octal_dtr_enable)
if (!nor->params->set_octal_dtr)
return 0;
if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR &&
@ -3107,7 +3118,7 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE))
return 0;
ret = nor->params->octal_dtr_enable(nor, enable);
ret = nor->params->set_octal_dtr(nor, enable);
if (ret)
return ret;
@ -3168,7 +3179,7 @@ static int spi_nor_init(struct spi_nor *nor)
{
int err;
err = spi_nor_octal_dtr_enable(nor, true);
err = spi_nor_set_octal_dtr(nor, true);
if (err) {
dev_dbg(nor->dev, "octal mode not supported\n");
return err;
@ -3270,7 +3281,7 @@ static int spi_nor_suspend(struct mtd_info *mtd)
int ret;
/* Disable octal DTR mode if we enabled it. */
ret = spi_nor_octal_dtr_enable(nor, false);
ret = spi_nor_set_octal_dtr(nor, false);
if (ret)
dev_err(nor->dev, "suspend() failed\n");

View File

@ -132,6 +132,7 @@ enum spi_nor_option_flags {
SNOR_F_SWP_IS_VOLATILE = BIT(13),
SNOR_F_RWW = BIT(14),
SNOR_F_ECC = BIT(15),
SNOR_F_NO_WP = BIT(16),
};
struct spi_nor_read_command {
@ -363,7 +364,7 @@ struct spi_nor_otp {
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
* @otp: SPI NOR OTP info.
* @octal_dtr_enable: enables SPI NOR octal DTR mode.
* @set_octal_dtr: enables or disables SPI NOR octal DTR mode.
* @quad_enable: enables SPI NOR quad mode.
* @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode.
* @convert_addr: converts an absolute address into something the flash
@ -377,6 +378,7 @@ struct spi_nor_otp {
* than reading the status register to indicate they
* are ready for a new command
* @locking_ops: SPI NOR locking methods.
* @priv: flash's private data.
*/
struct spi_nor_flash_parameter {
u64 bank_size;
@ -397,7 +399,7 @@ struct spi_nor_flash_parameter {
struct spi_nor_erase_map erase_map;
struct spi_nor_otp otp;
int (*octal_dtr_enable)(struct spi_nor *nor, bool enable);
int (*set_octal_dtr)(struct spi_nor *nor, bool enable);
int (*quad_enable)(struct spi_nor *nor);
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
@ -405,6 +407,7 @@ struct spi_nor_flash_parameter {
int (*ready)(struct spi_nor *nor);
const struct spi_nor_locking_ops *locking_ops;
void *priv;
};
/**
@ -431,7 +434,7 @@ struct spi_nor_fixups {
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt);
int (*post_sfdp)(struct spi_nor *nor);
void (*late_init)(struct spi_nor *nor);
int (*late_init)(struct spi_nor *nor);
};
/**

View File

@ -27,6 +27,7 @@ static const char *const snor_f_names[] = {
SNOR_F_NAME(SWP_IS_VOLATILE),
SNOR_F_NAME(RWW),
SNOR_F_NAME(ECC),
SNOR_F_NAME(NO_WP),
};
#undef SNOR_F_NAME

View File

@ -29,7 +29,7 @@ static const struct spi_nor_fixups is25lp256_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;
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++)
if (map->erase_type[i].size == 4096)
map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC;
return 0;
}
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;
}
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)
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 = {

View File

@ -120,7 +120,7 @@ static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
return 0;
}
static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
{
return enable ? micron_st_nor_octal_dtr_en(nor) :
micron_st_nor_octal_dtr_dis(nor);
@ -128,7 +128,7 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
static void mt35xu512aba_default_init(struct spi_nor *nor)
{
nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable;
nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr;
}
static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
@ -429,7 +429,7 @@ static void micron_st_nor_default_init(struct spi_nor *nor)
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;
@ -438,6 +438,8 @@ static void 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;
return 0;
}
static const struct spi_nor_fixups micron_st_nor_fixups = {

View File

@ -4,14 +4,19 @@
* Copyright (C) 2014, Freescale Semiconductor, Inc.
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mtd/spi-nor.h>
#include "core.h"
/* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */
#define USE_CLSR BIT(0)
#define USE_CLPEF BIT(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_WR_ANY_REG 0x71 /* Write any register */
#define SPINOR_REG_CYPRESS_VREG 0x00800000
@ -19,21 +24,16 @@
#define SPINOR_REG_CYPRESS_STR1V \
(SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_STR1)
#define SPINOR_REG_CYPRESS_CFR1 0x2
#define SPINOR_REG_CYPRESS_CFR1V \
(SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR1)
#define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */
#define SPINOR_REG_CYPRESS_CFR2 0x3
#define SPINOR_REG_CYPRESS_CFR2V \
(SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR2)
#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK GENMASK(3, 0)
#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb
#define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7)
#define SPINOR_REG_CYPRESS_CFR3 0x4
#define SPINOR_REG_CYPRESS_CFR3V \
(SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR3)
#define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */
#define SPINOR_REG_CYPRESS_CFR5 0x6
#define SPINOR_REG_CYPRESS_CFR5V \
(SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR5)
#define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6)
#define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1)
#define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0)
@ -57,22 +57,32 @@
SPI_MEM_OP_DUMMY(ndummy, 0), \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPANSION_CLSR_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \
#define SPANSION_OP(opcode) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
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.
* @nor: pointer to 'struct spi_nor'.
*/
static void spansion_nor_clear_sr(struct spi_nor *nor)
{
const struct spansion_nor_params *priv_params = nor->params->priv;
int ret;
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);
@ -88,11 +98,17 @@ static void spansion_nor_clear_sr(struct spi_nor *nor)
static int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr)
{
struct spi_nor_flash_parameter *params = nor->params;
struct spi_mem_op op =
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, addr,
CYPRESS_NOR_RD_ANY_REG_OP(params->addr_mode_nbytes, addr,
0, nor->bouncebuf);
int ret;
if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
op.dummy.nbytes = params->rdsr_dummy;
op.data.nbytes = 2;
}
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
@ -141,18 +157,26 @@ static int cypress_nor_sr_ready_and_clear(struct spi_nor *nor)
return 1;
}
static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
static int cypress_nor_set_memlat(struct spi_nor *nor, u64 addr)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
/* Use 24 dummy cycles for memory array reads. */
*buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR2V, 1, buf);
CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, buf);
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* Use 24 dummy cycles for memory array reads. */
*buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK;
*buf |= FIELD_PREP(SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK,
SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24);
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
@ -160,15 +184,41 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
nor->read_dummy = 24;
return 0;
}
static int cypress_nor_set_octal_dtr_bits(struct spi_nor *nor, u64 addr)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
/* Set the octal and DTR enable bits. */
buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR5V, 1, buf);
CYPRESS_NOR_WR_ANY_REG_OP(nor->params->addr_mode_nbytes,
addr, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
}
static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
{
const struct spi_nor_flash_parameter *params = nor->params;
u8 *buf = nor->bouncebuf;
u64 addr;
int i, ret;
for (i = 0; i < params->n_dice; i++) {
addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR2;
ret = cypress_nor_set_memlat(nor, addr);
if (ret)
return ret;
addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5;
ret = cypress_nor_set_octal_dtr_bits(nor, addr);
if (ret)
return ret;
}
/* Read flash ID to make sure the switch was successful. */
ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf,
@ -184,11 +234,10 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
return 0;
}
static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
static int cypress_nor_set_single_spi_bits(struct spi_nor *nor, u64 addr)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
/*
* The register is 1-byte wide, but 1-byte transactions are not allowed
@ -198,11 +247,23 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS;
buf[1] = 0;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
SPINOR_REG_CYPRESS_CFR5V, 2, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
if (ret)
return ret;
CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, addr, 2, buf);
return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
}
static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
{
const struct spi_nor_flash_parameter *params = nor->params;
u8 *buf = nor->bouncebuf;
u64 addr;
int i, ret;
for (i = 0; i < params->n_dice; i++) {
addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5;
ret = cypress_nor_set_single_spi_bits(nor, addr);
if (ret)
return ret;
}
/* Read flash ID to make sure the switch was successful. */
ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1);
@ -283,10 +344,6 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
u8 i;
int ret;
if (!params->n_dice)
return cypress_nor_quad_enable_volatile_reg(nor,
SPINOR_REG_CYPRESS_CFR1V);
for (i = 0; i < params->n_dice; i++) {
addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR1;
ret = cypress_nor_quad_enable_volatile_reg(nor, addr);
@ -408,28 +465,17 @@ static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor)
return 0;
}
static int cypress_nor_get_page_size_single_chip(struct spi_nor *nor)
{
struct spi_mem_op op =
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR3V, 0,
nor->bouncebuf);
int ret;
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ)
nor->params->page_size = 512;
else
nor->params->page_size = 256;
return 0;
}
static int cypress_nor_get_page_size_mcp(struct spi_nor *nor)
/**
* cypress_nor_get_page_size() - Get flash page size configuration.
* @nor: pointer to a 'struct spi_nor'
*
* The BFPT table advertises a 512B or 256B page size depending on part but the
* page size is actually configurable (with the default being 256B). Read from
* CFR3V[4] and set the correct size.
*
* Return: 0 on success, -errno otherwise.
*/
static int cypress_nor_get_page_size(struct spi_nor *nor)
{
struct spi_mem_op op =
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
@ -459,23 +505,6 @@ static int cypress_nor_get_page_size_mcp(struct spi_nor *nor)
return 0;
}
/**
* cypress_nor_get_page_size() - Get flash page size configuration.
* @nor: pointer to a 'struct spi_nor'
*
* The BFPT table advertises a 512B or 256B page size depending on part but the
* page size is actually configurable (with the default being 256B). Read from
* CFR3V[4] and set the correct size.
*
* Return: 0 on success, -errno otherwise.
*/
static int cypress_nor_get_page_size(struct spi_nor *nor)
{
if (nor->params->n_dice)
return cypress_nor_get_page_size_mcp(nor);
return cypress_nor_get_page_size_single_chip(nor);
}
static void cypress_nor_ecc_init(struct spi_nor *nor)
{
/*
@ -512,25 +541,39 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor,
if (nor->bouncebuf[0])
return -ENODEV;
return cypress_nor_get_page_size(nor);
return 0;
}
static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
/*
* S25FS256T does not define the SCCR map, but we would like to use the
* same code base for both single and multi chip package devices, thus
* set the vreg_offset and n_dice to be able to do so.
*/
params->vreg_offset = devm_kmalloc(nor->dev, sizeof(u32), GFP_KERNEL);
if (!params->vreg_offset)
return -ENOMEM;
params->vreg_offset[0] = SPINOR_REG_CYPRESS_VREG;
params->n_dice = 1;
/* PP_1_1_4_4B is supported but missing in 4BAIT. */
params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_1_1_4],
SPINOR_OP_PP_1_1_4_4B,
SNOR_PROTO_1_1_4);
return 0;
return cypress_nor_get_page_size(nor);
}
static void s25fs256t_late_init(struct spi_nor *nor)
static int s25fs256t_late_init(struct spi_nor *nor)
{
cypress_nor_ecc_init(nor);
return 0;
}
static struct spi_nor_fixups s25fs256t_fixups = {
@ -558,10 +601,20 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor)
{
struct spi_nor_erase_type *erase_type =
nor->params->erase_map.erase_type;
struct spi_nor_flash_parameter *params = nor->params;
struct spi_nor_erase_type *erase_type = params->erase_map.erase_type;
unsigned int i;
if (!params->n_dice || !params->vreg_offset) {
dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n",
__func__);
return -EOPNOTSUPP;
}
/* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */
if (params->size == SZ_256M)
params->n_dice = 2;
/*
* In some parts, 3byte erase opcodes are advertised by 4BAIT.
* Convert them to 4byte erase opcodes.
@ -579,25 +632,19 @@ static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor)
}
}
/* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */
if (nor->params->size == SZ_256M)
nor->params->n_dice = 2;
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;
/* Fast Read 4B requires mode cycles */
params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
params->ready = cypress_nor_sr_ready_and_clear;
cypress_nor_ecc_init(nor);
/* Replace ready() with multi die version */
if (params->n_dice)
params->ready = cypress_nor_sr_ready_and_clear;
return 0;
}
static struct spi_nor_fixups s25hx_t_fixups = {
@ -607,7 +654,7 @@ static struct spi_nor_fixups s25hx_t_fixups = {
};
/**
* cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
* cypress_nor_set_octal_dtr() - Enable or disable octal DTR on Cypress flashes.
* @nor: pointer to a 'struct spi_nor'
* @enable: whether to enable or disable Octal DTR
*
@ -616,7 +663,7 @@ static struct spi_nor_fixups s25hx_t_fixups = {
*
* Return: 0 on success, -errno otherwise.
*/
static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
static int cypress_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
{
return enable ? cypress_nor_octal_dtr_en(nor) :
cypress_nor_octal_dtr_dis(nor);
@ -624,22 +671,34 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
if (!params->n_dice || !params->vreg_offset) {
dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n",
__func__);
return -EOPNOTSUPP;
}
/* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */
if (params->size == SZ_256M)
params->n_dice = 2;
/*
* On older versions of the flash the xSPI Profile 1.0 table has the
* 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE.
*/
if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0)
nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode =
if (params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0)
params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode =
SPINOR_OP_CYPRESS_RD_FAST;
/* This flash is also missing the 4-byte Page Program opcode bit. */
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP],
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
/*
* Since xSPI Page Program opcode is backward compatible with
* Legacy SPI, use Legacy SPI opcode there as well.
*/
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
/*
@ -647,7 +706,7 @@ static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
* address bytes needed for Read Status Register command as 0 but the
* actual value for that is 4.
*/
nor->params->rdsr_addr_nbytes = 4;
params->rdsr_addr_nbytes = 4;
return cypress_nor_get_page_size(nor);
}
@ -656,19 +715,18 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
int ret;
ret = cypress_nor_set_addr_mode_nbytes(nor);
if (ret)
return ret;
return 0;
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->octal_dtr_enable = cypress_nor_octal_dtr_enable;
struct spi_nor_flash_parameter *params = nor->params;
params->set_octal_dtr = cypress_nor_set_octal_dtr;
params->ready = cypress_nor_sr_ready_and_clear;
cypress_nor_ecc_init(nor);
return 0;
}
static const struct spi_nor_fixups s28hx_t_fixups = {
@ -792,47 +850,59 @@ static const struct flash_info spansion_nor_parts[] = {
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
{ "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s25fs256t_fixups },
{ "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256)
{ "s25hl512t", INFO6(0x342a1a, 0x0f0390, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups },
{ "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512)
{ "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups },
{ "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
FLAGS(NO_CHIP_ERASE)
.fixups = &s25hx_t_fixups },
{ "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256)
{ "s25hs512t", INFO6(0x342b1a, 0x0f0390, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups },
{ "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512)
{ "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
MFR_FLAGS(USE_CLPEF)
.fixups = &s25hx_t_fixups },
{ "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
FLAGS(NO_CHIP_ERASE)
.fixups = &s25hx_t_fixups },
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE) },
{ "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256)
{ "s28hl512t", INFO(0x345a1a, 0, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups,
},
{ "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512)
{ "s28hl01gt", INFO(0x345a1b, 0, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups,
},
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
{ "s28hs512t", INFO(0x345b1a, 0, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups,
},
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
{ "s28hs01gt", INFO(0x345b1b, 0, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups,
},
{ "s28hs02gt", INFO(0x345b1c, 0, 0, 0)
PARSE_SFDP
MFR_FLAGS(USE_CLPEF)
.fixups = &s28hx_t_fixups,
},
};
@ -876,17 +946,35 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor)
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;
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE;
nor->mtd.erasesize = nor->info->sector_size;
}
if (nor->info->mfr_flags & USE_CLSR)
nor->params->ready = spansion_nor_sr_ready_and_clear;
if (mfr_flags & (USE_CLSR | USE_CLPEF)) {
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 = {

View File

@ -49,9 +49,11 @@ static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = {
.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;
return 0;
}
static const struct spi_nor_fixups sst26vf_nor_fixups = {
@ -111,6 +113,10 @@ static const struct flash_info sst_nor_parts[] = {
SPI_NOR_QUAD_READ) },
{ "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "sst26vf032b", INFO(0xbf2642, 0, 0, 0)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
PARSE_SFDP
.fixups = &sst26vf_nor_fixups },
{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
@ -203,10 +209,12 @@ out:
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)
nor->mtd._write = sst_nor_write;
return 0;
}
static const struct spi_nor_fixups sst_nor_fixups = {

View File

@ -214,8 +214,13 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
status_new = (status_old & ~mask & ~tb_mask) | val;
/* Disallow further writes if WP pin is asserted */
status_new |= SR_SRWD;
/*
* Disallow further writes if WP# pin is neither left floating nor
* 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))
status_new |= SR_SRWD;
if (!use_top)
status_new |= tb_mask;

View File

@ -120,8 +120,9 @@ static const struct flash_info winbond_nor_parts[] = {
NO_SFDP_FLAGS(SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 0, 0)
PARSE_SFDP
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
.fixups = &w25q256_fixups },
@ -216,7 +217,7 @@ static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
.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;
@ -232,6 +233,8 @@ static void winbond_nor_late_init(struct spi_nor *nor)
* from BFPT, if any.
*/
params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
return 0;
}
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;
}
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->ready = xilinx_nor_sr_ready;
return 0;
}
static const struct spi_nor_fixups xilinx_nor_fixups = {