- cadence-quadspi fixes (Apurva Nandan, Dhruva Gole)
- CHIP_ERASE optimization (Marek Vasut)
- fixups for s25fs512s (Takahiro Kuwano)
This commit is contained in:
Tom Rini 2023-05-01 13:29:52 -04:00
commit 50f64026f7
7 changed files with 231 additions and 44 deletions

View file

@ -669,6 +669,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
fallthrough;
case SNOR_MFR_ISSI:
case SNOR_MFR_MACRONIX:
case SNOR_MFR_WINBOND:
@ -903,6 +904,30 @@ static int read_bar(struct spi_nor *nor, const struct flash_info *info)
}
#endif
/**
* spi_nor_erase_chip() - Erase the entire flash memory.
* @nor: pointer to 'struct spi_nor'.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_erase_chip(struct spi_nor *nor)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
int ret;
spi_nor_setup_op(nor, &op, nor->write_proto);
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
return nor->mtd.size;
}
/*
* Initiate the erasure of a single sector. Returns the number of bytes erased
* on success, a negative error code on error.
@ -974,7 +999,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (ret < 0)
goto erase_err;
ret = spi_nor_erase_sector(nor, addr);
if (len == mtd->size &&
!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
ret = spi_nor_erase_chip(nor);
} else {
ret = spi_nor_erase_sector(nor, addr);
}
if (ret < 0)
goto erase_err;
@ -3199,6 +3229,87 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
/* Use ID byte 4 to distinguish S25FS256T and S25Hx-T */
#define S25FS256T_ID4 (0x08)
/* Number of dummy cycle for Read Any Register (RDAR) op. */
#define S25FS_S_RDAR_DUMMY 8
static int s25fs_s_quad_enable(struct spi_nor *nor)
{
return spansion_quad_enable_volatile(nor, 0, S25FS_S_RDAR_DUMMY);
}
static int s25fs_s_erase_non_uniform(struct spi_nor *nor, loff_t addr)
{
/* Support 8 x 4KB sectors at bottom */
return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0, SZ_32K);
}
static int s25fs_s_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_flash_parameter *params)
{
int ret;
u8 cfr3v;
/* Bank Address Register is not supported */
if (CONFIG_IS_ENABLED(SPI_FLASH_BAR))
return -EOPNOTSUPP;
/*
* Read CR3V to check if uniform sector is selected. If not, assign an
* erase hook that supports non-uniform erase.
*/
ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V,
S25FS_S_RDAR_DUMMY, &cfr3v);
if (ret)
return ret;
if (!(cfr3v & CFR3V_UNHYSA))
nor->erase = s25fs_s_erase_non_uniform;
return spi_nor_default_setup(nor, info, params);
}
static void s25fs_s_default_init(struct spi_nor *nor)
{
nor->setup = s25fs_s_setup;
}
static int s25fs_s_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *header,
const struct sfdp_bfpt *bfpt,
struct spi_nor_flash_parameter *params)
{
/* The erase size is set to 4K from BFPT, but it's wrong. Fix it. */
nor->erase_opcode = SPINOR_OP_SE;
nor->mtd.erasesize = nor->info->sector_size;
/* The S25FS-S chip family reports 512-byte pages in BFPT but
* in reality the write buffer still wraps at the safe default
* of 256 bytes. Overwrite the page size advertised by BFPT
* to get the writes working.
*/
params->page_size = 256;
return 0;
}
static void s25fs_s_post_sfdp_fixup(struct spi_nor *nor,
struct spi_nor_flash_parameter *params)
{
/* READ_1_1_2 is not supported */
params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_2;
/* READ_1_1_4 is not supported */
params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_4;
/* PP_1_1_4 is not supported */
params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
/* Use volatile register to enable quad */
params->quad_enable = s25fs_s_quad_enable;
}
static struct spi_nor_fixups s25fs_s_fixups = {
.default_init = s25fs_s_default_init,
.post_bfpt = s25fs_s_post_bfpt_fixup,
.post_sfdp = s25fs_s_post_sfdp_fixup,
};
static int s25_mdp_ready(struct spi_nor *nor)
{
u32 addr;
@ -3897,6 +4008,10 @@ void spi_nor_set_fixups(struct spi_nor *nor)
if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
!strcmp(nor->info->name, "s25fl256l"))
nor->fixups = &s25fl256l_fixups;
/* For FS-S (family ID = 0x81) */
if (JEDEC_MFR(nor->info) == SNOR_MFR_SPANSION && nor->info->id[5] == 0x81)
nor->fixups = &s25fs_s_fixups;
#endif
#ifdef CONFIG_SPI_FLASH_MT35XU

View file

@ -312,13 +312,12 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
* which is unsupported on some flash devices during register
* reads, prefer STIG mode for such small reads.
*/
if (!op->addr.nbytes ||
op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
mode = CQSPI_STIG_READ;
else
mode = CQSPI_READ;
} else {
if (!op->addr.nbytes || !op->data.buf.out)
if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
mode = CQSPI_STIG_WRITE;
else
mode = CQSPI_WRITE;
@ -362,8 +361,15 @@ static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
{
bool all_true, all_false;
all_true = op->cmd.dtr && op->addr.dtr && op->dummy.dtr &&
op->data.dtr;
/*
* op->dummy.dtr is required for converting nbytes into ncycles.
* Also, don't check the dtr field of the op phase having zero nbytes.
*/
all_true = op->cmd.dtr &&
(!op->addr.nbytes || op->addr.dtr) &&
(!op->dummy.nbytes || op->dummy.dtr) &&
(!op->data.nbytes || op->data.dtr);
all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr &&
!op->data.dtr;

View file

@ -120,7 +120,16 @@ static int cadence_qspi_set_protocol(struct cadence_spi_priv *priv,
{
int ret;
priv->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr;
/*
* For an op to be DTR, cmd phase along with every other non-empty
* phase should have dtr field set to 1. If an op phase has zero
* nbytes, ignore its dtr field; otherwise, check its dtr field.
* Also, dummy checks not performed here Since supports_op()
* already checks that all or none of the fields are DTR.
*/
priv->dtr = op->cmd.dtr &&
(!op->addr.nbytes || op->addr.dtr) &&
(!op->data.nbytes || op->data.dtr);
ret = cadence_qspi_buswidth_to_inst_type(op->cmd.buswidth);
if (ret < 0)
@ -367,6 +376,9 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg)
if (!cadence_qspi_wait_idle(reg_base))
return -EIO;
/* Flush the CMDCTRL reg after the execution */
writel(0, reg_base + CQSPI_REG_CMDCTRL);
return 0;
}
@ -453,11 +465,6 @@ int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
unsigned int dummy_clk;
u8 opcode;
if (rxlen > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) {
printf("QSPI: Invalid input arguments rxlen %u\n", rxlen);
return -EINVAL;
}
if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
@ -540,26 +547,12 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
unsigned int reg = 0;
unsigned int wr_data;
unsigned int wr_len;
unsigned int dummy_clk;
unsigned int txlen = op->data.nbytes;
const void *txbuf = op->data.buf.out;
void *reg_base = priv->regbase;
u32 addr;
u8 opcode;
/* Reorder address to SPI bus order if only transferring address */
if (!txlen) {
addr = cpu_to_be32(op->addr.val);
if (op->addr.nbytes == 3)
addr >>= 8;
txbuf = &addr;
txlen = op->addr.nbytes;
}
if (txlen > CQSPI_STIG_DATA_LEN_MAX) {
printf("QSPI: Invalid input arguments txlen %u\n", txlen);
return -EINVAL;
}
if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
@ -567,6 +560,27 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
reg |= opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
/* setup ADDR BIT field */
if (op->addr.nbytes) {
writel(op->addr.val, priv->regbase + CQSPI_REG_CMDADDRESS);
/*
* address bytes are zero indexed
*/
reg |= (((op->addr.nbytes - 1) &
CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) <<
CQSPI_REG_CMDCTRL_ADD_BYTES_LSB);
reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
}
/* Set up dummy cycles. */
dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
return -EOPNOTSUPP;
if (dummy_clk)
reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
<< CQSPI_REG_CMDCTRL_DUMMY_LSB;
if (txlen) {
/* writing data = yes */
reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB);

View file

@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/log2.h>
#include <linux/iopoll.h>
#include <power/regulator.h>
#define DW_SIZE 4
#define CHUNK_SIZE 16
@ -34,6 +35,34 @@
#define UMA_CTS_RDYST BIT(24)
#define UMA_CTS_DEV_NUM_MASK GENMASK(9, 8)
/* Direct Write Configuration Register */
#define DWR_CFG_WBURST_MASK GENMASK(25, 24)
#define DWR_CFG_ADDSIZ_MASK GENMASK(17, 16)
#define DWR_CFG_ABPCK_MASK GENMASK(11, 10)
#define DRW_CFG_DBPCK_MASK GENMASK(9, 8)
#define DRW_CFG_WRCMD 2
enum {
DWR_WBURST_1_BYTE,
DWR_WBURST_16_BYTE = 3,
};
enum {
DWR_ADDSIZ_24_BIT,
DWR_ADDSIZ_32_BIT,
};
enum {
DWR_ABPCK_BIT_PER_CLK,
DWR_ABPCK_2_BIT_PER_CLK,
DWR_ABPCK_4_BIT_PER_CLK,
};
enum {
DWR_DBPCK_BIT_PER_CLK,
DWR_DBPCK_2_BIT_PER_CLK,
DWR_DBPCK_4_BIT_PER_CLK,
};
struct npcm_fiu_regs {
unsigned int drd_cfg;
unsigned int dwr_cfg;
@ -67,19 +96,10 @@ struct npcm_fiu_regs {
struct npcm_fiu_priv {
struct npcm_fiu_regs *regs;
struct clk clk;
};
static int npcm_fiu_spi_set_speed(struct udevice *bus, uint speed)
{
struct npcm_fiu_priv *priv = dev_get_priv(bus);
int ret;
debug("%s: set speed %u\n", bus->name, speed);
ret = clk_set_rate(&priv->clk, speed);
if (ret < 0)
return ret;
return 0;
}
@ -349,13 +369,38 @@ static int npcm_fiu_exec_op(struct spi_slave *slave,
static int npcm_fiu_spi_probe(struct udevice *bus)
{
struct npcm_fiu_priv *priv = dev_get_priv(bus);
int ret;
struct udevice *vqspi_supply;
int vqspi_uv;
priv->regs = (struct npcm_fiu_regs *)dev_read_addr_ptr(bus);
ret = clk_get_by_index(bus, 0, &priv->clk);
if (ret < 0)
return ret;
if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
device_get_supply_regulator(bus, "vqspi-supply", &vqspi_supply);
vqspi_uv = dev_read_u32_default(bus, "vqspi-microvolt", 0);
/* Set IO voltage */
if (vqspi_supply && vqspi_uv)
regulator_set_value(vqspi_supply, vqspi_uv);
}
return 0;
}
static int npcm_fiu_spi_bind(struct udevice *bus)
{
struct npcm_fiu_regs *regs;
if (dev_read_bool(bus, "nuvoton,spix-mode")) {
regs = dev_read_addr_ptr(bus);
if (!regs)
return -EINVAL;
/* Setup direct write cfg for SPIX */
writel(FIELD_PREP(DWR_CFG_WBURST_MASK, DWR_WBURST_16_BYTE) |
FIELD_PREP(DWR_CFG_ADDSIZ_MASK, DWR_ADDSIZ_24_BIT) |
FIELD_PREP(DWR_CFG_ABPCK_MASK, DWR_ABPCK_4_BIT_PER_CLK) |
FIELD_PREP(DRW_CFG_DBPCK_MASK, DWR_DBPCK_4_BIT_PER_CLK) |
DRW_CFG_WRCMD, &regs->dwr_cfg);
}
return 0;
}
@ -384,4 +429,5 @@ U_BOOT_DRIVER(npcm_fiu_spi) = {
.ops = &npcm_fiu_spi_ops,
.priv_auto = sizeof(struct npcm_fiu_priv),
.probe = npcm_fiu_spi_probe,
.bind = npcm_fiu_spi_bind,
};

View file

@ -181,8 +181,12 @@ bool spi_mem_dtr_supports_op(struct spi_slave *slave,
if (op->dummy.nbytes && op->dummy.buswidth == 8 && op->dummy.nbytes % 2)
return false;
if (op->data.dir != SPI_MEM_NO_DATA &&
op->dummy.buswidth == 8 && op->data.nbytes % 2)
/*
* Transactions of odd length do not make sense for 8D-8D-8D mode
* because a byte is transferred in just half a cycle.
*/
if (op->data.dir != SPI_MEM_NO_DATA && op->data.dir != SPI_MEM_DATA_IN &&
op->data.buswidth == 8 && op->data.nbytes % 2)
return false;
return spi_mem_check_buswidth(slave, op);

View file

@ -556,7 +556,7 @@ static bool f_ospi_supports_op(struct spi_slave *slave,
if (!f_ospi_supports_op_width(op))
return false;
return true;
return spi_mem_default_supports_op(slave, op);
}
static int f_ospi_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)

View file

@ -186,7 +186,7 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
struct udevice *bus = dev->parent;
struct synquacer_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
u32 val, div, bus_width;
u32 val, div, bus_width = 1;
int rwflag;
rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
@ -211,6 +211,8 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
bus_width = 4;
else if (priv->mode & SPI_TX_OCTAL)
bus_width = 8;
else
log_warning("SPI mode not configured, setting to byte mode\n");
div = DIV_ROUND_UP(125000000, priv->speed);