Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-spi
- cadence-quadspi fixes (Apurva Nandan, Dhruva Gole) - CHIP_ERASE optimization (Marek Vasut) - fixups for s25fs512s (Takahiro Kuwano)
This commit is contained in:
commit
50f64026f7
7 changed files with 231 additions and 44 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ®s->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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue