|
@@ -149,24 +149,6 @@ static int read_cr(struct spi_nor *nor)
|
|
|
return val;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Dummy Cycle calculation for different type of read.
|
|
|
- * It can be used to support more commands with
|
|
|
- * different dummy cycle requirements.
|
|
|
- */
|
|
|
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
|
|
|
-{
|
|
|
- switch (nor->flash_read) {
|
|
|
- case SPI_NOR_FAST:
|
|
|
- case SPI_NOR_DUAL:
|
|
|
- case SPI_NOR_QUAD:
|
|
|
- return 8;
|
|
|
- case SPI_NOR_NORMAL:
|
|
|
- return 0;
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Write status register 1 byte
|
|
|
* Returns negative if error occurred.
|
|
@@ -1460,30 +1442,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
|
|
-{
|
|
|
- int status;
|
|
|
-
|
|
|
- switch (JEDEC_MFR(info)) {
|
|
|
- case SNOR_MFR_MACRONIX:
|
|
|
- status = macronix_quad_enable(nor);
|
|
|
- if (status) {
|
|
|
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- return status;
|
|
|
- case SNOR_MFR_MICRON:
|
|
|
- return 0;
|
|
|
- default:
|
|
|
- status = spansion_quad_enable(nor);
|
|
|
- if (status) {
|
|
|
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- return status;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static int spi_nor_check(struct spi_nor *nor)
|
|
|
{
|
|
|
if (!nor->dev || !nor->read || !nor->write ||
|
|
@@ -1536,8 +1494,323 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
+struct spi_nor_read_command {
|
|
|
+ u8 num_mode_clocks;
|
|
|
+ u8 num_wait_states;
|
|
|
+ u8 opcode;
|
|
|
+ enum spi_nor_protocol proto;
|
|
|
+};
|
|
|
+
|
|
|
+struct spi_nor_pp_command {
|
|
|
+ u8 opcode;
|
|
|
+ enum spi_nor_protocol proto;
|
|
|
+};
|
|
|
+
|
|
|
+enum spi_nor_read_command_index {
|
|
|
+ SNOR_CMD_READ,
|
|
|
+ SNOR_CMD_READ_FAST,
|
|
|
+
|
|
|
+ /* Dual SPI */
|
|
|
+ SNOR_CMD_READ_1_1_2,
|
|
|
+ SNOR_CMD_READ_1_2_2,
|
|
|
+ SNOR_CMD_READ_2_2_2,
|
|
|
+
|
|
|
+ /* Quad SPI */
|
|
|
+ SNOR_CMD_READ_1_1_4,
|
|
|
+ SNOR_CMD_READ_1_4_4,
|
|
|
+ SNOR_CMD_READ_4_4_4,
|
|
|
+
|
|
|
+ SNOR_CMD_READ_MAX
|
|
|
+};
|
|
|
+
|
|
|
+enum spi_nor_pp_command_index {
|
|
|
+ SNOR_CMD_PP,
|
|
|
+
|
|
|
+ /* Quad SPI */
|
|
|
+ SNOR_CMD_PP_1_1_4,
|
|
|
+ SNOR_CMD_PP_1_4_4,
|
|
|
+ SNOR_CMD_PP_4_4_4,
|
|
|
+
|
|
|
+ SNOR_CMD_PP_MAX
|
|
|
+};
|
|
|
+
|
|
|
+struct spi_nor_flash_parameter {
|
|
|
+ u64 size;
|
|
|
+ u32 page_size;
|
|
|
+
|
|
|
+ struct spi_nor_hwcaps hwcaps;
|
|
|
+ struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
|
|
|
+ struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
|
|
|
+
|
|
|
+ int (*quad_enable)(struct spi_nor *nor);
|
|
|
+};
|
|
|
+
|
|
|
+static void
|
|
|
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
|
|
+ u8 num_mode_clocks,
|
|
|
+ u8 num_wait_states,
|
|
|
+ u8 opcode,
|
|
|
+ enum spi_nor_protocol proto)
|
|
|
+{
|
|
|
+ read->num_mode_clocks = num_mode_clocks;
|
|
|
+ read->num_wait_states = num_wait_states;
|
|
|
+ read->opcode = opcode;
|
|
|
+ read->proto = proto;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
|
|
|
+ u8 opcode,
|
|
|
+ enum spi_nor_protocol proto)
|
|
|
+{
|
|
|
+ pp->opcode = opcode;
|
|
|
+ pp->proto = proto;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_init_params(struct spi_nor *nor,
|
|
|
+ const struct flash_info *info,
|
|
|
+ struct spi_nor_flash_parameter *params)
|
|
|
+{
|
|
|
+ /* Set legacy flash parameters as default. */
|
|
|
+ memset(params, 0, sizeof(*params));
|
|
|
+
|
|
|
+ /* Set SPI NOR sizes. */
|
|
|
+ params->size = info->sector_size * info->n_sectors;
|
|
|
+ params->page_size = info->page_size;
|
|
|
+
|
|
|
+ /* (Fast) Read settings. */
|
|
|
+ params->hwcaps.mask |= SNOR_HWCAPS_READ;
|
|
|
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ],
|
|
|
+ 0, 0, SPINOR_OP_READ,
|
|
|
+ SNOR_PROTO_1_1_1);
|
|
|
+
|
|
|
+ if (!(info->flags & SPI_NOR_NO_FR)) {
|
|
|
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
|
|
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST],
|
|
|
+ 0, 8, SPINOR_OP_READ_FAST,
|
|
|
+ SNOR_PROTO_1_1_1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info->flags & SPI_NOR_DUAL_READ) {
|
|
|
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
|
|
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2],
|
|
|
+ 0, 8, SPINOR_OP_READ_1_1_2,
|
|
|
+ SNOR_PROTO_1_1_2);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info->flags & SPI_NOR_QUAD_READ) {
|
|
|
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
|
|
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4],
|
|
|
+ 0, 8, SPINOR_OP_READ_1_1_4,
|
|
|
+ SNOR_PROTO_1_1_4);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Page Program settings. */
|
|
|
+ params->hwcaps.mask |= SNOR_HWCAPS_PP;
|
|
|
+ spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP],
|
|
|
+ SPINOR_OP_PP, SNOR_PROTO_1_1_1);
|
|
|
+
|
|
|
+ /* Select the procedure to set the Quad Enable bit. */
|
|
|
+ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
|
|
|
+ SNOR_HWCAPS_PP_QUAD)) {
|
|
|
+ switch (JEDEC_MFR(info)) {
|
|
|
+ case SNOR_MFR_MACRONIX:
|
|
|
+ params->quad_enable = macronix_quad_enable;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SNOR_MFR_MICRON:
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ params->quad_enable = spansion_quad_enable;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
|
|
|
+{
|
|
|
+ size_t i;
|
|
|
+
|
|
|
+ for (i = 0; i < size; i++)
|
|
|
+ if (table[i][0] == (int)hwcaps)
|
|
|
+ return table[i][1];
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
|
|
|
+{
|
|
|
+ static const int hwcaps_read2cmd[][2] = {
|
|
|
+ { SNOR_HWCAPS_READ, SNOR_CMD_READ },
|
|
|
+ { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
|
|
|
+ { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
|
|
|
+ { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
|
|
|
+ { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
|
|
|
+ { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
|
|
|
+ { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
|
|
|
+ { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
|
|
|
+ };
|
|
|
+
|
|
|
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
|
|
|
+ ARRAY_SIZE(hwcaps_read2cmd));
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
|
|
|
+{
|
|
|
+ static const int hwcaps_pp2cmd[][2] = {
|
|
|
+ { SNOR_HWCAPS_PP, SNOR_CMD_PP },
|
|
|
+ { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
|
|
|
+ { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
|
|
|
+ { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
|
|
|
+ };
|
|
|
+
|
|
|
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
|
|
|
+ ARRAY_SIZE(hwcaps_pp2cmd));
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_select_read(struct spi_nor *nor,
|
|
|
+ const struct spi_nor_flash_parameter *params,
|
|
|
+ u32 shared_hwcaps)
|
|
|
+{
|
|
|
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
|
|
|
+ const struct spi_nor_read_command *read;
|
|
|
+
|
|
|
+ if (best_match < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
|
|
|
+ if (cmd < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ read = ¶ms->reads[cmd];
|
|
|
+ nor->read_opcode = read->opcode;
|
|
|
+ nor->read_proto = read->proto;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In the spi-nor framework, we don't need to make the difference
|
|
|
+ * between mode clock cycles and wait state clock cycles.
|
|
|
+ * Indeed, the value of the mode clock cycles is used by a QSPI
|
|
|
+ * flash memory to know whether it should enter or leave its 0-4-4
|
|
|
+ * (Continuous Read / XIP) mode.
|
|
|
+ * eXecution In Place is out of the scope of the mtd sub-system.
|
|
|
+ * Hence we choose to merge both mode and wait state clock cycles
|
|
|
+ * into the so called dummy clock cycles.
|
|
|
+ */
|
|
|
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_select_pp(struct spi_nor *nor,
|
|
|
+ const struct spi_nor_flash_parameter *params,
|
|
|
+ u32 shared_hwcaps)
|
|
|
+{
|
|
|
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
|
|
|
+ const struct spi_nor_pp_command *pp;
|
|
|
+
|
|
|
+ if (best_match < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
|
|
|
+ if (cmd < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ pp = ¶ms->page_programs[cmd];
|
|
|
+ nor->program_opcode = pp->opcode;
|
|
|
+ nor->write_proto = pp->proto;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_select_erase(struct spi_nor *nor,
|
|
|
+ const struct flash_info *info)
|
|
|
+{
|
|
|
+ struct mtd_info *mtd = &nor->mtd;
|
|
|
+
|
|
|
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
|
|
+ /* prefer "small sector" erase if possible */
|
|
|
+ if (info->flags & SECT_4K) {
|
|
|
+ nor->erase_opcode = SPINOR_OP_BE_4K;
|
|
|
+ mtd->erasesize = 4096;
|
|
|
+ } else if (info->flags & SECT_4K_PMC) {
|
|
|
+ nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
|
|
|
+ mtd->erasesize = 4096;
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ nor->erase_opcode = SPINOR_OP_SE;
|
|
|
+ mtd->erasesize = info->sector_size;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
|
|
|
+ const struct spi_nor_flash_parameter *params,
|
|
|
+ const struct spi_nor_hwcaps *hwcaps)
|
|
|
+{
|
|
|
+ u32 ignored_mask, shared_mask;
|
|
|
+ bool enable_quad_io;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Keep only the hardware capabilities supported by both the SPI
|
|
|
+ * controller and the SPI flash memory.
|
|
|
+ */
|
|
|
+ shared_mask = hwcaps->mask & params->hwcaps.mask;
|
|
|
+
|
|
|
+ /* SPI n-n-n protocols are not supported yet. */
|
|
|
+ ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
|
|
|
+ SNOR_HWCAPS_READ_4_4_4 |
|
|
|
+ SNOR_HWCAPS_PP_4_4_4);
|
|
|
+ if (shared_mask & ignored_mask) {
|
|
|
+ dev_dbg(nor->dev,
|
|
|
+ "SPI n-n-n protocols are not supported yet.\n");
|
|
|
+ shared_mask &= ~ignored_mask;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Select the (Fast) Read command. */
|
|
|
+ err = spi_nor_select_read(nor, params, shared_mask);
|
|
|
+ if (err) {
|
|
|
+ dev_err(nor->dev,
|
|
|
+ "can't select read settings supported by both the SPI controller and memory.\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Select the Page Program command. */
|
|
|
+ err = spi_nor_select_pp(nor, params, shared_mask);
|
|
|
+ if (err) {
|
|
|
+ dev_err(nor->dev,
|
|
|
+ "can't select write settings supported by both the SPI controller and memory.\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Select the Sector Erase command. */
|
|
|
+ err = spi_nor_select_erase(nor, info);
|
|
|
+ if (err) {
|
|
|
+ dev_err(nor->dev,
|
|
|
+ "can't select erase settings supported by both the SPI controller and memory.\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Enable Quad I/O if needed. */
|
|
|
+ enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
|
|
|
+ spi_nor_get_protocol_width(nor->write_proto) == 4);
|
|
|
+ if (enable_quad_io && params->quad_enable) {
|
|
|
+ err = params->quad_enable(nor);
|
|
|
+ if (err) {
|
|
|
+ dev_err(nor->dev, "quad mode not supported\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|
|
+ const struct spi_nor_hwcaps *hwcaps)
|
|
|
{
|
|
|
+ struct spi_nor_flash_parameter params;
|
|
|
const struct flash_info *info = NULL;
|
|
|
struct device *dev = nor->dev;
|
|
|
struct mtd_info *mtd = &nor->mtd;
|
|
@@ -1549,6 +1822,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+ /* Reset SPI protocol for all commands. */
|
|
|
+ nor->reg_proto = SNOR_PROTO_1_1_1;
|
|
|
+ nor->read_proto = SNOR_PROTO_1_1_1;
|
|
|
+ nor->write_proto = SNOR_PROTO_1_1_1;
|
|
|
+
|
|
|
if (name)
|
|
|
info = spi_nor_match_id(name);
|
|
|
/* Try to auto-detect if chip name wasn't specified or not found */
|
|
@@ -1591,6 +1869,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
if (info->flags & SPI_S3AN)
|
|
|
nor->flags |= SNOR_F_READY_XSR_RDY;
|
|
|
|
|
|
+ /* Parse the Serial Flash Discoverable Parameters table. */
|
|
|
+ ret = spi_nor_init_params(nor, info, ¶ms);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
/*
|
|
|
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
|
|
* with the software protection bits set
|
|
@@ -1611,7 +1894,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
mtd->type = MTD_NORFLASH;
|
|
|
mtd->writesize = 1;
|
|
|
mtd->flags = MTD_CAP_NORFLASH;
|
|
|
- mtd->size = info->sector_size * info->n_sectors;
|
|
|
+ mtd->size = params.size;
|
|
|
mtd->_erase = spi_nor_erase;
|
|
|
mtd->_read = spi_nor_read;
|
|
|
|
|
@@ -1642,75 +1925,38 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
if (info->flags & NO_CHIP_ERASE)
|
|
|
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
|
|
|
|
|
|
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
|
|
- /* prefer "small sector" erase if possible */
|
|
|
- if (info->flags & SECT_4K) {
|
|
|
- nor->erase_opcode = SPINOR_OP_BE_4K;
|
|
|
- mtd->erasesize = 4096;
|
|
|
- } else if (info->flags & SECT_4K_PMC) {
|
|
|
- nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
|
|
|
- mtd->erasesize = 4096;
|
|
|
- } else
|
|
|
-#endif
|
|
|
- {
|
|
|
- nor->erase_opcode = SPINOR_OP_SE;
|
|
|
- mtd->erasesize = info->sector_size;
|
|
|
- }
|
|
|
-
|
|
|
if (info->flags & SPI_NOR_NO_ERASE)
|
|
|
mtd->flags |= MTD_NO_ERASE;
|
|
|
|
|
|
mtd->dev.parent = dev;
|
|
|
- nor->page_size = info->page_size;
|
|
|
+ nor->page_size = params.page_size;
|
|
|
mtd->writebufsize = nor->page_size;
|
|
|
|
|
|
if (np) {
|
|
|
/* If we were instantiated by DT, use it */
|
|
|
if (of_property_read_bool(np, "m25p,fast-read"))
|
|
|
- nor->flash_read = SPI_NOR_FAST;
|
|
|
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
|
|
else
|
|
|
- nor->flash_read = SPI_NOR_NORMAL;
|
|
|
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
|
|
|
} else {
|
|
|
/* If we weren't instantiated by DT, default to fast-read */
|
|
|
- nor->flash_read = SPI_NOR_FAST;
|
|
|
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
|
|
}
|
|
|
|
|
|
/* Some devices cannot do fast-read, no matter what DT tells us */
|
|
|
if (info->flags & SPI_NOR_NO_FR)
|
|
|
- nor->flash_read = SPI_NOR_NORMAL;
|
|
|
-
|
|
|
- /* Quad/Dual-read mode takes precedence over fast/normal */
|
|
|
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
|
|
|
- ret = set_quad_mode(nor, info);
|
|
|
- if (ret) {
|
|
|
- dev_err(dev, "quad mode not supported\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
- nor->flash_read = SPI_NOR_QUAD;
|
|
|
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
|
|
|
- nor->flash_read = SPI_NOR_DUAL;
|
|
|
- }
|
|
|
-
|
|
|
- /* Default commands */
|
|
|
- switch (nor->flash_read) {
|
|
|
- case SPI_NOR_QUAD:
|
|
|
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
|
|
|
- break;
|
|
|
- case SPI_NOR_DUAL:
|
|
|
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
|
|
|
- break;
|
|
|
- case SPI_NOR_FAST:
|
|
|
- nor->read_opcode = SPINOR_OP_READ_FAST;
|
|
|
- break;
|
|
|
- case SPI_NOR_NORMAL:
|
|
|
- nor->read_opcode = SPINOR_OP_READ;
|
|
|
- break;
|
|
|
- default:
|
|
|
- dev_err(dev, "No Read opcode defined\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
|
|
|
|
|
|
- nor->program_opcode = SPINOR_OP_PP;
|
|
|
+ /*
|
|
|
+ * Configure the SPI memory:
|
|
|
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
|
|
|
+ * - set the number of dummy cycles (mode cycles + wait states).
|
|
|
+ * - set the SPI protocols for register and memory accesses.
|
|
|
+ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
|
|
|
+ */
|
|
|
+ ret = spi_nor_setup(nor, info, ¶ms, hwcaps);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
if (info->addr_width)
|
|
|
nor->addr_width = info->addr_width;
|
|
@@ -1732,8 +1978,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
|
|
|
-
|
|
|
if (info->flags & SPI_S3AN) {
|
|
|
ret = s3an_nor_scan(info, nor);
|
|
|
if (ret)
|