@@ -466,6 +466,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
case SNOR_MFR_ISSI:
case SNOR_MFR_MACRONIX:
case SNOR_MFR_WINBOND:
+ case SNOR_MFR_CYPRESS:
if (need_wren)
write_enable(nor);
@@ -730,6 +731,54 @@ erase_err:
return ret;
}
+#ifdef CONFIG_SPI_FLASH_SPANSION
+/*
+ * Erase for Spansion/Cypress Flash devices that has overlaid 4KB sectors at
+ * the top and/or bottom.
+ */
+static int spansion_overlaid_erase(struct mtd_info *mtd,
+ struct erase_info *instr)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ struct erase_info instr_4k;
+ u8 opcode;
+ u32 erasesize;
+ int ret;
+
+ /* Perform default erase operation (non-overlaid portion is erased) */
+ ret = spi_nor_erase(mtd, instr);
+ if (ret)
+ return ret;
+
+ /* Backup default erase opcode and size */
+ opcode = nor->erase_opcode;
+ erasesize = mtd->erasesize;
+
+ /*
+ * Erase 4KB sectors. Use the possible max length of 4KB sector region.
+ * The Flash just ignores the command if the address is not configured
+ * as 4KB sector and reports ready status immediately.
+ */
+ instr_4k.len = SZ_128K;
+ nor->erase_opcode = SPINOR_OP_BE_4K_4B;
+ mtd->erasesize = SZ_4K;
+ if (instr->addr == 0) {
+ instr_4k.addr = 0;
+ ret = spi_nor_erase(mtd, &instr_4k);
+ }
+ if (!ret && instr->addr + instr->len == mtd->size) {
+ instr_4k.addr = mtd->size - instr_4k.len;
+ ret = spi_nor_erase(mtd, &instr_4k);
+ }
+
+ /* Restore erase opcode and size */
+ nor->erase_opcode = opcode;
+ mtd->erasesize = erasesize;
+
+ return ret;
+}
+#endif
+
#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
/* Write status register and ensure bits in mask match written values */
static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
@@ -1550,6 +1599,60 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
return 0;
}
+/**
+ * spansion_quad_enable_volatile() - enable Quad I/O mode in volatile register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * It is recommended to update volatile registers in the field application due
+ * to a risk of the non-volatile registers corruption by power interrupt. This
+ * function sets Quad Enable bit in CFR1 volatile.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_quad_enable_volatile(struct spi_nor *nor)
+{
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRAR, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width,
+ SPINOR_REG_ADDR_CFR1V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, NULL, 1));
+ u8 cr;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_dbg(nor->dev,
+ "error while reading configuration register\n");
+ return -EINVAL;
+ }
+
+ if (ret & CR_QUAD_EN_SPAN)
+ return 0;
+
+ cr = ret | CR_QUAD_EN_SPAN;
+
+ write_enable(nor);
+
+ ret = spi_nor_read_write_reg(nor, &op, &cr);
+
+ if (ret < 0) {
+ dev_dbg(nor->dev,
+ "error while writing configuration register\n");
+ return -EINVAL;
+ }
+
+ /* Read back and check it. */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_dbg(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#if CONFIG_IS_ENABLED(SPI_FLASH_SFDP_SUPPORT)
/**
* spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.
@@ -2488,8 +2591,83 @@ static int spi_nor_init(struct spi_nor *nor)
return 0;
}
+#ifdef CONFIG_SPI_FLASH_SPANSION
+static int s25hx_t_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_flash_parameter *params,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+#ifdef CONFIG_SPI_FLASH_BAR
+ return -ENOTSUPP; /* Bank Address Register is not supported */
+#endif
+ /*
+ * The Cypress Semper family has transparent ECC. To preserve
+ * ECC enabled, multi-pass programming within the same 16-byte
+ * ECC data unit needs to be avoided. Set writesize to the page
+ * size and remove the MTD_BIT_WRITEABLE flag in mtd_info to
+ * prevent multi-pass programming.
+ */
+ nor->mtd.writesize = params->page_size;
+ nor->mtd.flags &= ~MTD_BIT_WRITEABLE;
+
+ /* Emulate uniform sector architecure by this erase hook*/
+ nor->mtd._erase = spansion_overlaid_erase;
+ /* Enter 4-byte addressing mode for WRAR used in quad_enable */
+ set_4byte(nor, info, true);
+
+ return spi_nor_default_setup(nor, info, params, hwcaps);
+}
+
+static void s25hx_t_default_init(struct spi_nor *nor)
+{
+ nor->setup = s25hx_t_setup;
+}
+
+static int s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
+ const struct sfdp_parameter_header *header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ /* Default page size is 256-byte, but BFPT reports 512-byte */
+ params->page_size = 256;
+ /* Reset erase size in case it is set to 4K from BFPT */
+ nor->mtd.erasesize = 0;
+
+ return 0;
+}
+
+static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params)
+{
+ /* READ_FAST_4B (0Ch) requires mode cycles*/
+ params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
+ /* 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 = spansion_quad_enable_volatile;
+}
+
+static struct spi_nor_fixups s25hx_t_fixups = {
+ .default_init = s25hx_t_default_init,
+ .post_bfpt = s25hx_t_post_bfpt_fixup,
+ .post_sfdp = s25hx_t_post_sfdp_fixup,
+};
+#endif
+
static void spi_nor_set_fixups(struct spi_nor *nor)
{
+#ifdef CONFIG_SPI_FLASH_SPANSION
+ if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) {
+ switch (nor->info->id[1]) {
+ case 0x2a: /* S25HL (QSPI, 3.3V) */
+ case 0x2b: /* S25HS (QSPI, 1.8V) */
+ nor->fixups = &s25hx_t_fixups;
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
}
int spi_nor_scan(struct spi_nor *nor)
@@ -215,6 +215,30 @@ 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) },
+
+ /* S25HL/HS-T (Semper Flash with Quad SPI) Family has overlaid 4KB
+ * sectors at top and/or bottom, depending on the device configuration.
+ * To support this, an erase hook makes overlaid sectors appear as
+ * uniform sectors.
+ */
+ { INFO6("s25hl256t", 0x342a19, 0x0f0390, 256 * 1024, 128,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
+ { INFO6("s25hl512t", 0x342a1a, 0x0f0390, 256 * 1024, 256,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
+ { INFO6("s25hl01gt", 0x342a1b, 0x0f0390, 256 * 1024, 512,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
+ { INFO6("s25hs256t", 0x342b19, 0x0f0390, 256 * 1024, 128,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
+ { INFO6("s25hs512t", 0x342b1a, 0x0f0390, 256 * 1024, 256,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
+ { INFO6("s25hs01gt", 0x342b1b, 0x0f0390, 256 * 1024, 512,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
+ USE_CLSR) },
#endif
#ifdef CONFIG_SPI_FLASH_SST /* SST */
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
@@ -544,6 +544,69 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
}
#endif /* CONFIG_SPI_FLASH_SPANSION */
+#ifdef CONFIG_SPI_FLASH_SPANSION
+/**
+ * spansion_quad_enable_volatile() - enable Quad I/O mode in volatile register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * It is recommended to update volatile registers in the field application due
+ * to a risk of the non-volatile registers corruption by power interrupt. This
+ * function sets Quad Enable bit in CFR1 volatile.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_quad_enable_volatile(struct spi_nor *nor)
+{
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRAR, 1),
+ SPI_MEM_OP_ADDR(4, SPINOR_REG_ADDR_CFR1V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, NULL, 1));
+ u8 cr;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_dbg(nor->dev,
+ "error while reading configuration register\n");
+ return -EINVAL;
+ }
+
+ if (ret & CR_QUAD_EN_SPAN)
+ return 0;
+
+ cr = ret | CR_QUAD_EN_SPAN;
+
+ /* Use 4-byte address for WRAR */
+ ret = spi_nor_write_reg(nor, SPINOR_OP_EN4B, NULL, 0);
+ if (ret < 0) {
+ dev_dbg(nor->dev,
+ "error while enabling 4-byte address\n");
+ return ret;
+ }
+
+ write_enable(nor);
+
+ ret = spi_nor_read_write_reg(nor, &op, &cr);
+
+ if (ret < 0) {
+ dev_dbg(nor->dev,
+ "error while writing configuration register\n");
+ return -EINVAL;
+ }
+
+ /* Read back and check it. */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_dbg(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
static void
spi_nor_set_read_settings(struct spi_nor_read_command *read,
u8 num_mode_clocks,
@@ -572,6 +635,11 @@ static int spi_nor_init_params(struct spi_nor *nor,
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST],
0, 8, SPINOR_OP_READ_FAST,
SNOR_PROTO_1_1_1);
+#ifdef CONFIG_SPI_FLASH_SPANSION
+ if (JEDEC_MFR(info) == SNOR_MFR_CYPRESS &&
+ (info->id[1] == 0x2a || info->id[1] == 0x2b))
+ params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
+#endif
}
if (info->flags & SPI_NOR_QUAD_READ) {
@@ -648,6 +716,11 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
case SNOR_MFR_MACRONIX:
err = macronix_quad_enable(nor);
break;
+#endif
+#ifdef CONFIG_SPI_FLASH_SPANSION
+ case SNOR_MFR_CYPRESS:
+ err = spansion_quad_enable_volatile(nor);
+ break;
#endif
case SNOR_MFR_ST:
case SNOR_MFR_MICRON:
@@ -27,6 +27,7 @@
#define SNOR_MFR_SPANSION CFI_MFR_AMD
#define SNOR_MFR_SST CFI_MFR_SST
#define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */
+#define SNOR_MFR_CYPRESS 0x34
/*
* Note on opcode nomenclature: some opcodes have a format like
@@ -120,6 +121,8 @@
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
#define SPINOR_OP_BRRD 0x16 /* Bank register read */
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
+#define SPINOR_OP_WRAR 0x71 /* Write any register */
+#define SPINOR_REG_ADDR_CFR1V 0x00800002
/* Used for Micron flashes only. */
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */