diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2022-12-05 15:40:59 +0100 |
---|---|---|
committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2022-12-05 15:40:59 +0100 |
commit | a34506e08db7ccce160a259e4b00b1e307486c59 (patch) | |
tree | b046d4ec23c74a0e7ef08b0d0fdd266dbd629b3f /drivers/mtd/spi-nor/core.c | |
parent | 1d46f1ae82494fae60802c1ed09ca5009e1b414c (diff) | |
parent | 1799cd8540b67b88514c82f5fae1c75b986bcbd8 (diff) |
Merge tag 'spi-nor/for-6.2' into mtd/next
SPI NOR core changes:
* Add support for flash reset using the dt reset-gpios property.
* Update hwcaps.mask to include 8D-8D-8D read and page program ops
when xSPI profile 1.0 table is defined.
* Bypass zero erase size in spi_nor_find_best_erase_type().
* Fix select_uniform_erase to skip 0 erase size
* Add generic flash driver. If a flash is not found in the flash_info
array, fall back to the generic flash driver which is described solely
by the flash's SFDP tables.
* Fix the number of bytes for the dummy cycles in
spi_nor_spimem_check_readop().
* Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable.
SPI NOR manufacturer drivers changes:
* Spansion:
- use PARSE_SFDP for s28hs512t,
- add support for s28hl512t, s28hl01gt, and s28hs01gt.
* Gigadevice: Replace default_init() with post_bfpt() for gd25q256.
* Micron - ST: Enable locking for mt25qu256a.
* Winbond: Add support for W25Q512NW-IQ.
* ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP.
Fix merge conflict in the jedec,spi-nor bindings.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Diffstat (limited to 'drivers/mtd/spi-nor/core.c')
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 85 |
1 files changed, 80 insertions, 5 deletions
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index bee8fc4c9f07..d8703d7dfd0a 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1184,6 +1184,8 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map, continue; erase = &map->erase_type[i]; + if (!erase->size) + continue; /* Alignment is not mandatory for overlaid regions */ if (region->offset & SNOR_OVERLAID_REGION && @@ -1632,6 +1634,16 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_xmc, }; +static const struct flash_info spi_nor_generic_flash = { + .name = "spi-nor-generic", + /* + * JESD216 rev A doesn't specify the page size, therefore we need a + * sane default. + */ + .page_size = 256, + .parse_sfdp = true, +}; + static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, const u8 *id) { @@ -1664,7 +1676,20 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor) return ERR_PTR(ret); } + /* Cache the complete flash ID. */ + nor->id = devm_kmemdup(nor->dev, id, SPI_NOR_MAX_ID_LEN, GFP_KERNEL); + if (!nor->id) + return ERR_PTR(-ENOMEM); + info = spi_nor_match_id(nor, id); + + /* Fallback to a generic flash described only by its SFDP data. */ + if (!info) { + ret = spi_nor_check_sfdp_signature(nor); + if (!ret) + info = &spi_nor_generic_flash; + } + if (!info) { dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", SPI_NOR_MAX_ID_LEN, id); @@ -1914,7 +1939,8 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, spi_nor_spimem_setup_op(nor, &op, read->proto); /* convert the dummy cycles to the number of bytes */ - op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * + op.dummy.buswidth / 8; if (spi_nor_protocol_is_dtr(nor->read_proto)) op.dummy.nbytes *= 2; @@ -2091,8 +2117,12 @@ static int spi_nor_select_pp(struct spi_nor *nor, * spi_nor_select_uniform_erase() - select optimum uniform erase type * @map: the erase map of the SPI NOR * @wanted_size: the erase type size to search for. Contains the value of - * info->sector_size or of the "small sector" size in case - * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined. + * info->sector_size, the "small sector" size in case + * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if + * there is no information about the sector size. The + * latter is the case if the flash parameters are parsed + * solely by SFDP, then the largest supported erase type + * is selected. * * Once the optimum uniform sector erase command is found, disable all the * other. @@ -2113,6 +2143,10 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map, tested_erase = &map->erase_type[i]; + /* Skip masked erase types. */ + if (!tested_erase->size) + continue; + /* * If the current erase size is the one, stop here: * we have found the right uniform Sector Erase command. @@ -2565,6 +2599,12 @@ static void spi_nor_init_default_params(struct spi_nor *nor) 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); + + if (info->flags & SPI_NOR_QUAD_PP) { + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4); + } } /** @@ -2840,10 +2880,20 @@ static void spi_nor_put_device(struct mtd_info *mtd) void spi_nor_restore(struct spi_nor *nor) { + int ret; + /* restore the addressing mode */ if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && - nor->flags & SNOR_F_BROKEN_RESET) - nor->params->set_4byte_addr_mode(nor, false); + nor->flags & SNOR_F_BROKEN_RESET) { + ret = nor->params->set_4byte_addr_mode(nor, false); + if (ret) + /* + * Do not stop the execution in the hope that the flash + * will default to the 3-byte address mode after the + * software reset. + */ + dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret); + } if (nor->flags & SNOR_F_SOFT_RESET) spi_nor_soft_reset(nor); @@ -2935,6 +2985,27 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor) mtd->_put_device = spi_nor_put_device; } +static int spi_nor_hw_reset(struct spi_nor *nor) +{ + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(reset)) + return PTR_ERR_OR_ZERO(reset); + + /* + * Experimental delay values by looking at different flash device + * vendors datasheets. + */ + usleep_range(1, 5); + gpiod_set_value_cansleep(reset, 1); + usleep_range(100, 150); + gpiod_set_value_cansleep(reset, 0); + usleep_range(1000, 1200); + + return 0; +} + int spi_nor_scan(struct spi_nor *nor, const char *name, const struct spi_nor_hwcaps *hwcaps) { @@ -2967,6 +3038,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (!nor->bouncebuf) return -ENOMEM; + ret = spi_nor_hw_reset(nor); + if (ret) + return ret; + info = spi_nor_get_flash_info(nor, name); if (IS_ERR(info)) return PTR_ERR(info); |