summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorPratyush Yadav <p.yadav@ti.com>2021-04-14 23:53:21 +0530
committerPraneeth Bajjuri <praneeth@ti.com>2021-04-14 13:52:44 -0500
commit5b0d892fb6f95b747c0d05e080101dc679456b34 (patch)
tree0bdffcaa06ef8bd328630c661e0c6441d276fe14 /drivers/mtd
parent37534ef5f25653b42c93ee14d8ac1d933414b0b4 (diff)
mtd: spi-nor-core: Add support for Cypress Semper flash
The Cypress Semper flash is an xSPI compliant octal DTR flash. Add support for using it in octal DTR mode. The flash by default boots in a hybrid sector mode. Switch to uniform sector mode on boot. Use the default 20 dummy cycles for a read fast command. The SFDP programming on some older versions of the flash was incorrect. Fixes for that are included in the fixup hooks. Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/spi/Kconfig8
-rw-r--r--drivers/mtd/spi/spi-nor-core.c199
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c3
3 files changed, 210 insertions, 0 deletions
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index b5b728d050..ecfed6d215 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -157,6 +157,14 @@ config SPI_FLASH_SPANSION
help
Add support for various Spansion SPI flash chips (S25FLxxx)
+config SPI_FLASH_S28HS512T
+ bool "Cypress S28HS512T chip support"
+ depends on SPI_FLASH_SPANSION
+ help
+ Add support for the Cypress S28HS512T chip. This is a separate config
+ because the fixup hooks for this flash add extra size overhead. Boards
+ that don't use the flash can disable this to save space.
+
config SPI_FLASH_STMICRO
bool "STMicro SPI flash support"
help
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 488a06b83a..13c2460371 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2940,6 +2940,201 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
return nor->setup(nor, info, params);
}
+#ifdef CONFIG_SPI_FLASH_S28HS512T
+/**
+ * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * This also sets the memory access latency cycles to 24 to allow the flash to
+ * run at up to 200MHz.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor)
+{
+ struct spi_mem_op op;
+ u8 buf;
+ u8 addr_width = 3;
+ int ret;
+
+ /* Use 24 dummy cycles for memory array reads. */
+ ret = write_enable(nor);
+ if (ret)
+ return ret;
+
+ buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
+ op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR2V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret) {
+ dev_warn(nor->dev,
+ "failed to set default memory latency value: %d\n",
+ ret);
+ return ret;
+ }
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ nor->read_dummy = 24;
+
+ /* Set the octal and DTR enable bits. */
+ ret = write_enable(nor);
+ if (ret)
+ return ret;
+
+ buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
+ op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR5V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret) {
+ dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int s28hs512t_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_flash_parameter *params)
+{
+ struct spi_mem_op op;
+ u8 buf;
+ u8 addr_width = 3;
+ int ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ /*
+ * This Cypress flash also supports hybrid sector sizes. Make sure
+ * uniform sector mode is selected. This is done by setting the bit
+ * CFR3N[3].
+ */
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width,
+ SPINOR_REG_CYPRESS_CFR3V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, &buf, 1));
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret)
+ return ret;
+
+ ret = write_enable(nor);
+ if (ret)
+ return ret;
+
+ /* Set the uniform sector mode bit. */
+ buf |= SPINOR_REG_CYPRESS_CFR3N_UNISECT;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width,
+ SPINOR_REG_CYPRESS_CFR3N, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret) {
+ dev_err(nor->dev, "Failed to change to uniform sector mode\n");
+ return ret;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ return spi_nor_default_setup(nor, info, params);
+}
+
+static void s28hs512t_default_init(struct spi_nor *nor)
+{
+ nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
+ nor->setup = s28hs512t_setup;
+}
+
+static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params)
+{
+ /*
+ * 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 (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;
+
+ params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+ /* This flash is also missing the 4-byte Page Program opcode bit. */
+ 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(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+ SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
+
+ /*
+ * The xSPI Profile 1.0 table advertises the number of additional
+ * address bytes needed for Read Status Register command as 0 but the
+ * actual value for that is 4.
+ */
+ params->rdsr_addr_nbytes = 4;
+}
+
+static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ struct spi_mem_op op;
+ u8 buf;
+ u8 addr_width = 3;
+ int ret;
+
+ /*
+ * The BFPT table advertises a 512B page size but the page size is
+ * actually configurable (with the default being 256B). Read from
+ * CFR3V[4] and set the correct size.
+ */
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR3V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, &buf, 1));
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret)
+ return ret;
+
+ if (buf & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
+ params->page_size = 512;
+ else
+ params->page_size = 256;
+
+ /*
+ * The BFPT advertises that it supports 4k erases, and the datasheet
+ * says the same. But 4k erases did not work when testing. So, use 256k
+ * erases for now.
+ */
+ nor->erase_opcode = SPINOR_OP_SE_4B;
+ nor->mtd.erasesize = 0x40000;
+
+ return 0;
+}
+
+static struct spi_nor_fixups s28hs512t_fixups = {
+ .default_init = s28hs512t_default_init,
+ .post_sfdp = s28hs512t_post_sfdp_fixup,
+ .post_bfpt = s28hs512t_post_bfpt_fixup,
+};
+#endif /* CONFIG_SPI_FLASH_S28HS512T */
+
/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
* @nor: pointer to a 'struct spi_nor'
*
@@ -3082,6 +3277,10 @@ int spi_nor_remove(struct spi_nor *nor)
void spi_nor_set_fixups(struct spi_nor *nor)
{
+#ifdef CONFIG_SPI_FLASH_S28HS512T
+ if (!strcmp(nor->info->name, "s28hs512t"))
+ nor->fixups = &s28hs512t_fixups;
+#endif
}
int spi_nor_scan(struct spi_nor *nor)
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 5bd5dd3003..af202a2069 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -217,6 +217,9 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("s25fl208k", 0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
{ INFO("s25fl064l", 0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("s25fl128l", 0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+#ifdef CONFIG_SPI_FLASH_S28HS512T
+ { INFO("s28hs512t", 0x345b1a, 0, 256 * 1024, 256, SPI_NOR_OCTAL_DTR_READ) },
+#endif
#endif
#ifdef CONFIG_SPI_FLASH_SST /* SST */
/* SST -- large erase sizes are "overlays", "sectors" are 4K */