aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-05-01 13:29:52 -0400
committerTom Rini <trini@konsulko.com>2023-05-01 13:29:52 -0400
commit50f64026f7a4c2d0a101c93916e01782e4fbbe7f (patch)
treeee7e0a348fe5501c19b2e0f51fac01763500885c
parent6735ab59e6fd71ced1c58d8dfb3dd6baf3690d16 (diff)
parent08b3098eadc7f826c3e6fb9d184cf6d82f5028fe (diff)
downloadu-boot-WIP/01May2023.zip
u-boot-WIP/01May2023.tar.gz
u-boot-WIP/01May2023.tar.bz2
Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-spiWIP/01May2023
- cadence-quadspi fixes (Apurva Nandan, Dhruva Gole) - CHIP_ERASE optimization (Marek Vasut) - fixups for s25fs512s (Takahiro Kuwano)
-rw-r--r--drivers/mtd/spi/spi-nor-core.c117
-rw-r--r--drivers/spi/cadence_qspi.c16
-rw-r--r--drivers/spi/cadence_qspi_apb.c56
-rw-r--r--drivers/spi/npcm_fiu_spi.c72
-rw-r--r--drivers/spi/spi-mem.c8
-rw-r--r--drivers/spi/spi-sn-f-ospi.c2
-rw-r--r--drivers/spi/spi-synquacer.c4
7 files changed, 231 insertions, 44 deletions
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 2c3116e..6093277 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -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
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index c7f10c5..f931e4c 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -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;
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 21fe2e6..9ce2c0f 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -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);
diff --git a/drivers/spi/npcm_fiu_spi.c b/drivers/spi/npcm_fiu_spi.c
index 7000fe5..73c5064 100644
--- a/drivers/spi/npcm_fiu_spi.c
+++ b/drivers/spi/npcm_fiu_spi.c
@@ -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,
};
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 8e8995f..b7eca58 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -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);
diff --git a/drivers/spi/spi-sn-f-ospi.c b/drivers/spi/spi-sn-f-ospi.c
index ebf2903..e3633a5 100644
--- a/drivers/spi/spi-sn-f-ospi.c
+++ b/drivers/spi/spi-sn-f-ospi.c
@@ -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)
diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c
index 0cae3df..0f5d0a3 100644
--- a/drivers/spi/spi-synquacer.c
+++ b/drivers/spi/spi-synquacer.c
@@ -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);