2018-09-11 15:40:45

by Tudor Ambarus

[permalink] [raw]
Subject: [PATCH v3 0/2] add support to non-uniform SFDP SPI NOR flash memories

Backward compatibility test done on mx25l3273fm2i-08g with
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS set and unset.
Non-uniform erase test done on sst26vf064b-104i/sn with
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS set and unset.

Note that in order to do the non-uniform test you'll have to force the
spi_nor_has_uniform_erase() to return false, because the erase map is
tuned to come back to the uniform case. Also, for the SST flashes, you'll
have to unlock the global protection (see
https://www.mail-archive.com/[email protected]/msg1763887.html).

Changes in v3:
- drop "mtd: spi-nor: parse SFDP 4-byte Address Instruction Table":
let's add just a feature per patch set to make it easily digestible.
No worries, will resubmit it once the non-uniform erase supports gets
approved.
- SNOR_ERASE_FLAGS_OFFSET was buggy and since it's used only once
in the uniform erase case, remove it entirely and do the init by
hand
- describe all introduced functions together with arguments and
return value
- various minor updates: drop a cast, rename a function - cosmetics ...

Changes in v2:
- add JESD216B SFDP optional parsers: Sector Map Parameter Table parser and
4-byte Address Instruction Table parser (patch 2/3 and patch 3/3).

- save the Erase Types from bfpt to determine the Erase Type sizes. Sort all
the map's Erase Types in ascending order with the smallest erase size being
the first member in the erase_type array. Sort in the same way the Erase Type
mask in regions in order to have a 1-to-1 correspondence with the Erase
Types defined in the map. We sort the Erase Type by size, at init, in order to
speed up the process of finding the best erase command at run-time.

- remove setting of mtd->erasesize and nor->erase_opcode when parsing the Basic
Flash Parameter Table. mtd->erasesize was set to the maximum supported Erase
Type size, without verifying if that Erase Type can erase the entire flash.
mtd->erasesize is now set just in spi_nor_select_erase(), after we find which
Erase Types are supported in the uniform case.

- spi_nor_select_erase(): pass directly info->sector_size instead of the whole
flash_info structure in order to simplify the code.

- fix condition on finding erase regions:
while (addr < region_start || addr >= region_end) {
- fix the list command adding: list_add_tail() instead of list_add()
- write_enable(nor); before erasing the sectors
- As Boris suggested, remove likely() because it doesn't make any difference
given the time it takes to actually erase the block.
- bool return value for spi_nor_has_uniform_erase().

Changes in v1 or what I've done on top of Cyrille's work (https://lkml.org/lkml//
2017/4/15/70):
- minimize the amount of erase() calls by using the best sequence of erase
type commands depending on alignment.
- build the list of best fitted erase commands to be executed once we
validate that the erase can be performed.
- add improvements on how the erase map is handled. The regions are
consecutive in the address space, walk through the regions incrementally.
- speed up finding the best erase type command. Order erase types by
size, iterate them from the biggest to the smallest and stop when best
fitted command is found.
- determine at init if there are erase types that can erase the entire
memory
- fix support for overlaid regions.


Tudor Ambarus (2):
mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories
mtd: spi-nor: parse SFDP Sector Map Parameter Table

drivers/mtd/spi-nor/spi-nor.c | 910 +++++++++++++++++++++++++++++++++++++++---
include/linux/mtd/spi-nor.h | 119 ++++++
2 files changed, 971 insertions(+), 58 deletions(-)

--
2.9.4



2018-09-11 15:42:08

by Tudor Ambarus

[permalink] [raw]
Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Based on Cyrille Pitchen's patch https://lkml.org/lkml/2017/3/22/935.

This patch is a transitional patch in introducing the support of
SFDP SPI memories with non-uniform erase sizes like Spansion s25fs512s.
Non-uniform erase maps will be used later when initialized based on the
SFDP data.

Introduce the memory erase map which splits the memory array into one
or many erase regions. Each erase region supports up to 4 erase types,
as defined by the JEDEC JESD216B (SFDP) specification.

To be backward compatible, the erase map of uniform SPI NOR flash memories
is initialized so it contains only one erase region and this erase region
supports only one erase command. Hence a single size is used to erase any
sector/block of the memory.

Besides, since the algorithm used to erase sectors on non-uniform SPI NOR
flash memories is quite expensive, when possible, the erase map is tuned
to come back to the uniform case.

The 'erase with the best command, move forward and repeat' approach was
suggested by Cristian Birsan in a brainstorm session, so:

Suggested-by: Cristian Birsan <[email protected]>
Signed-off-by: Tudor Ambarus <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 594 +++++++++++++++++++++++++++++++++++++++---
include/linux/mtd/spi-nor.h | 107 ++++++++
2 files changed, 659 insertions(+), 42 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index dc8757e..4687345 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -18,6 +18,7 @@
#include <linux/math64.h>
#include <linux/sizes.h>
#include <linux/slab.h>
+#include <linux/sort.h>

#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
@@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+
+ if (!spi_nor_has_uniform_erase(nor)) {
+ struct spi_nor_erase_map *map = &nor->erase_map;
+ struct spi_nor_erase_type *erase;
+ int i;
+
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ erase = &map->erase_type[i];
+ erase->opcode =
+ spi_nor_convert_3to4_erase(erase->opcode);
+ }
+ }
}

/* Enable/disable 4-byte addressing mode. */
@@ -499,6 +512,275 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
}

/*
+ * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @dividend: dividend value
+ * @remainder: pointer to u32 remainder (will be updated)
+ *
+ * Returns two values: remainder and the new dividend
+ */
+static u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
+ u64 dividend, u32 *remainder)
+{
+ /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
+ *remainder = (u32)dividend & erase->size_mask;
+ return dividend >> erase->size_shift;
+}
+
+/*
+ * spi_nor_find_best_erase_type() - find the best erase type for the given
+ * offset in the serial flash memory and the number of bytes to erase. The
+ * region in which the address fits is expected to be provided.
+ * @map: the erase map of the SPI NOR
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Returns a pointer to the best fitted erase type, NULL otherwise.
+ */
+static const struct spi_nor_erase_type *
+spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
+ const struct spi_nor_erase_region *region,
+ u64 addr, u32 len)
+{
+ const struct spi_nor_erase_type *erase;
+ u32 rem;
+ int i;
+ u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
+
+ /*
+ * Erase types are ordered by size, with the biggest erase type at
+ * index 0.
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ /* Does the erase region support the tested erase type? */
+ if (!(erase_mask & BIT(i)))
+ continue;
+
+ erase = &map->erase_type[i];
+
+ /* Don't erase more than what the user has asked for. */
+ if (erase->size > len)
+ continue;
+
+ /* Alignment is not mandatory for overlaid regions */
+ if (region->offset & SNOR_OVERLAID_REGION)
+ return erase;
+
+ spi_nor_div_by_erase_size(erase, addr, &rem);
+ if (rem)
+ continue;
+ else
+ return erase;
+ }
+
+ return NULL;
+}
+
+/*
+ * spi_nor_region_next() - get the next spi nor region
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ *
+ * Returns the next spi nor region or NULL if last region.
+ */
+static struct spi_nor_erase_region *
+spi_nor_region_next(struct spi_nor_erase_region *region)
+{
+ if (spi_nor_region_is_last(region))
+ return NULL;
+ region++;
+ return region;
+}
+
+/*
+ * spi_nor_find_erase_region() - find the region of the serial flash memory in
+ * which the offset fits
+ * @map: the erase map of the SPI NOR
+ * @addr: offset in the serial flash memory
+ *
+ * Returns pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
+ * otherwise.
+ */
+static struct spi_nor_erase_region *
+spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64 addr)
+{
+ struct spi_nor_erase_region *region = map->regions;
+ u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
+ u64 region_end = region_start + region->size;
+
+ while (addr < region_start || addr >= region_end) {
+ region = spi_nor_region_next(region);
+ if (!region)
+ return ERR_PTR(-EINVAL);
+
+ region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
+ region_end = region_start + region->size;
+ }
+
+ return region;
+}
+
+/*
+ * spi_nor_init_erase_cmd() - initialize an erase command
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ *
+ * Returns the pointer to the allocated erase command, ERR_PTR(-errno)
+ * otherwise.
+ */
+static struct spi_nor_erase_command *
+spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
+ const struct spi_nor_erase_type *erase)
+{
+ struct spi_nor_erase_command *cmd;
+
+ cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cmd->list);
+ cmd->opcode = erase->opcode;
+ cmd->count = 1;
+
+ if (region->offset & SNOR_OVERLAID_REGION)
+ cmd->size = region->size;
+ else
+ cmd->size = erase->size;
+
+ return cmd;
+}
+
+/*
+ * spi_nor_destroy_erase_cmd_list() - destroy erase command list
+ * @erase_list: list of erase commands
+ */
+static void spi_nor_destroy_erase_cmd_list(struct list_head *erase_list)
+{
+ struct spi_nor_erase_command *cmd, *next;
+
+ list_for_each_entry_safe(cmd, next, erase_list, list) {
+ list_del(&cmd->list);
+ kfree(cmd);
+ }
+}
+
+/*
+ * spi_nor_init_erase_cmd_list() - initialize erase command list
+ * @nor: pointer to a 'struct spi_nor'
+ * @erase_list: list of erase commands to be executed once we validate that the
+ * erase can be performed
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Builds the list of best fitted erase commands and verifies if the erase can
+ * be performed.
+ *
+ * Returns 0 on success, -errno otherwise.
+ */
+static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
+ struct list_head *erase_list,
+ u64 addr, u32 len)
+{
+ const struct spi_nor_erase_map *map = &nor->erase_map;
+ const struct spi_nor_erase_type *erase, *prev_erase = NULL;
+ struct spi_nor_erase_region *region;
+ struct spi_nor_erase_command *cmd = NULL;
+ u64 region_end;
+ int ret = -EINVAL;
+
+ region = spi_nor_find_erase_region(map, addr);
+ if (IS_ERR(region))
+ return PTR_ERR(region);
+
+ region_end = spi_nor_region_end(region);
+
+ while (len) {
+ erase = spi_nor_find_best_erase_type(map, region, addr, len);
+ if (!erase)
+ goto destroy_erase_cmd_list;
+
+ if (prev_erase != erase ||
+ region->offset & SNOR_OVERLAID_REGION) {
+ cmd = spi_nor_init_erase_cmd(region, erase);
+ if (IS_ERR(cmd)) {
+ ret = PTR_ERR(cmd);
+ goto destroy_erase_cmd_list;
+ }
+
+ list_add_tail(&cmd->list, erase_list);
+ } else {
+ cmd->count++;
+ }
+
+ addr += cmd->size;
+ len -= cmd->size;
+
+ if (len && addr >= region_end) {
+ region = spi_nor_region_next(region);
+ if (!region)
+ goto destroy_erase_cmd_list;
+ region_end = spi_nor_region_end(region);
+ }
+
+ prev_erase = erase;
+ }
+
+ return 0;
+
+destroy_erase_cmd_list:
+ spi_nor_destroy_erase_cmd_list(erase_list);
+ return ret;
+}
+
+/*
+ * spi_nor_erase_multi_sectors() - perform a non-uniform erase
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Build a list of best fitted erase commands and execute it once we
+ * validate that the erase can be performed.
+ *
+ * Returns 0 on success, -errno otherwise.
+ */
+static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
+{
+ LIST_HEAD(erase_list);
+ struct spi_nor_erase_command *cmd, *next;
+ int ret;
+
+ ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(cmd, next, &erase_list, list) {
+ nor->erase_opcode = cmd->opcode;
+ while (cmd->count) {
+ write_enable(nor);
+
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
+ goto destroy_erase_cmd_list;
+
+ addr += cmd->size;
+ cmd->count--;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto destroy_erase_cmd_list;
+ }
+ list_del(&cmd->list);
+ kfree(cmd);
+ }
+
+ return 0;
+
+destroy_erase_cmd_list:
+ spi_nor_destroy_erase_cmd_list(&erase_list);
+ return ret;
+}
+
+/*
* Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
@@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
(long long)instr->len);

- div_u64_rem(instr->len, mtd->erasesize, &rem);
- if (rem)
- return -EINVAL;
+ if (spi_nor_has_uniform_erase(nor)) {
+ div_u64_rem(instr->len, mtd->erasesize, &rem);
+ if (rem)
+ return -EINVAL;
+ }

addr = instr->addr;
len = instr->len;
@@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
*/

/* "sector"-at-a-time erase */
- } else {
+ } else if (spi_nor_has_uniform_erase(nor)) {
while (len) {
write_enable(nor);

@@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (ret)
goto erase_err;
}
+
+ /* erase multiple sectors */
+ } else {
+ ret = spi_nor_erase_multi_sectors(nor, addr, len);
+ if (ret)
+ goto erase_err;
}

write_disable(nor);
@@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = {

static int spi_nor_hwcaps_read2cmd(u32 hwcaps);

+/*
+ * spi_nor_set_erase_type() - set a SPI NOR erase type
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @size: the size of the sector/block erased by the erase type
+ * @opcode: the SPI command op code to erase the sector/block
+ */
+static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
+ u32 size, u8 opcode)
+{
+ erase->size = size;
+ erase->opcode = opcode;
+ /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
+ erase->size_shift = ffs(erase->size) - 1;
+ erase->size_mask = (1 << erase->size_shift) - 1;
+}
+
+/*
+ * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @size: the size of the sector/block erased by the erase type
+ * @opcode: the SPI command op code to erase the sector/block
+ * @i: erase type index as sorted in the Basic Flash Parameter Table
+ *
+ * The supported Erase Types will be sorted at init in ascending order, with
+ * the smallest Erase Type size being the first member in the erase_type array
+ * of the spi_nor_erase_map structure. Save the Erase Type index as sorted in
+ * the Basic Flash Parameter Table since it will be used later on to
+ * synchronize with the supported Erase Types defined in SFDP optional tables.
+ */
+static void
+spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
+ u32 size, u8 opcode, u8 i)
+{
+ erase->idx = i;
+ spi_nor_set_erase_type(erase, size, opcode);
+}
+
+/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
+ * @l: member in the left half of the map's erase_type array
+ * @r: member in the right half of the map's erase_type array
+ *
+ * Comparison function used in the sort() call to sort in ascending order the
+ * map's erase types, the smallest erase type size being the first member in the
+ * sorted erase_type array.
+ */
+static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
+{
+ const struct spi_nor_erase_type *left = l, *right = r;
+
+ return left->size - right->size;
+}
+
+/*
+ * spi_nor_regions_sort_erase_types() - sort erase types in each region
+ * @map: the erase map of the SPI NOR
+ *
+ * Function assumes that the erase types defined in the erase map are already
+ * sorted in ascending order, with the smallest erase type size being the first
+ * member in the erase_type array. It replicates the sort done for the map's
+ * erase types. Each region's erase bitmask will indicate which erase types are
+ * supported from the sorted erase types defined in the erase map.
+ * Sort the all region's erase type at init in order to speed up the process of
+ * finding the best erase command at runtime.
+ */
+static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
+{
+ struct spi_nor_erase_region *region = map->regions;
+ struct spi_nor_erase_type *erase_type = map->erase_type;
+ int i;
+ u8 region_erase_mask, sorted_erase_mask;
+
+ while (region) {
+ region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
+
+ /* Replicate the sort done for the map's erase types. */
+ sorted_erase_mask = 0;
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (erase_type[i].size &&
+ region_erase_mask & BIT(erase_type[i].idx))
+ sorted_erase_mask |= BIT(i);
+
+ /* Overwrite erase mask. */
+ region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
+ sorted_erase_mask;
+
+ region = spi_nor_region_next(region);
+ }
+}
+
+/*
+ *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
+ * @map: the erase map of the SPI NOR
+ * @erase_mask: bitmask encoding erase types that can erase the entire
+ * flash memory
+ * @flash_size: the spi nor flash memory size
+ */
+static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
+ u8 erase_mask, u64 flash_size)
+{
+ /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
+ map->uniform_region.offset = (erase_mask & SNOR_ERASE_TYPE_MASK) |
+ SNOR_LAST_REGION;
+ map->uniform_region.size = flash_size;
+ map->regions = &map->uniform_region;
+ map->uniform_erase_type = erase_mask;
+}
+
/**
* spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
* @nor: pointer to a 'struct spi_nor'
@@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
struct spi_nor_flash_parameter *params)
{
- struct mtd_info *mtd = &nor->mtd;
+ struct spi_nor_erase_map *map = &nor->erase_map;
+ struct spi_nor_erase_type *erase_type = map->erase_type;
struct sfdp_bfpt bfpt;
size_t len;
int i, cmd, err;
u32 addr;
u16 half;
+ u8 erase_mask;

/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
@@ -2298,7 +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
}

- /* Sector Erase settings. */
+ /*
+ * Sector Erase settings. Reinitialize the uniform erase map using the
+ * Erase Types defined in the bfpt table.
+ */
+ erase_mask = 0;
+ memset(&nor->erase_map, 0, sizeof(nor->erase_map));
for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
u32 erasesize;
@@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,

erasesize = 1U << erasesize;
opcode = (half >> 8) & 0xff;
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
- if (erasesize == SZ_4K) {
- nor->erase_opcode = opcode;
- mtd->erasesize = erasesize;
- break;
- }
-#endif
- if (!mtd->erasesize || mtd->erasesize < erasesize) {
- nor->erase_opcode = opcode;
- mtd->erasesize = erasesize;
- }
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_settings_from_bfpt(&erase_type[i], erasesize,
+ opcode, i);
}
+ spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+ /*
+ * Sort all the map's Erase Types in ascending order with the smallest
+ * erase size being the first member in the erase_type array.
+ */
+ sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
+ spi_nor_map_cmp_erase_type, NULL);
+ /*
+ * Sort the erase types in the uniform region in order to update the
+ * uniform_erase_type bitmask. The bitmask will be used later on when
+ * selecting the uniform erase.
+ */
+ spi_nor_regions_sort_erase_types(map);
+ map->uniform_erase_type = map->uniform_region.offset &
+ SNOR_ERASE_TYPE_MASK;

/* Stop here if not JESD216 rev A or later. */
if (bfpt_header->length < BFPT_DWORD_MAX)
@@ -2480,6 +2891,9 @@ static int spi_nor_init_params(struct spi_nor *nor,
const struct flash_info *info,
struct spi_nor_flash_parameter *params)
{
+ struct spi_nor_erase_map *map = &nor->erase_map;
+ u8 i, erase_mask;
+
/* Set legacy flash parameters as default. */
memset(params, 0, sizeof(*params));

@@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP, SNOR_PROTO_1_1_1);

+ /*
+ * Sector Erase settings. Sort Erase Types in ascending order, with the
+ * smallest erase size starting at BIT(0).
+ */
+ erase_mask = 0;
+ i = 0;
+ if (info->flags & SECT_4K_PMC) {
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+ SPINOR_OP_BE_4K_PMC);
+ i++;
+ } else if (info->flags & SECT_4K) {
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+ SPINOR_OP_BE_4K);
+ i++;
+ }
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
+ SPINOR_OP_SE);
+ spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+
/* Select the procedure to set the Quad Enable bit. */
if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
SNOR_HWCAPS_PP_QUAD)) {
@@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor *nor,
params->quad_enable = info->quad_enable;
}

- /* Override the parameters with data read from SFDP tables. */
- nor->addr_width = 0;
- nor->mtd.erasesize = 0;
if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
!(info->flags & SPI_NOR_SKIP_SFDP)) {
struct spi_nor_flash_parameter sfdp_params;
+ struct spi_nor_erase_map prev_map;

memcpy(&sfdp_params, params, sizeof(sfdp_params));
- if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
- nor->addr_width = 0;
- nor->mtd.erasesize = 0;
- } else {
+ memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
+
+ if (spi_nor_parse_sfdp(nor, &sfdp_params))
+ /* restore previous erase map */
+ memcpy(&nor->erase_map, &prev_map,
+ sizeof(nor->erase_map));
+ else
memcpy(params, &sfdp_params, sizeof(*params));
- }
}

return 0;
@@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor *nor,
return 0;
}

-static int spi_nor_select_erase(struct spi_nor *nor,
- const struct flash_info *info)
+/*
+ * 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.
+ *
+ * Once the optimum uniform sector erase command is found, disable all the
+ * other.
+ *
+ * Return: pointer to erase type on success, NULL otherwise.
+ */
+static const struct spi_nor_erase_type *
+spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
+ const u32 wanted_size)
{
- struct mtd_info *mtd = &nor->mtd;
+ const struct spi_nor_erase_type *tested_erase, *erase = NULL;
+ int i;
+ u8 uniform_erase_type = map->uniform_erase_type;

- /* Do nothing if already configured from SFDP. */
- if (mtd->erasesize)
- return 0;
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (!(uniform_erase_type & BIT(i)))
+ continue;
+
+ tested_erase = &map->erase_type[i];
+
+ /*
+ * If the current erase size is the one, stop here:
+ * we have found the right uniform Sector Erase command.
+ */
+ if (tested_erase->size == wanted_size) {
+ erase = tested_erase;
+ break;
+ }

+ /*
+ * Otherwise, the current erase size is still a valid canditate.
+ * Select the biggest valid candidate.
+ */
+ if (!erase && tested_erase->size)
+ erase = tested_erase;
+ /* keep iterating to find the wanted_size */
+ }
+
+ if (!erase)
+ return NULL;
+
+ /* Disable all other Sector Erase commands. */
+ map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
+ map->uniform_erase_type |= BIT(erase - map->erase_type);
+ return erase;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size)
+{
+ struct spi_nor_erase_map *map = &nor->erase_map;
+ const struct spi_nor_erase_type *erase = NULL;
+ struct mtd_info *mtd = &nor->mtd;
+ int i;
+
+ /*
+ * The previous implementation handling Sector Erase commands assumed
+ * that the SPI flash memory has an uniform layout then used only one
+ * of the supported erase sizes for all Sector Erase commands.
+ * So to be backward compatible, the new implementation also tries to
+ * manage the SPI flash memory as uniform with a single erase sector
+ * size, when possible.
+ */
#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
+ wanted_size = 4096u;
#endif
- {
- nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
+
+ if (spi_nor_has_uniform_erase(nor)) {
+ erase = spi_nor_select_uniform_erase(map, wanted_size);
+ if (!erase)
+ return -EINVAL;
+ nor->erase_opcode = erase->opcode;
+ mtd->erasesize = erase->size;
+ return 0;
}
+
+ /*
+ * For non-uniform SPI flash memory, set mtd->erasesize to the
+ * maximum erase sector size. No need to set nor->erase_opcode.
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (map->erase_type[i].size) {
+ erase = &map->erase_type[i];
+ break;
+ }
+ }
+
+ if (!erase)
+ return -EINVAL;
+
+ mtd->erasesize = erase->size;
return 0;
}

@@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
}

/* Select the Sector Erase command. */
- err = spi_nor_select_erase(nor, info);
+ err = spi_nor_select_erase(nor, info->sector_size);
if (err) {
dev_err(nor->dev,
"can't select erase settings supported by both the SPI controller and memory.\n");
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 09a10fd..a873a0b 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -240,6 +240,94 @@ enum spi_nor_option_flags {
};

/**
+ * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
+ * @size: the size of the sector/block erased by the erase type.
+ * JEDEC JESD216B imposes erase sizes to be a power of 2.
+ * @size_shift: @size is a power of 2, the shift is stored in
+ * @size_shift.
+ * @size_mask: the size mask based on @size_shift.
+ * @opcode: the SPI command op code to erase the sector/block.
+ * @idx: Erase Type index as sorted in the Basic Flash Parameter
+ * Table. It will be used to synchronize the supported
+ * Erase Types with the ones identified in the SFDP
+ * optional tables.
+ */
+struct spi_nor_erase_type {
+ u32 size;
+ u32 size_shift;
+ u32 size_mask;
+ u8 opcode;
+ u8 idx;
+};
+
+/**
+ * struct spi_nor_erase_command - Used for non-uniform erases
+ * The structure is used to describe a list of erase commands to be executed
+ * once we validate that the erase can be performed. The elements in the list
+ * are run-length encoded.
+ * @list: for inclusion into the list of erase commands.
+ * @count: how many times the same erase command should be
+ * consecutively used.
+ * @size: the size of the sector/block erased by the command.
+ * @opcode: the SPI command op code to erase the sector/block.
+ */
+struct spi_nor_erase_command {
+ struct list_head list;
+ u32 count;
+ u32 size;
+ u8 opcode;
+};
+
+/**
+ * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
+ * @offset: the offset in the data array of erase region start.
+ * LSB bits are used as a bitmask encoding flags to
+ * determine if this region is overlaid, if this region is
+ * the last in the SPI NOR flash memory and to indicate
+ * all the supported erase commands inside this region.
+ * The erase types are sorted in ascending order with the
+ * smallest Erase Type size being at BIT(0).
+ * @size: the size of the region in bytes.
+ */
+struct spi_nor_erase_region {
+ u64 offset;
+ u64 size;
+};
+
+#define SNOR_ERASE_TYPE_MAX 4
+#define SNOR_ERASE_TYPE_MASK GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
+
+#define SNOR_LAST_REGION BIT(4)
+#define SNOR_OVERLAID_REGION BIT(5)
+
+#define SNOR_ERASE_FLAGS_MAX 6
+#define SNOR_ERASE_FLAGS_MASK GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
+
+/**
+ * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
+ * @regions: array of erase regions. The regions are consecutive in
+ * address space. Walking through the regions is done
+ * incrementally.
+ * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform
+ * sector size (legacy implementation).
+ * @erase_type: an array of erase types shared by all the regions.
+ * The erase types are sorted in ascending order, with the
+ * smallest Erase Type size being the first member in the
+ * erase_type array.
+ * @uniform_erase_type: bitmask encoding erase types that can erase the
+ * entire memory. This member is completed at init by
+ * uniform and non-uniform SPI NOR flash memories if they
+ * support at least one erase type that can erase the
+ * entire memory.
+ */
+struct spi_nor_erase_map {
+ struct spi_nor_erase_region *regions;
+ struct spi_nor_erase_region uniform_region;
+ struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
+ u8 uniform_erase_type;
+};
+
+/**
* struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan()
*/
@@ -263,6 +351,7 @@ struct flash_info;
* @write_proto: the SPI protocol for write operations
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations
* @cmd_buf: used by the write_reg
+ * @erase_map: the erase map of the SPI NOR
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the
@@ -298,6 +387,7 @@ struct spi_nor {
bool sst_write_second;
u32 flags;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+ struct spi_nor_erase_map erase_map;

int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
@@ -318,6 +408,23 @@ struct spi_nor {
void *priv;
};

+static u64 __maybe_unused
+spi_nor_region_is_last(const struct spi_nor_erase_region *region)
+{
+ return region->offset & SNOR_LAST_REGION;
+}
+
+static u64 __maybe_unused
+spi_nor_region_end(const struct spi_nor_erase_region *region)
+{
+ return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
+}
+
+static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
+{
+ return !!nor->erase_map.uniform_erase_type;
+}
+
static inline void spi_nor_set_flash_node(struct spi_nor *nor,
struct device_node *np)
{
--
2.9.4


2018-09-11 15:43:24

by Tudor Ambarus

[permalink] [raw]
Subject: [PATCH v3 2/2] mtd: spi-nor: parse SFDP Sector Map Parameter Table

Add support for the SFDP (JESD216B) Sector Map Parameter Table. This
table is optional, but when available, we parse it to identify the
location and size of sectors within the main data array of the
flash memory device and to identify which Erase Types are supported by
each sector.

Signed-off-by: Tudor Ambarus <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 316 +++++++++++++++++++++++++++++++++++++++---
include/linux/mtd/spi-nor.h | 12 ++
2 files changed, 312 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 4687345..fbefcdb 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2179,6 +2179,35 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
*/

/**
+ * spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
+ * addr_width and read_dummy members of the struct spi_nor should be previously
+ * set.
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to read
+ * @buf: buffer where the data is copied into
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf)
+{
+ int ret;
+
+ while (len) {
+ ret = nor->read(nor, addr, len, buf);
+ if (!ret || ret > len)
+ return -EIO;
+ if (ret < 0)
+ return ret;
+
+ buf += ret;
+ addr += ret;
+ len -= ret;
+ }
+ return 0;
+}
+
+/**
* spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
* @addr: offset in the SFDP area to start reading data from
@@ -2205,22 +2234,8 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
nor->addr_width = 3;
nor->read_dummy = 8;

- while (len) {
- ret = nor->read(nor, addr, len, (u8 *)buf);
- if (!ret || ret > len) {
- ret = -EIO;
- goto read_err;
- }
- if (ret < 0)
- goto read_err;
-
- buf += ret;
- addr += ret;
- len -= ret;
- }
- ret = 0;
+ ret = spi_nor_read_raw(nor, addr, len, buf);

-read_err:
nor->read_opcode = read_opcode;
nor->addr_width = addr_width;
nor->read_dummy = read_dummy;
@@ -2777,6 +2792,275 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
return 0;
}

+#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22)
+#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_4 (0x2UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_USE_CURRENT (0x3UL << 22)
+
+#define SMPT_CMD_READ_DUMMY_MASK GENMASK(19, 16)
+#define SMPT_CMD_READ_DUMMY_SHIFT 16
+#define SMPT_CMD_READ_DUMMY(_cmd) \
+ (((_cmd) & SMPT_CMD_READ_DUMMY_MASK) >> SMPT_CMD_READ_DUMMY_SHIFT)
+#define SMPT_CMD_READ_DUMMY_IS_VARIABLE 0xfUL
+
+#define SMPT_CMD_READ_DATA_MASK GENMASK(31, 24)
+#define SMPT_CMD_READ_DATA_SHIFT 24
+#define SMPT_CMD_READ_DATA(_cmd) \
+ (((_cmd) & SMPT_CMD_READ_DATA_MASK) >> SMPT_CMD_READ_DATA_SHIFT)
+
+#define SMPT_CMD_OPCODE_MASK GENMASK(15, 8)
+#define SMPT_CMD_OPCODE_SHIFT 8
+#define SMPT_CMD_OPCODE(_cmd) \
+ (((_cmd) & SMPT_CMD_OPCODE_MASK) >> SMPT_CMD_OPCODE_SHIFT)
+
+#define SMPT_MAP_REGION_COUNT_MASK GENMASK(23, 16)
+#define SMPT_MAP_REGION_COUNT_SHIFT 16
+#define SMPT_MAP_REGION_COUNT(_header) \
+ ((((_header) & SMPT_MAP_REGION_COUNT_MASK) >> \
+ SMPT_MAP_REGION_COUNT_SHIFT) + 1)
+
+#define SMPT_MAP_ID_MASK GENMASK(15, 8)
+#define SMPT_MAP_ID_SHIFT 8
+#define SMPT_MAP_ID(_header) \
+ (((_header) & SMPT_MAP_ID_MASK) >> SMPT_MAP_ID_SHIFT)
+
+#define SMPT_MAP_REGION_SIZE_MASK GENMASK(31, 8)
+#define SMPT_MAP_REGION_SIZE_SHIFT 8
+#define SMPT_MAP_REGION_SIZE(_region) \
+ (((((_region) & SMPT_MAP_REGION_SIZE_MASK) >> \
+ SMPT_MAP_REGION_SIZE_SHIFT) + 1) * 256)
+
+#define SMPT_MAP_REGION_ERASE_TYPE_MASK GENMASK(3, 0)
+#define SMPT_MAP_REGION_ERASE_TYPE(_region) \
+ ((_region) & SMPT_MAP_REGION_ERASE_TYPE_MASK)
+
+#define SMPT_DESC_TYPE_MAP BIT(1)
+#define SMPT_DESC_END BIT(0)
+
+/*
+ * spi_nor_smpt_addr_width() - return the address width used in the
+ * configuration detection command.
+ * @nor: pointer to a 'struct spi_nor'
+ * @settings: configuration detection command descriptor, dword1
+ */
+static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings)
+{
+ switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) {
+ case SMPT_CMD_ADDRESS_LEN_0:
+ return 0;
+ case SMPT_CMD_ADDRESS_LEN_3:
+ return 3;
+ case SMPT_CMD_ADDRESS_LEN_4:
+ return 4;
+ case SMPT_CMD_ADDRESS_LEN_USE_CURRENT:
+ /* fall through */
+ default:
+ return nor->addr_width;
+ }
+}
+
+/*
+ * spi_nor_smpt_read_dummy() - return the configuration detection command read
+ * latency, in clock cycles.
+ * @nor: pointer to a 'struct spi_nor'
+ * @settings: configuration detection command descriptor, dword1
+ */
+static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
+{
+ u8 read_dummy = SMPT_CMD_READ_DUMMY(settings);
+
+ if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE)
+ return nor->read_dummy;
+ return read_dummy;
+}
+
+/*
+ * spi_nor_get_map_in_use() - get the configuration map in use
+ * @nor: pointer to a 'struct spi_nor'
+ * @smpt: pointer to the sector map parameter table
+ */
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+{
+ const u32 *ret = NULL;
+ u32 i, addr;
+ int err;
+ u8 addr_width, read_opcode, read_dummy;
+ u8 read_data_mask, data_byte, map_id;
+
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+ read_opcode = nor->read_opcode;
+
+ map_id = 0;
+ i = 0;
+ /* Determine if there are any optional Detection Command Descriptors */
+ while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
+ nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
+ nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
+ nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
+ addr = smpt[i + 1];
+
+ err = spi_nor_read_raw(nor, addr, 1, &data_byte);
+ if (err)
+ goto out;
+
+ /*
+ * Build an index value that is used to select the Sector Map
+ * Configuration that is currently in use.
+ */
+ map_id = map_id << 1 | !!(data_byte & read_data_mask);
+ i = i + 2;
+ }
+
+ /* Find the matching configuration map */
+ while (SMPT_MAP_ID(smpt[i]) != map_id) {
+ if (smpt[i] & SMPT_DESC_END)
+ goto out;
+ /* increment the table index to the next map */
+ i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
+ }
+
+ ret = smpt + i;
+ /* fall through */
+out:
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+ nor->read_opcode = read_opcode;
+ return ret;
+}
+
+/*
+ * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @erase_type: erase type bitmask
+ */
+static void
+spi_nor_region_check_overlay(struct spi_nor_erase_region *region,
+ const struct spi_nor_erase_type *erase,
+ const u8 erase_type)
+{
+ int i;
+
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ if (!(erase_type & BIT(i)))
+ continue;
+ if (region->size & erase[i].size_mask) {
+ spi_nor_region_mark_overlay(region);
+ return;
+ }
+ }
+}
+
+/*
+ * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map
+ * @nor: pointer to a 'struct spi_nor'
+ * @smpt: pointer to the sector map parameter table
+ *
+ * Returns 0 on success, -errno otherwise.
+ */
+static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
+ const u32 *smpt)
+{
+ struct spi_nor_erase_map *map = &nor->erase_map;
+ const struct spi_nor_erase_type *erase = map->erase_type;
+ struct spi_nor_erase_region *region;
+ u64 offset;
+ u32 region_count;
+ int i, j;
+ u8 erase_type;
+
+ region_count = SMPT_MAP_REGION_COUNT(*smpt);
+ /*
+ * The regions will be freed when the driver detaches from the
+ * device.
+ */
+ region = devm_kcalloc(nor->dev, region_count, sizeof(*region),
+ GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ map->regions = region;
+
+ map->uniform_erase_type = 0xff;
+ offset = 0;
+ /* Populate regions. */
+ for (i = 0; i < region_count; i++) {
+ j = i + 1; /* index for the region dword */
+ region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]);
+ erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]);
+ region[i].offset = offset | erase_type;
+
+ spi_nor_region_check_overlay(&region[i], erase, erase_type);
+
+ /*
+ * Save the erase types that are supported in all regions and
+ * can erase the entire flash memory.
+ */
+ map->uniform_erase_type &= erase_type;
+
+ offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
+ region[i].size;
+ }
+
+ spi_nor_region_mark_end(&region[i - 1]);
+
+ return 0;
+}
+
+/*
+ * spi_nor_parse_smpt() - parse Sector Map Parameter Table
+ * @nor: pointer to a 'struct spi_nor'
+ * @smpt_header: sector map parameter table header
+ *
+ * This table is optional, but when available, we parse it to identify the
+ * location and size of sectors within the main data array of the flash memory
+ * device and to identify which Erase Types are supported by each sector.
+ *
+ * Returns 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_smpt(struct spi_nor *nor,
+ const struct sfdp_parameter_header *smpt_header)
+{
+ const u32 *sector_map;
+ u32 *smpt;
+ size_t len;
+ u32 addr;
+ int i, ret;
+
+ /* Read the Sector Map Parameter Table. */
+ len = smpt_header->length * sizeof(*smpt);
+ smpt = kzalloc(len, GFP_KERNEL);
+ if (!smpt)
+ return -ENOMEM;
+
+ addr = SFDP_PARAM_HEADER_PTP(smpt_header);
+ ret = spi_nor_read_sfdp(nor, addr, len, smpt);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the SMPT DWORDs. */
+ for (i = 0; i < smpt_header->length; i++)
+ smpt[i] = le32_to_cpu(smpt[i]);
+
+ sector_map = spi_nor_get_map_in_use(nor, smpt);
+ if (!sector_map) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = spi_nor_init_non_uniform_erase_map(nor, sector_map);
+ if (ret)
+ goto out;
+
+ spi_nor_regions_sort_erase_types(&nor->erase_map);
+ /* fall through */
+out:
+ kfree(smpt);
+ return ret;
+}
+
/**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
@@ -2871,7 +3155,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,

switch (SFDP_PARAM_HEADER_ID(param_header)) {
case SFDP_SECTOR_MAP_ID:
- dev_info(dev, "non-uniform erase sector maps are not supported yet.\n");
+ err = spi_nor_parse_smpt(nor, param_header);
break;

default:
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index a873a0b..ed8a5b7 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -420,6 +420,18 @@ spi_nor_region_end(const struct spi_nor_erase_region *region)
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
}

+static void __maybe_unused
+spi_nor_region_mark_end(struct spi_nor_erase_region *region)
+{
+ region->offset |= SNOR_LAST_REGION;
+}
+
+static void __maybe_unused
+spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
+{
+ region->offset |= SNOR_OVERLAID_REGION;
+}
+
static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
return !!nor->erase_map.uniform_erase_type;
--
2.9.4


2018-09-11 19:02:12

by Marek Vasut

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On 09/11/2018 05:40 PM, Tudor Ambarus wrote:
> Based on Cyrille Pitchen's patch https://lkml.org/lkml/2017/3/22/935.
>
> This patch is a transitional patch in introducing the support of
> SFDP SPI memories with non-uniform erase sizes like Spansion s25fs512s.
> Non-uniform erase maps will be used later when initialized based on the
> SFDP data.
>
> Introduce the memory erase map which splits the memory array into one
> or many erase regions. Each erase region supports up to 4 erase types,
> as defined by the JEDEC JESD216B (SFDP) specification.
>
> To be backward compatible, the erase map of uniform SPI NOR flash memories
> is initialized so it contains only one erase region and this erase region
> supports only one erase command. Hence a single size is used to erase any
> sector/block of the memory.
>
> Besides, since the algorithm used to erase sectors on non-uniform SPI NOR
> flash memories is quite expensive, when possible, the erase map is tuned
> to come back to the uniform case.
>
> The 'erase with the best command, move forward and repeat' approach was
> suggested by Cristian Birsan in a brainstorm session, so:
>
> Suggested-by: Cristian Birsan <[email protected]>
> Signed-off-by: Tudor Ambarus <[email protected]>

Reviewed-by: Marek Vasut <[email protected]>

--
Best regards,
Marek Vasut

2018-09-11 19:02:15

by Marek Vasut

[permalink] [raw]
Subject: Re: [PATCH v3 2/2] mtd: spi-nor: parse SFDP Sector Map Parameter Table

On 09/11/2018 05:40 PM, Tudor Ambarus wrote:
> Add support for the SFDP (JESD216B) Sector Map Parameter Table. This
> table is optional, but when available, we parse it to identify the
> location and size of sectors within the main data array of the
> flash memory device and to identify which Erase Types are supported by
> each sector.
>
> Signed-off-by: Tudor Ambarus <[email protected]>

Reviewed-by: Marek Vasut <[email protected]>


--
Best regards,
Marek Vasut

2018-09-17 17:08:32

by Tudor Ambarus

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi, Boris,

On 09/11/2018 06:40 PM, Tudor Ambarus wrote:
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c

[cut]

> /*
I missed to use the opening comment mark for kernel-doc comments: "/**". This
observation applies to all newly introduced function descriptions, in this patch
and in the next as well.
> + * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
> + * @erase: pointer to a structure that describes a SPI NOR erase type
> + * @dividend: dividend value
> + * @remainder: pointer to u32 remainder (will be updated)
> + *
> + * Returns two values: remainder and the new dividend

I should have used "Return: describe the return value" in order to be aligned
with the example from kernel-doc. This also applies to most of the function
descriptions.

[cut]

> +/*
> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map

add a space after that "*"

I will gladly submit a new version if there are no other comments. Please let me
know.

Cheers,
ta

2018-09-18 13:10:36

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 0/2] add support to non-uniform SFDP SPI NOR flash memories

On Tue, 11 Sep 2018 18:40:05 +0300
Tudor Ambarus <[email protected]> wrote:

> Backward compatibility test done on mx25l3273fm2i-08g with
> CONFIG_MTD_SPI_NOR_USE_4K_SECTORS set and unset.
> Non-uniform erase test done on sst26vf064b-104i/sn with
> CONFIG_MTD_SPI_NOR_USE_4K_SECTORS set and unset.
>
> Note that in order to do the non-uniform test you'll have to force the
> spi_nor_has_uniform_erase() to return false, because the erase map is
> tuned to come back to the uniform case. Also, for the SST flashes, you'll
> have to unlock the global protection (see
> https://www.mail-archive.com/[email protected]/msg1763887.html).
>
> Changes in v3:
> - drop "mtd: spi-nor: parse SFDP 4-byte Address Instruction Table":
> let's add just a feature per patch set to make it easily digestible.
> No worries, will resubmit it once the non-uniform erase supports gets
> approved.
> - SNOR_ERASE_FLAGS_OFFSET was buggy and since it's used only once
> in the uniform erase case, remove it entirely and do the init by
> hand
> - describe all introduced functions together with arguments and
> return value
> - various minor updates: drop a cast, rename a function - cosmetics ...
>
> Changes in v2:
> - add JESD216B SFDP optional parsers: Sector Map Parameter Table parser and
> 4-byte Address Instruction Table parser (patch 2/3 and patch 3/3).
>
> - save the Erase Types from bfpt to determine the Erase Type sizes. Sort all
> the map's Erase Types in ascending order with the smallest erase size being
> the first member in the erase_type array. Sort in the same way the Erase Type
> mask in regions in order to have a 1-to-1 correspondence with the Erase
> Types defined in the map. We sort the Erase Type by size, at init, in order to
> speed up the process of finding the best erase command at run-time.
>
> - remove setting of mtd->erasesize and nor->erase_opcode when parsing the Basic
> Flash Parameter Table. mtd->erasesize was set to the maximum supported Erase
> Type size, without verifying if that Erase Type can erase the entire flash.
> mtd->erasesize is now set just in spi_nor_select_erase(), after we find which
> Erase Types are supported in the uniform case.
>
> - spi_nor_select_erase(): pass directly info->sector_size instead of the whole
> flash_info structure in order to simplify the code.
>
> - fix condition on finding erase regions:
> while (addr < region_start || addr >= region_end) {
> - fix the list command adding: list_add_tail() instead of list_add()
> - write_enable(nor); before erasing the sectors
> - As Boris suggested, remove likely() because it doesn't make any difference
> given the time it takes to actually erase the block.
> - bool return value for spi_nor_has_uniform_erase().
>
> Changes in v1 or what I've done on top of Cyrille's work (https://lkml.org/lkml//
> 2017/4/15/70):
> - minimize the amount of erase() calls by using the best sequence of erase
> type commands depending on alignment.
> - build the list of best fitted erase commands to be executed once we
> validate that the erase can be performed.
> - add improvements on how the erase map is handled. The regions are
> consecutive in the address space, walk through the regions incrementally.
> - speed up finding the best erase type command. Order erase types by
> size, iterate them from the biggest to the smallest and stop when best
> fitted command is found.
> - determine at init if there are erase types that can erase the entire
> memory
> - fix support for overlaid regions.
>
>
> Tudor Ambarus (2):
> mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories
> mtd: spi-nor: parse SFDP Sector Map Parameter Table

Queued to spi-nor/next after fixing the kerneldoc headers.

Thanks,

Boris

2018-10-16 09:52:52

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Tudor,

This patch is breaking the 1-4-4 Read protocol for the spansion flash "s25fl512s".

Without this patch read request command for Quad mode, 4-byte enable, is coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
But after applying this patch, read request command for Quad mode is coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.

This flash also supports non-uniform erase.
Can you please check and provide some suggestion?

--
Regards
Yogesh Gaur

> -----Original Message-----
> From: linux-mtd [mailto:[email protected]] On Behalf Of
> Tudor Ambarus
> Sent: Tuesday, September 11, 2018 9:10 PM
> To: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected]
> Cc: Tudor Ambarus <[email protected]>; linux-
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR
> flash memories
>
> Based on Cyrille Pitchen's patch
> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flkml.or
> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
> c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
> 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
>
> This patch is a transitional patch in introducing the support of SFDP SPI
> memories with non-uniform erase sizes like Spansion s25fs512s.
> Non-uniform erase maps will be used later when initialized based on the SFDP
> data.
>
> Introduce the memory erase map which splits the memory array into one or
> many erase regions. Each erase region supports up to 4 erase types, as defined
> by the JEDEC JESD216B (SFDP) specification.
>
> To be backward compatible, the erase map of uniform SPI NOR flash memories
> is initialized so it contains only one erase region and this erase region supports
> only one erase command. Hence a single size is used to erase any sector/block
> of the memory.
>
> Besides, since the algorithm used to erase sectors on non-uniform SPI NOR flash
> memories is quite expensive, when possible, the erase map is tuned to come
> back to the uniform case.
>
> The 'erase with the best command, move forward and repeat' approach was
> suggested by Cristian Birsan in a brainstorm session, so:
>
> Suggested-by: Cristian Birsan <[email protected]>
> Signed-off-by: Tudor Ambarus <[email protected]>
> ---
> drivers/mtd/spi-nor/spi-nor.c | 594
> +++++++++++++++++++++++++++++++++++++++---
> include/linux/mtd/spi-nor.h | 107 ++++++++
> 2 files changed, 659 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index
> dc8757e..4687345 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -18,6 +18,7 @@
> #include <linux/math64.h>
> #include <linux/sizes.h>
> #include <linux/slab.h>
> +#include <linux/sort.h>
>
> #include <linux/mtd/mtd.h>
> #include <linux/of_platform.h>
> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor
> *nor,
> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
> nor->program_opcode = spi_nor_convert_3to4_program(nor-
> >program_opcode);
> nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
> +
> + if (!spi_nor_has_uniform_erase(nor)) {
> + struct spi_nor_erase_map *map = &nor->erase_map;
> + struct spi_nor_erase_type *erase;
> + int i;
> +
> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
> + erase = &map->erase_type[i];
> + erase->opcode =
> + spi_nor_convert_3to4_erase(erase->opcode);
> + }
> + }
> }
>
> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275 @@ static
> int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) }
>
> /*
> + * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
> + * @erase: pointer to a structure that describes a SPI NOR erase type
> + * @dividend: dividend value
> + * @remainder: pointer to u32 remainder (will be updated)
> + *
> + * Returns two values: remainder and the new dividend */ static u64
> +spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
> + u64 dividend, u32 *remainder)
> +{
> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> + *remainder = (u32)dividend & erase->size_mask;
> + return dividend >> erase->size_shift;
> +}
> +
> +/*
> + * spi_nor_find_best_erase_type() - find the best erase type for the
> +given
> + * offset in the serial flash memory and the number of bytes to erase.
> +The
> + * region in which the address fits is expected to be provided.
> + * @map: the erase map of the SPI NOR
> + * @region: pointer to a structure that describes a SPI NOR erase region
> + * @addr: offset in the serial flash memory
> + * @len: number of bytes to erase
> + *
> + * Returns a pointer to the best fitted erase type, NULL otherwise.
> + */
> +static const struct spi_nor_erase_type *
> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
> + const struct spi_nor_erase_region *region,
> + u64 addr, u32 len)
> +{
> + const struct spi_nor_erase_type *erase;
> + u32 rem;
> + int i;
> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
> +
> + /*
> + * Erase types are ordered by size, with the biggest erase type at
> + * index 0.
> + */
> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> + /* Does the erase region support the tested erase type? */
> + if (!(erase_mask & BIT(i)))
> + continue;
> +
> + erase = &map->erase_type[i];
> +
> + /* Don't erase more than what the user has asked for. */
> + if (erase->size > len)
> + continue;
> +
> + /* Alignment is not mandatory for overlaid regions */
> + if (region->offset & SNOR_OVERLAID_REGION)
> + return erase;
> +
> + spi_nor_div_by_erase_size(erase, addr, &rem);
> + if (rem)
> + continue;
> + else
> + return erase;
> + }
> +
> + return NULL;
> +}
> +
> +/*
> + * spi_nor_region_next() - get the next spi nor region
> + * @region: pointer to a structure that describes a SPI NOR erase region
> + *
> + * Returns the next spi nor region or NULL if last region.
> + */
> +static struct spi_nor_erase_region *
> +spi_nor_region_next(struct spi_nor_erase_region *region) {
> + if (spi_nor_region_is_last(region))
> + return NULL;
> + region++;
> + return region;
> +}
> +
> +/*
> + * spi_nor_find_erase_region() - find the region of the serial flash
> +memory in
> + * which the offset fits
> + * @map: the erase map of the SPI NOR
> + * @addr: offset in the serial flash memory
> + *
> + * Returns pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
> + * otherwise.
> + */
> +static struct spi_nor_erase_region *
> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64
> +addr) {
> + struct spi_nor_erase_region *region = map->regions;
> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> + u64 region_end = region_start + region->size;
> +
> + while (addr < region_start || addr >= region_end) {
> + region = spi_nor_region_next(region);
> + if (!region)
> + return ERR_PTR(-EINVAL);
> +
> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> + region_end = region_start + region->size;
> + }
> +
> + return region;
> +}
> +
> +/*
> + * spi_nor_init_erase_cmd() - initialize an erase command
> + * @region: pointer to a structure that describes a SPI NOR erase region
> + * @erase: pointer to a structure that describes a SPI NOR erase type
> + *
> + * Returns the pointer to the allocated erase command, ERR_PTR(-errno)
> + * otherwise.
> + */
> +static struct spi_nor_erase_command *
> +spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
> + const struct spi_nor_erase_type *erase) {
> + struct spi_nor_erase_command *cmd;
> +
> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
> + if (!cmd)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&cmd->list);
> + cmd->opcode = erase->opcode;
> + cmd->count = 1;
> +
> + if (region->offset & SNOR_OVERLAID_REGION)
> + cmd->size = region->size;
> + else
> + cmd->size = erase->size;
> +
> + return cmd;
> +}
> +
> +/*
> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
> + * @erase_list: list of erase commands
> + */
> +static void spi_nor_destroy_erase_cmd_list(struct list_head
> +*erase_list) {
> + struct spi_nor_erase_command *cmd, *next;
> +
> + list_for_each_entry_safe(cmd, next, erase_list, list) {
> + list_del(&cmd->list);
> + kfree(cmd);
> + }
> +}
> +
> +/*
> + * spi_nor_init_erase_cmd_list() - initialize erase command list
> + * @nor: pointer to a 'struct spi_nor'
> + * @erase_list: list of erase commands to be executed once we validate that
> the
> + * erase can be performed
> + * @addr: offset in the serial flash memory
> + * @len: number of bytes to erase
> + *
> + * Builds the list of best fitted erase commands and verifies if the
> +erase can
> + * be performed.
> + *
> + * Returns 0 on success, -errno otherwise.
> + */
> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
> + struct list_head *erase_list,
> + u64 addr, u32 len)
> +{
> + const struct spi_nor_erase_map *map = &nor->erase_map;
> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
> + struct spi_nor_erase_region *region;
> + struct spi_nor_erase_command *cmd = NULL;
> + u64 region_end;
> + int ret = -EINVAL;
> +
> + region = spi_nor_find_erase_region(map, addr);
> + if (IS_ERR(region))
> + return PTR_ERR(region);
> +
> + region_end = spi_nor_region_end(region);
> +
> + while (len) {
> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
> + if (!erase)
> + goto destroy_erase_cmd_list;
> +
> + if (prev_erase != erase ||
> + region->offset & SNOR_OVERLAID_REGION) {
> + cmd = spi_nor_init_erase_cmd(region, erase);
> + if (IS_ERR(cmd)) {
> + ret = PTR_ERR(cmd);
> + goto destroy_erase_cmd_list;
> + }
> +
> + list_add_tail(&cmd->list, erase_list);
> + } else {
> + cmd->count++;
> + }
> +
> + addr += cmd->size;
> + len -= cmd->size;
> +
> + if (len && addr >= region_end) {
> + region = spi_nor_region_next(region);
> + if (!region)
> + goto destroy_erase_cmd_list;
> + region_end = spi_nor_region_end(region);
> + }
> +
> + prev_erase = erase;
> + }
> +
> + return 0;
> +
> +destroy_erase_cmd_list:
> + spi_nor_destroy_erase_cmd_list(erase_list);
> + return ret;
> +}
> +
> +/*
> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
> + * @nor: pointer to a 'struct spi_nor'
> + * @addr: offset in the serial flash memory
> + * @len: number of bytes to erase
> + *
> + * Build a list of best fitted erase commands and execute it once we
> + * validate that the erase can be performed.
> + *
> + * Returns 0 on success, -errno otherwise.
> + */
> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr,
> +u32 len) {
> + LIST_HEAD(erase_list);
> + struct spi_nor_erase_command *cmd, *next;
> + int ret;
> +
> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
> + if (ret)
> + return ret;
> +
> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
> + nor->erase_opcode = cmd->opcode;
> + while (cmd->count) {
> + write_enable(nor);
> +
> + ret = spi_nor_erase_sector(nor, addr);
> + if (ret)
> + goto destroy_erase_cmd_list;
> +
> + addr += cmd->size;
> + cmd->count--;
> +
> + ret = spi_nor_wait_till_ready(nor);
> + if (ret)
> + goto destroy_erase_cmd_list;
> + }
> + list_del(&cmd->list);
> + kfree(cmd);
> + }
> +
> + return 0;
> +
> +destroy_erase_cmd_list:
> + spi_nor_destroy_erase_cmd_list(&erase_list);
> + return ret;
> +}
> +
> +/*
> * Erase an address range on the nor chip. The address range may extend
> * one or more erase sectors. Return an error is there is a problem erasing.
> */
> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
> erase_info *instr)
> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
> (long long)instr->len);
>
> - div_u64_rem(instr->len, mtd->erasesize, &rem);
> - if (rem)
> - return -EINVAL;
> + if (spi_nor_has_uniform_erase(nor)) {
> + div_u64_rem(instr->len, mtd->erasesize, &rem);
> + if (rem)
> + return -EINVAL;
> + }
>
> addr = instr->addr;
> len = instr->len;
> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
> erase_info *instr)
> */
>
> /* "sector"-at-a-time erase */
> - } else {
> + } else if (spi_nor_has_uniform_erase(nor)) {
> while (len) {
> write_enable(nor);
>
> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
> erase_info *instr)
> if (ret)
> goto erase_err;
> }
> +
> + /* erase multiple sectors */
> + } else {
> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
> + if (ret)
> + goto erase_err;
> }
>
> write_disable(nor);
> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
> sfdp_bfpt_erases[] = {
>
> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
>
> +/*
> + * spi_nor_set_erase_type() - set a SPI NOR erase type
> + * @erase: pointer to a structure that describes a SPI NOR erase type
> + * @size: the size of the sector/block erased by the erase type
> + * @opcode: the SPI command op code to erase the sector/block
> + */
> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
> + u32 size, u8 opcode)
> +{
> + erase->size = size;
> + erase->opcode = opcode;
> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> + erase->size_shift = ffs(erase->size) - 1;
> + erase->size_mask = (1 << erase->size_shift) - 1; }
> +
> +/*
> + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
> + * @erase: pointer to a structure that describes a SPI NOR erase type
> + * @size: the size of the sector/block erased by the erase type
> + * @opcode: the SPI command op code to erase the sector/block
> + * @i: erase type index as sorted in the Basic Flash Parameter Table
> + *
> + * The supported Erase Types will be sorted at init in ascending order,
> +with
> + * the smallest Erase Type size being the first member in the
> +erase_type array
> + * of the spi_nor_erase_map structure. Save the Erase Type index as
> +sorted in
> + * the Basic Flash Parameter Table since it will be used later on to
> + * synchronize with the supported Erase Types defined in SFDP optional tables.
> + */
> +static void
> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
> + u32 size, u8 opcode, u8 i)
> +{
> + erase->idx = i;
> + spi_nor_set_erase_type(erase, size, opcode); }
> +
> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
> + * @l: member in the left half of the map's erase_type array
> + * @r: member in the right half of the map's erase_type array
> + *
> + * Comparison function used in the sort() call to sort in ascending
> +order the
> + * map's erase types, the smallest erase type size being the first
> +member in the
> + * sorted erase_type array.
> + */
> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
> + const struct spi_nor_erase_type *left = l, *right = r;
> +
> + return left->size - right->size;
> +}
> +
> +/*
> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
> + * @map: the erase map of the SPI NOR
> + *
> + * Function assumes that the erase types defined in the erase map are
> +already
> + * sorted in ascending order, with the smallest erase type size being
> +the first
> + * member in the erase_type array. It replicates the sort done for the
> +map's
> + * erase types. Each region's erase bitmask will indicate which erase
> +types are
> + * supported from the sorted erase types defined in the erase map.
> + * Sort the all region's erase type at init in order to speed up the
> +process of
> + * finding the best erase command at runtime.
> + */
> +static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map
> +*map) {
> + struct spi_nor_erase_region *region = map->regions;
> + struct spi_nor_erase_type *erase_type = map->erase_type;
> + int i;
> + u8 region_erase_mask, sorted_erase_mask;
> +
> + while (region) {
> + region_erase_mask = region->offset &
> SNOR_ERASE_TYPE_MASK;
> +
> + /* Replicate the sort done for the map's erase types. */
> + sorted_erase_mask = 0;
> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
> + if (erase_type[i].size &&
> + region_erase_mask & BIT(erase_type[i].idx))
> + sorted_erase_mask |= BIT(i);
> +
> + /* Overwrite erase mask. */
> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
> |
> + sorted_erase_mask;
> +
> + region = spi_nor_region_next(region);
> + }
> +}
> +
> +/*
> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
> + * @map: the erase map of the SPI NOR
> + * @erase_mask: bitmask encoding erase types that can erase
> the entire
> + * flash memory
> + * @flash_size: the spi nor flash memory size
> + */
> +static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
> + u8 erase_mask, u64 flash_size)
> +{
> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
> + map->uniform_region.offset = (erase_mask &
> SNOR_ERASE_TYPE_MASK) |
> + SNOR_LAST_REGION;
> + map->uniform_region.size = flash_size;
> + map->regions = &map->uniform_region;
> + map->uniform_erase_type = erase_mask;
> +}
> +
> /**
> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
> * @nor: pointer to a 'struct spi_nor'
> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
> const struct sfdp_parameter_header *bfpt_header,
> struct spi_nor_flash_parameter *params) {
> - struct mtd_info *mtd = &nor->mtd;
> + struct spi_nor_erase_map *map = &nor->erase_map;
> + struct spi_nor_erase_type *erase_type = map->erase_type;
> struct sfdp_bfpt bfpt;
> size_t len;
> int i, cmd, err;
> u32 addr;
> u16 half;
> + u8 erase_mask;
>
> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
> }
>
> - /* Sector Erase settings. */
> + /*
> + * Sector Erase settings. Reinitialize the uniform erase map using the
> + * Erase Types defined in the bfpt table.
> + */
> + erase_mask = 0;
> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
> u32 erasesize;
> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>
> erasesize = 1U << erasesize;
> opcode = (half >> 8) & 0xff;
> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> - if (erasesize == SZ_4K) {
> - nor->erase_opcode = opcode;
> - mtd->erasesize = erasesize;
> - break;
> - }
> -#endif
> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
> - nor->erase_opcode = opcode;
> - mtd->erasesize = erasesize;
> - }
> + erase_mask |= BIT(i);
> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
> erasesize,
> + opcode, i);
> }
> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> + /*
> + * Sort all the map's Erase Types in ascending order with the smallest
> + * erase size being the first member in the erase_type array.
> + */
> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
> + spi_nor_map_cmp_erase_type, NULL);
> + /*
> + * Sort the erase types in the uniform region in order to update the
> + * uniform_erase_type bitmask. The bitmask will be used later on when
> + * selecting the uniform erase.
> + */
> + spi_nor_regions_sort_erase_types(map);
> + map->uniform_erase_type = map->uniform_region.offset &
> + SNOR_ERASE_TYPE_MASK;
>
> /* Stop here if not JESD216 rev A or later. */
> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
> @@ static int spi_nor_init_params(struct spi_nor *nor,
> const struct flash_info *info,
> struct spi_nor_flash_parameter *params) {
> + struct spi_nor_erase_map *map = &nor->erase_map;
> + u8 i, erase_mask;
> +
> /* Set legacy flash parameters as default. */
> memset(params, 0, sizeof(*params));
>
> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
>
> + /*
> + * Sector Erase settings. Sort Erase Types in ascending order, with the
> + * smallest erase size starting at BIT(0).
> + */
> + erase_mask = 0;
> + i = 0;
> + if (info->flags & SECT_4K_PMC) {
> + erase_mask |= BIT(i);
> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> + SPINOR_OP_BE_4K_PMC);
> + i++;
> + } else if (info->flags & SECT_4K) {
> + erase_mask |= BIT(i);
> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> + SPINOR_OP_BE_4K);
> + i++;
> + }
> + erase_mask |= BIT(i);
> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
> + SPINOR_OP_SE);
> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> +
> /* Select the procedure to set the Quad Enable bit. */
> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> SNOR_HWCAPS_PP_QUAD)) {
> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor *nor,
> params->quad_enable = info->quad_enable;
> }
>
> - /* Override the parameters with data read from SFDP tables. */
> - nor->addr_width = 0;
> - nor->mtd.erasesize = 0;
> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
> !(info->flags & SPI_NOR_SKIP_SFDP)) {
> struct spi_nor_flash_parameter sfdp_params;
> + struct spi_nor_erase_map prev_map;
>
> memcpy(&sfdp_params, params, sizeof(sfdp_params));
> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
> - nor->addr_width = 0;
> - nor->mtd.erasesize = 0;
> - } else {
> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
> +
> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
> + /* restore previous erase map */
> + memcpy(&nor->erase_map, &prev_map,
> + sizeof(nor->erase_map));
> + else
> memcpy(params, &sfdp_params, sizeof(*params));
> - }
> }
>
> return 0;
> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor *nor,
> return 0;
> }
>
> -static int spi_nor_select_erase(struct spi_nor *nor,
> - const struct flash_info *info)
> +/*
> + * 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.
> + *
> + * Once the optimum uniform sector erase command is found, disable all
> +the
> + * other.
> + *
> + * Return: pointer to erase type on success, NULL otherwise.
> + */
> +static const struct spi_nor_erase_type *
> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
> + const u32 wanted_size)
> {
> - struct mtd_info *mtd = &nor->mtd;
> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
> + int i;
> + u8 uniform_erase_type = map->uniform_erase_type;
>
> - /* Do nothing if already configured from SFDP. */
> - if (mtd->erasesize)
> - return 0;
> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> + if (!(uniform_erase_type & BIT(i)))
> + continue;
> +
> + tested_erase = &map->erase_type[i];
> +
> + /*
> + * If the current erase size is the one, stop here:
> + * we have found the right uniform Sector Erase command.
> + */
> + if (tested_erase->size == wanted_size) {
> + erase = tested_erase;
> + break;
> + }
>
> + /*
> + * Otherwise, the current erase size is still a valid canditate.
> + * Select the biggest valid candidate.
> + */
> + if (!erase && tested_erase->size)
> + erase = tested_erase;
> + /* keep iterating to find the wanted_size */
> + }
> +
> + if (!erase)
> + return NULL;
> +
> + /* Disable all other Sector Erase commands. */
> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
> + map->uniform_erase_type |= BIT(erase - map->erase_type);
> + return erase;
> +}
> +
> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
> + struct spi_nor_erase_map *map = &nor->erase_map;
> + const struct spi_nor_erase_type *erase = NULL;
> + struct mtd_info *mtd = &nor->mtd;
> + int i;
> +
> + /*
> + * The previous implementation handling Sector Erase commands
> assumed
> + * that the SPI flash memory has an uniform layout then used only one
> + * of the supported erase sizes for all Sector Erase commands.
> + * So to be backward compatible, the new implementation also tries to
> + * manage the SPI flash memory as uniform with a single erase sector
> + * size, when possible.
> + */
> #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
> + wanted_size = 4096u;
> #endif
> - {
> - nor->erase_opcode = SPINOR_OP_SE;
> - mtd->erasesize = info->sector_size;
> +
> + if (spi_nor_has_uniform_erase(nor)) {
> + erase = spi_nor_select_uniform_erase(map, wanted_size);
> + if (!erase)
> + return -EINVAL;
> + nor->erase_opcode = erase->opcode;
> + mtd->erasesize = erase->size;
> + return 0;
> }
> +
> + /*
> + * For non-uniform SPI flash memory, set mtd->erasesize to the
> + * maximum erase sector size. No need to set nor->erase_opcode.
> + */
> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> + if (map->erase_type[i].size) {
> + erase = &map->erase_type[i];
> + break;
> + }
> + }
> +
> + if (!erase)
> + return -EINVAL;
> +
> + mtd->erasesize = erase->size;
> return 0;
> }
>
> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor, const
> struct flash_info *info,
> }
>
> /* Select the Sector Erase command. */
> - err = spi_nor_select_erase(nor, info);
> + err = spi_nor_select_erase(nor, info->sector_size);
> if (err) {
> dev_err(nor->dev,
> "can't select erase settings supported by both the SPI
> controller and memory.\n"); diff --git a/include/linux/mtd/spi-nor.h
> b/include/linux/mtd/spi-nor.h index 09a10fd..a873a0b 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
>
> /**
> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
> + * @size: the size of the sector/block erased by the erase type.
> + * JEDEC JESD216B imposes erase sizes to be a power of 2.
> + * @size_shift: @size is a power of 2, the shift is stored in
> + * @size_shift.
> + * @size_mask: the size mask based on @size_shift.
> + * @opcode: the SPI command op code to erase the sector/block.
> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
> + * Table. It will be used to synchronize the supported
> + * Erase Types with the ones identified in the SFDP
> + * optional tables.
> + */
> +struct spi_nor_erase_type {
> + u32 size;
> + u32 size_shift;
> + u32 size_mask;
> + u8 opcode;
> + u8 idx;
> +};
> +
> +/**
> + * struct spi_nor_erase_command - Used for non-uniform erases
> + * The structure is used to describe a list of erase commands to be
> +executed
> + * once we validate that the erase can be performed. The elements in
> +the list
> + * are run-length encoded.
> + * @list: for inclusion into the list of erase commands.
> + * @count: how many times the same erase command should be
> + * consecutively used.
> + * @size: the size of the sector/block erased by the command.
> + * @opcode: the SPI command op code to erase the sector/block.
> + */
> +struct spi_nor_erase_command {
> + struct list_head list;
> + u32 count;
> + u32 size;
> + u8 opcode;
> +};
> +
> +/**
> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
> + * @offset: the offset in the data array of erase region start.
> + * LSB bits are used as a bitmask encoding flags to
> + * determine if this region is overlaid, if this region is
> + * the last in the SPI NOR flash memory and to indicate
> + * all the supported erase commands inside this region.
> + * The erase types are sorted in ascending order with the
> + * smallest Erase Type size being at BIT(0).
> + * @size: the size of the region in bytes.
> + */
> +struct spi_nor_erase_region {
> + u64 offset;
> + u64 size;
> +};
> +
> +#define SNOR_ERASE_TYPE_MAX 4
> +#define SNOR_ERASE_TYPE_MASK
> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
> +
> +#define SNOR_LAST_REGION BIT(4)
> +#define SNOR_OVERLAID_REGION BIT(5)
> +
> +#define SNOR_ERASE_FLAGS_MAX 6
> +#define SNOR_ERASE_FLAGS_MASK
> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
> +
> +/**
> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
> + * @regions: array of erase regions. The regions are consecutive in
> + * address space. Walking through the regions is done
> + * incrementally.
> + * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform
> + * sector size (legacy implementation).
> + * @erase_type: an array of erase types shared by all the regions.
> + * The erase types are sorted in ascending order, with the
> + * smallest Erase Type size being the first member in the
> + * erase_type array.
> + * @uniform_erase_type: bitmask encoding erase types that can erase
> the
> + * entire memory. This member is completed at init by
> + * uniform and non-uniform SPI NOR flash memories if
> they
> + * support at least one erase type that can erase the
> + * entire memory.
> + */
> +struct spi_nor_erase_map {
> + struct spi_nor_erase_region *regions;
> + struct spi_nor_erase_region uniform_region;
> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
> + u8 uniform_erase_type;
> +};
> +
> +/**
> * struct flash_info - Forward declaration of a structure used internally by
> * spi_nor_scan()
> */
> @@ -263,6 +351,7 @@ struct flash_info;
> * @write_proto: the SPI protocol for write operations
> * @reg_proto the SPI protocol for read_reg/write_reg/erase
> operations
> * @cmd_buf: used by the write_reg
> + * @erase_map: the erase map of the SPI NOR
> * @prepare: [OPTIONAL] do some preparations for the
> * read/write/erase/lock/unlock operations
> * @unprepare: [OPTIONAL] do some post work after the
> @@ -298,6 +387,7 @@ struct spi_nor {
> bool sst_write_second;
> u32 flags;
> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> + struct spi_nor_erase_map erase_map;
>
> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@ -
> 318,6 +408,23 @@ struct spi_nor {
> void *priv;
> };
>
> +static u64 __maybe_unused
> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
> + return region->offset & SNOR_LAST_REGION; }
> +
> +static u64 __maybe_unused
> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; }
> +
> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
> +spi_nor *nor) {
> + return !!nor->erase_map.uniform_erase_type;
> +}
> +
> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
> struct device_node *np)
> {
> --
> 2.9.4
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.infr
> adead.org%2Fmailman%2Flistinfo%2Flinux-
> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
> 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
> 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0

2018-10-16 12:05:52

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 16 Oct 2018 09:51:47 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi Tudor,
>
> This patch is breaking the 1-4-4 Read protocol for the spansion flash "s25fl512s".
>
> Without this patch read request command for Quad mode, 4-byte enable, is coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> But after applying this patch, read request command for Quad mode is coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
>
> This flash also supports non-uniform erase.
> Can you please check and provide some suggestion?

Are you sure the regression comes from this patch? I suspect your bug
comes from 41fe242979e4 ("mtd: spi-nor: fsl-quadspi: fix read error for
flash size larger than 16MB").

2018-10-16 12:18:47

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 16 Oct 2018 14:04:11 +0200
Boris Brezillon <[email protected]> wrote:

> On Tue, 16 Oct 2018 09:51:47 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi Tudor,
> >
> > This patch is breaking the 1-4-4 Read protocol for the spansion flash "s25fl512s".
> >
> > Without this patch read request command for Quad mode, 4-byte enable, is coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> > But after applying this patch, read request command for Quad mode is coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
> >
> > This flash also supports non-uniform erase.
> > Can you please check and provide some suggestion?
>
> Are you sure the regression comes from this patch? I suspect your bug
> comes from 41fe242979e4 ("mtd: spi-nor: fsl-quadspi: fix read error for
> flash size larger than 16MB").

I guess you're testing with an fsl-qspi controller, right? Can you try
with this patch?

--->8---

diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 1ff3430f82c8..c47fe70c9f98 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -477,9 +477,6 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{
switch (cmd) {
- case SPINOR_OP_READ_1_1_4:
- case SPINOR_OP_READ_1_1_4_4B:
- return SEQID_READ;
case SPINOR_OP_WREN:
return SEQID_WREN;
case SPINOR_OP_WRDI:
@@ -490,8 +487,6 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
return SEQID_SE;
case SPINOR_OP_CHIP_ERASE:
return SEQID_CHIP_ERASE;
- case SPINOR_OP_PP:
- return SEQID_PP;
case SPINOR_OP_RDID:
return SEQID_RDID;
case SPINOR_OP_WRSR:
@@ -503,7 +498,11 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
case SPINOR_OP_BRWR:
return SEQID_BRWR;
default:
- if (cmd == q->nor[0].erase_opcode)
+ if (cmd == q->nor[0].read_opcode)
+ return SEQID_READ;
+ else if (cmd == q->nor[0].program_opcode)
+ return SEQID_PP;
+ else if (cmd == q->nor[0].erase_opcode)
return SEQID_SE;
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break;

2018-10-16 15:15:52

by Tudor Ambarus

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi, Yogesh,

On 10/16/2018 12:51 PM, Yogesh Narayan Gaur wrote:
> Hi Tudor,
>
> This patch is breaking the 1-4-4 Read protocol for the spansion flash "s25fl512s".
>
> Without this patch read request command for Quad mode, 4-byte enable, is coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> But after applying this patch, read request command for Quad mode is coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
>
> This flash also supports non-uniform erase.
> Can you please check and provide some suggestion?

I don't have this memory to test it, but I'll try to help.

Does s25fl512s support non-uniform erase? I'm looking in datasheet[1] at JEDEC
BFPT table, dwords 8 and 9, page 132/146 and it looks like it supports just
256KB uniform erase.

Thanks,
ta

[1] http://www.cypress.com/file/177971/download

>
> --
> Regards
> Yogesh Gaur
>
>> -----Original Message-----
>> From: linux-mtd [mailto:[email protected]] On Behalf Of
>> Tudor Ambarus
>> Sent: Tuesday, September 11, 2018 9:10 PM
>> To: [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected]
>> Cc: Tudor Ambarus <[email protected]>; linux-
>> [email protected]; [email protected];
>> [email protected]; [email protected]; linux-arm-
>> [email protected]; [email protected]
>> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR
>> flash memories
>>
>> Based on Cyrille Pitchen's patch
>> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flkml.or
>> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
>> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
>> c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
>> 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
>>
>> This patch is a transitional patch in introducing the support of SFDP SPI
>> memories with non-uniform erase sizes like Spansion s25fs512s.
>> Non-uniform erase maps will be used later when initialized based on the SFDP
>> data.
>>
>> Introduce the memory erase map which splits the memory array into one or
>> many erase regions. Each erase region supports up to 4 erase types, as defined
>> by the JEDEC JESD216B (SFDP) specification.
>>
>> To be backward compatible, the erase map of uniform SPI NOR flash memories
>> is initialized so it contains only one erase region and this erase region supports
>> only one erase command. Hence a single size is used to erase any sector/block
>> of the memory.
>>
>> Besides, since the algorithm used to erase sectors on non-uniform SPI NOR flash
>> memories is quite expensive, when possible, the erase map is tuned to come
>> back to the uniform case.
>>
>> The 'erase with the best command, move forward and repeat' approach was
>> suggested by Cristian Birsan in a brainstorm session, so:
>>
>> Suggested-by: Cristian Birsan <[email protected]>
>> Signed-off-by: Tudor Ambarus <[email protected]>
>> ---
>> drivers/mtd/spi-nor/spi-nor.c | 594
>> +++++++++++++++++++++++++++++++++++++++---
>> include/linux/mtd/spi-nor.h | 107 ++++++++
>> 2 files changed, 659 insertions(+), 42 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index
>> dc8757e..4687345 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -18,6 +18,7 @@
>> #include <linux/math64.h>
>> #include <linux/sizes.h>
>> #include <linux/slab.h>
>> +#include <linux/sort.h>
>>
>> #include <linux/mtd/mtd.h>
>> #include <linux/of_platform.h>
>> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor
>> *nor,
>> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
>> nor->program_opcode = spi_nor_convert_3to4_program(nor-
>>> program_opcode);
>> nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
>> +
>> + if (!spi_nor_has_uniform_erase(nor)) {
>> + struct spi_nor_erase_map *map = &nor->erase_map;
>> + struct spi_nor_erase_type *erase;
>> + int i;
>> +
>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
>> + erase = &map->erase_type[i];
>> + erase->opcode =
>> + spi_nor_convert_3to4_erase(erase->opcode);
>> + }
>> + }
>> }
>>
>> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275 @@ static
>> int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) }
>>
>> /*
>> + * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>> + * @dividend: dividend value
>> + * @remainder: pointer to u32 remainder (will be updated)
>> + *
>> + * Returns two values: remainder and the new dividend */ static u64
>> +spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
>> + u64 dividend, u32 *remainder)
>> +{
>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>> + *remainder = (u32)dividend & erase->size_mask;
>> + return dividend >> erase->size_shift;
>> +}
>> +
>> +/*
>> + * spi_nor_find_best_erase_type() - find the best erase type for the
>> +given
>> + * offset in the serial flash memory and the number of bytes to erase.
>> +The
>> + * region in which the address fits is expected to be provided.
>> + * @map: the erase map of the SPI NOR
>> + * @region: pointer to a structure that describes a SPI NOR erase region
>> + * @addr: offset in the serial flash memory
>> + * @len: number of bytes to erase
>> + *
>> + * Returns a pointer to the best fitted erase type, NULL otherwise.
>> + */
>> +static const struct spi_nor_erase_type *
>> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
>> + const struct spi_nor_erase_region *region,
>> + u64 addr, u32 len)
>> +{
>> + const struct spi_nor_erase_type *erase;
>> + u32 rem;
>> + int i;
>> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
>> +
>> + /*
>> + * Erase types are ordered by size, with the biggest erase type at
>> + * index 0.
>> + */
>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>> + /* Does the erase region support the tested erase type? */
>> + if (!(erase_mask & BIT(i)))
>> + continue;
>> +
>> + erase = &map->erase_type[i];
>> +
>> + /* Don't erase more than what the user has asked for. */
>> + if (erase->size > len)
>> + continue;
>> +
>> + /* Alignment is not mandatory for overlaid regions */
>> + if (region->offset & SNOR_OVERLAID_REGION)
>> + return erase;
>> +
>> + spi_nor_div_by_erase_size(erase, addr, &rem);
>> + if (rem)
>> + continue;
>> + else
>> + return erase;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/*
>> + * spi_nor_region_next() - get the next spi nor region
>> + * @region: pointer to a structure that describes a SPI NOR erase region
>> + *
>> + * Returns the next spi nor region or NULL if last region.
>> + */
>> +static struct spi_nor_erase_region *
>> +spi_nor_region_next(struct spi_nor_erase_region *region) {
>> + if (spi_nor_region_is_last(region))
>> + return NULL;
>> + region++;
>> + return region;
>> +}
>> +
>> +/*
>> + * spi_nor_find_erase_region() - find the region of the serial flash
>> +memory in
>> + * which the offset fits
>> + * @map: the erase map of the SPI NOR
>> + * @addr: offset in the serial flash memory
>> + *
>> + * Returns pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
>> + * otherwise.
>> + */
>> +static struct spi_nor_erase_region *
>> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64
>> +addr) {
>> + struct spi_nor_erase_region *region = map->regions;
>> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>> + u64 region_end = region_start + region->size;
>> +
>> + while (addr < region_start || addr >= region_end) {
>> + region = spi_nor_region_next(region);
>> + if (!region)
>> + return ERR_PTR(-EINVAL);
>> +
>> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>> + region_end = region_start + region->size;
>> + }
>> +
>> + return region;
>> +}
>> +
>> +/*
>> + * spi_nor_init_erase_cmd() - initialize an erase command
>> + * @region: pointer to a structure that describes a SPI NOR erase region
>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>> + *
>> + * Returns the pointer to the allocated erase command, ERR_PTR(-errno)
>> + * otherwise.
>> + */
>> +static struct spi_nor_erase_command *
>> +spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
>> + const struct spi_nor_erase_type *erase) {
>> + struct spi_nor_erase_command *cmd;
>> +
>> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
>> + if (!cmd)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + INIT_LIST_HEAD(&cmd->list);
>> + cmd->opcode = erase->opcode;
>> + cmd->count = 1;
>> +
>> + if (region->offset & SNOR_OVERLAID_REGION)
>> + cmd->size = region->size;
>> + else
>> + cmd->size = erase->size;
>> +
>> + return cmd;
>> +}
>> +
>> +/*
>> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
>> + * @erase_list: list of erase commands
>> + */
>> +static void spi_nor_destroy_erase_cmd_list(struct list_head
>> +*erase_list) {
>> + struct spi_nor_erase_command *cmd, *next;
>> +
>> + list_for_each_entry_safe(cmd, next, erase_list, list) {
>> + list_del(&cmd->list);
>> + kfree(cmd);
>> + }
>> +}
>> +
>> +/*
>> + * spi_nor_init_erase_cmd_list() - initialize erase command list
>> + * @nor: pointer to a 'struct spi_nor'
>> + * @erase_list: list of erase commands to be executed once we validate that
>> the
>> + * erase can be performed
>> + * @addr: offset in the serial flash memory
>> + * @len: number of bytes to erase
>> + *
>> + * Builds the list of best fitted erase commands and verifies if the
>> +erase can
>> + * be performed.
>> + *
>> + * Returns 0 on success, -errno otherwise.
>> + */
>> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
>> + struct list_head *erase_list,
>> + u64 addr, u32 len)
>> +{
>> + const struct spi_nor_erase_map *map = &nor->erase_map;
>> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
>> + struct spi_nor_erase_region *region;
>> + struct spi_nor_erase_command *cmd = NULL;
>> + u64 region_end;
>> + int ret = -EINVAL;
>> +
>> + region = spi_nor_find_erase_region(map, addr);
>> + if (IS_ERR(region))
>> + return PTR_ERR(region);
>> +
>> + region_end = spi_nor_region_end(region);
>> +
>> + while (len) {
>> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
>> + if (!erase)
>> + goto destroy_erase_cmd_list;
>> +
>> + if (prev_erase != erase ||
>> + region->offset & SNOR_OVERLAID_REGION) {
>> + cmd = spi_nor_init_erase_cmd(region, erase);
>> + if (IS_ERR(cmd)) {
>> + ret = PTR_ERR(cmd);
>> + goto destroy_erase_cmd_list;
>> + }
>> +
>> + list_add_tail(&cmd->list, erase_list);
>> + } else {
>> + cmd->count++;
>> + }
>> +
>> + addr += cmd->size;
>> + len -= cmd->size;
>> +
>> + if (len && addr >= region_end) {
>> + region = spi_nor_region_next(region);
>> + if (!region)
>> + goto destroy_erase_cmd_list;
>> + region_end = spi_nor_region_end(region);
>> + }
>> +
>> + prev_erase = erase;
>> + }
>> +
>> + return 0;
>> +
>> +destroy_erase_cmd_list:
>> + spi_nor_destroy_erase_cmd_list(erase_list);
>> + return ret;
>> +}
>> +
>> +/*
>> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
>> + * @nor: pointer to a 'struct spi_nor'
>> + * @addr: offset in the serial flash memory
>> + * @len: number of bytes to erase
>> + *
>> + * Build a list of best fitted erase commands and execute it once we
>> + * validate that the erase can be performed.
>> + *
>> + * Returns 0 on success, -errno otherwise.
>> + */
>> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr,
>> +u32 len) {
>> + LIST_HEAD(erase_list);
>> + struct spi_nor_erase_command *cmd, *next;
>> + int ret;
>> +
>> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
>> + if (ret)
>> + return ret;
>> +
>> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
>> + nor->erase_opcode = cmd->opcode;
>> + while (cmd->count) {
>> + write_enable(nor);
>> +
>> + ret = spi_nor_erase_sector(nor, addr);
>> + if (ret)
>> + goto destroy_erase_cmd_list;
>> +
>> + addr += cmd->size;
>> + cmd->count--;
>> +
>> + ret = spi_nor_wait_till_ready(nor);
>> + if (ret)
>> + goto destroy_erase_cmd_list;
>> + }
>> + list_del(&cmd->list);
>> + kfree(cmd);
>> + }
>> +
>> + return 0;
>> +
>> +destroy_erase_cmd_list:
>> + spi_nor_destroy_erase_cmd_list(&erase_list);
>> + return ret;
>> +}
>> +
>> +/*
>> * Erase an address range on the nor chip. The address range may extend
>> * one or more erase sectors. Return an error is there is a problem erasing.
>> */
>> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>> erase_info *instr)
>> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>> (long long)instr->len);
>>
>> - div_u64_rem(instr->len, mtd->erasesize, &rem);
>> - if (rem)
>> - return -EINVAL;
>> + if (spi_nor_has_uniform_erase(nor)) {
>> + div_u64_rem(instr->len, mtd->erasesize, &rem);
>> + if (rem)
>> + return -EINVAL;
>> + }
>>
>> addr = instr->addr;
>> len = instr->len;
>> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>> erase_info *instr)
>> */
>>
>> /* "sector"-at-a-time erase */
>> - } else {
>> + } else if (spi_nor_has_uniform_erase(nor)) {
>> while (len) {
>> write_enable(nor);
>>
>> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>> erase_info *instr)
>> if (ret)
>> goto erase_err;
>> }
>> +
>> + /* erase multiple sectors */
>> + } else {
>> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
>> + if (ret)
>> + goto erase_err;
>> }
>>
>> write_disable(nor);
>> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
>> sfdp_bfpt_erases[] = {
>>
>> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
>>
>> +/*
>> + * spi_nor_set_erase_type() - set a SPI NOR erase type
>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>> + * @size: the size of the sector/block erased by the erase type
>> + * @opcode: the SPI command op code to erase the sector/block
>> + */
>> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
>> + u32 size, u8 opcode)
>> +{
>> + erase->size = size;
>> + erase->opcode = opcode;
>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>> + erase->size_shift = ffs(erase->size) - 1;
>> + erase->size_mask = (1 << erase->size_shift) - 1; }
>> +
>> +/*
>> + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>> + * @size: the size of the sector/block erased by the erase type
>> + * @opcode: the SPI command op code to erase the sector/block
>> + * @i: erase type index as sorted in the Basic Flash Parameter Table
>> + *
>> + * The supported Erase Types will be sorted at init in ascending order,
>> +with
>> + * the smallest Erase Type size being the first member in the
>> +erase_type array
>> + * of the spi_nor_erase_map structure. Save the Erase Type index as
>> +sorted in
>> + * the Basic Flash Parameter Table since it will be used later on to
>> + * synchronize with the supported Erase Types defined in SFDP optional tables.
>> + */
>> +static void
>> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
>> + u32 size, u8 opcode, u8 i)
>> +{
>> + erase->idx = i;
>> + spi_nor_set_erase_type(erase, size, opcode); }
>> +
>> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
>> + * @l: member in the left half of the map's erase_type array
>> + * @r: member in the right half of the map's erase_type array
>> + *
>> + * Comparison function used in the sort() call to sort in ascending
>> +order the
>> + * map's erase types, the smallest erase type size being the first
>> +member in the
>> + * sorted erase_type array.
>> + */
>> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
>> + const struct spi_nor_erase_type *left = l, *right = r;
>> +
>> + return left->size - right->size;
>> +}
>> +
>> +/*
>> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
>> + * @map: the erase map of the SPI NOR
>> + *
>> + * Function assumes that the erase types defined in the erase map are
>> +already
>> + * sorted in ascending order, with the smallest erase type size being
>> +the first
>> + * member in the erase_type array. It replicates the sort done for the
>> +map's
>> + * erase types. Each region's erase bitmask will indicate which erase
>> +types are
>> + * supported from the sorted erase types defined in the erase map.
>> + * Sort the all region's erase type at init in order to speed up the
>> +process of
>> + * finding the best erase command at runtime.
>> + */
>> +static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map
>> +*map) {
>> + struct spi_nor_erase_region *region = map->regions;
>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>> + int i;
>> + u8 region_erase_mask, sorted_erase_mask;
>> +
>> + while (region) {
>> + region_erase_mask = region->offset &
>> SNOR_ERASE_TYPE_MASK;
>> +
>> + /* Replicate the sort done for the map's erase types. */
>> + sorted_erase_mask = 0;
>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
>> + if (erase_type[i].size &&
>> + region_erase_mask & BIT(erase_type[i].idx))
>> + sorted_erase_mask |= BIT(i);
>> +
>> + /* Overwrite erase mask. */
>> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
>> |
>> + sorted_erase_mask;
>> +
>> + region = spi_nor_region_next(region);
>> + }
>> +}
>> +
>> +/*
>> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
>> + * @map: the erase map of the SPI NOR
>> + * @erase_mask: bitmask encoding erase types that can erase
>> the entire
>> + * flash memory
>> + * @flash_size: the spi nor flash memory size
>> + */
>> +static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
>> + u8 erase_mask, u64 flash_size)
>> +{
>> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
>> + map->uniform_region.offset = (erase_mask &
>> SNOR_ERASE_TYPE_MASK) |
>> + SNOR_LAST_REGION;
>> + map->uniform_region.size = flash_size;
>> + map->regions = &map->uniform_region;
>> + map->uniform_erase_type = erase_mask;
>> +}
>> +
>> /**
>> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
>> * @nor: pointer to a 'struct spi_nor'
>> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>> const struct sfdp_parameter_header *bfpt_header,
>> struct spi_nor_flash_parameter *params) {
>> - struct mtd_info *mtd = &nor->mtd;
>> + struct spi_nor_erase_map *map = &nor->erase_map;
>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>> struct sfdp_bfpt bfpt;
>> size_t len;
>> int i, cmd, err;
>> u32 addr;
>> u16 half;
>> + u8 erase_mask;
>>
>> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
>> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
>> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
>> }
>>
>> - /* Sector Erase settings. */
>> + /*
>> + * Sector Erase settings. Reinitialize the uniform erase map using the
>> + * Erase Types defined in the bfpt table.
>> + */
>> + erase_mask = 0;
>> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
>> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
>> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
>> u32 erasesize;
>> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>
>> erasesize = 1U << erasesize;
>> opcode = (half >> 8) & 0xff;
>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>> - if (erasesize == SZ_4K) {
>> - nor->erase_opcode = opcode;
>> - mtd->erasesize = erasesize;
>> - break;
>> - }
>> -#endif
>> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
>> - nor->erase_opcode = opcode;
>> - mtd->erasesize = erasesize;
>> - }
>> + erase_mask |= BIT(i);
>> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
>> erasesize,
>> + opcode, i);
>> }
>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>> + /*
>> + * Sort all the map's Erase Types in ascending order with the smallest
>> + * erase size being the first member in the erase_type array.
>> + */
>> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
>> + spi_nor_map_cmp_erase_type, NULL);
>> + /*
>> + * Sort the erase types in the uniform region in order to update the
>> + * uniform_erase_type bitmask. The bitmask will be used later on when
>> + * selecting the uniform erase.
>> + */
>> + spi_nor_regions_sort_erase_types(map);
>> + map->uniform_erase_type = map->uniform_region.offset &
>> + SNOR_ERASE_TYPE_MASK;
>>
>> /* Stop here if not JESD216 rev A or later. */
>> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
>> @@ static int spi_nor_init_params(struct spi_nor *nor,
>> const struct flash_info *info,
>> struct spi_nor_flash_parameter *params) {
>> + struct spi_nor_erase_map *map = &nor->erase_map;
>> + u8 i, erase_mask;
>> +
>> /* Set legacy flash parameters as default. */
>> memset(params, 0, sizeof(*params));
>>
>> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
>> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
>> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
>>
>> + /*
>> + * Sector Erase settings. Sort Erase Types in ascending order, with the
>> + * smallest erase size starting at BIT(0).
>> + */
>> + erase_mask = 0;
>> + i = 0;
>> + if (info->flags & SECT_4K_PMC) {
>> + erase_mask |= BIT(i);
>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>> + SPINOR_OP_BE_4K_PMC);
>> + i++;
>> + } else if (info->flags & SECT_4K) {
>> + erase_mask |= BIT(i);
>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>> + SPINOR_OP_BE_4K);
>> + i++;
>> + }
>> + erase_mask |= BIT(i);
>> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
>> + SPINOR_OP_SE);
>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>> +
>> /* Select the procedure to set the Quad Enable bit. */
>> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>> SNOR_HWCAPS_PP_QUAD)) {
>> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor *nor,
>> params->quad_enable = info->quad_enable;
>> }
>>
>> - /* Override the parameters with data read from SFDP tables. */
>> - nor->addr_width = 0;
>> - nor->mtd.erasesize = 0;
>> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
>> !(info->flags & SPI_NOR_SKIP_SFDP)) {
>> struct spi_nor_flash_parameter sfdp_params;
>> + struct spi_nor_erase_map prev_map;
>>
>> memcpy(&sfdp_params, params, sizeof(sfdp_params));
>> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
>> - nor->addr_width = 0;
>> - nor->mtd.erasesize = 0;
>> - } else {
>> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
>> +
>> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
>> + /* restore previous erase map */
>> + memcpy(&nor->erase_map, &prev_map,
>> + sizeof(nor->erase_map));
>> + else
>> memcpy(params, &sfdp_params, sizeof(*params));
>> - }
>> }
>>
>> return 0;
>> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor *nor,
>> return 0;
>> }
>>
>> -static int spi_nor_select_erase(struct spi_nor *nor,
>> - const struct flash_info *info)
>> +/*
>> + * 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.
>> + *
>> + * Once the optimum uniform sector erase command is found, disable all
>> +the
>> + * other.
>> + *
>> + * Return: pointer to erase type on success, NULL otherwise.
>> + */
>> +static const struct spi_nor_erase_type *
>> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
>> + const u32 wanted_size)
>> {
>> - struct mtd_info *mtd = &nor->mtd;
>> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
>> + int i;
>> + u8 uniform_erase_type = map->uniform_erase_type;
>>
>> - /* Do nothing if already configured from SFDP. */
>> - if (mtd->erasesize)
>> - return 0;
>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>> + if (!(uniform_erase_type & BIT(i)))
>> + continue;
>> +
>> + tested_erase = &map->erase_type[i];
>> +
>> + /*
>> + * If the current erase size is the one, stop here:
>> + * we have found the right uniform Sector Erase command.
>> + */
>> + if (tested_erase->size == wanted_size) {
>> + erase = tested_erase;
>> + break;
>> + }
>>
>> + /*
>> + * Otherwise, the current erase size is still a valid canditate.
>> + * Select the biggest valid candidate.
>> + */
>> + if (!erase && tested_erase->size)
>> + erase = tested_erase;
>> + /* keep iterating to find the wanted_size */
>> + }
>> +
>> + if (!erase)
>> + return NULL;
>> +
>> + /* Disable all other Sector Erase commands. */
>> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
>> + map->uniform_erase_type |= BIT(erase - map->erase_type);
>> + return erase;
>> +}
>> +
>> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
>> + struct spi_nor_erase_map *map = &nor->erase_map;
>> + const struct spi_nor_erase_type *erase = NULL;
>> + struct mtd_info *mtd = &nor->mtd;
>> + int i;
>> +
>> + /*
>> + * The previous implementation handling Sector Erase commands
>> assumed
>> + * that the SPI flash memory has an uniform layout then used only one
>> + * of the supported erase sizes for all Sector Erase commands.
>> + * So to be backward compatible, the new implementation also tries to
>> + * manage the SPI flash memory as uniform with a single erase sector
>> + * size, when possible.
>> + */
>> #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
>> + wanted_size = 4096u;
>> #endif
>> - {
>> - nor->erase_opcode = SPINOR_OP_SE;
>> - mtd->erasesize = info->sector_size;
>> +
>> + if (spi_nor_has_uniform_erase(nor)) {
>> + erase = spi_nor_select_uniform_erase(map, wanted_size);
>> + if (!erase)
>> + return -EINVAL;
>> + nor->erase_opcode = erase->opcode;
>> + mtd->erasesize = erase->size;
>> + return 0;
>> }
>> +
>> + /*
>> + * For non-uniform SPI flash memory, set mtd->erasesize to the
>> + * maximum erase sector size. No need to set nor->erase_opcode.
>> + */
>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>> + if (map->erase_type[i].size) {
>> + erase = &map->erase_type[i];
>> + break;
>> + }
>> + }
>> +
>> + if (!erase)
>> + return -EINVAL;
>> +
>> + mtd->erasesize = erase->size;
>> return 0;
>> }
>>
>> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor, const
>> struct flash_info *info,
>> }
>>
>> /* Select the Sector Erase command. */
>> - err = spi_nor_select_erase(nor, info);
>> + err = spi_nor_select_erase(nor, info->sector_size);
>> if (err) {
>> dev_err(nor->dev,
>> "can't select erase settings supported by both the SPI
>> controller and memory.\n"); diff --git a/include/linux/mtd/spi-nor.h
>> b/include/linux/mtd/spi-nor.h index 09a10fd..a873a0b 100644
>> --- a/include/linux/mtd/spi-nor.h
>> +++ b/include/linux/mtd/spi-nor.h
>> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
>>
>> /**
>> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
>> + * @size: the size of the sector/block erased by the erase type.
>> + * JEDEC JESD216B imposes erase sizes to be a power of 2.
>> + * @size_shift: @size is a power of 2, the shift is stored in
>> + * @size_shift.
>> + * @size_mask: the size mask based on @size_shift.
>> + * @opcode: the SPI command op code to erase the sector/block.
>> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
>> + * Table. It will be used to synchronize the supported
>> + * Erase Types with the ones identified in the SFDP
>> + * optional tables.
>> + */
>> +struct spi_nor_erase_type {
>> + u32 size;
>> + u32 size_shift;
>> + u32 size_mask;
>> + u8 opcode;
>> + u8 idx;
>> +};
>> +
>> +/**
>> + * struct spi_nor_erase_command - Used for non-uniform erases
>> + * The structure is used to describe a list of erase commands to be
>> +executed
>> + * once we validate that the erase can be performed. The elements in
>> +the list
>> + * are run-length encoded.
>> + * @list: for inclusion into the list of erase commands.
>> + * @count: how many times the same erase command should be
>> + * consecutively used.
>> + * @size: the size of the sector/block erased by the command.
>> + * @opcode: the SPI command op code to erase the sector/block.
>> + */
>> +struct spi_nor_erase_command {
>> + struct list_head list;
>> + u32 count;
>> + u32 size;
>> + u8 opcode;
>> +};
>> +
>> +/**
>> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
>> + * @offset: the offset in the data array of erase region start.
>> + * LSB bits are used as a bitmask encoding flags to
>> + * determine if this region is overlaid, if this region is
>> + * the last in the SPI NOR flash memory and to indicate
>> + * all the supported erase commands inside this region.
>> + * The erase types are sorted in ascending order with the
>> + * smallest Erase Type size being at BIT(0).
>> + * @size: the size of the region in bytes.
>> + */
>> +struct spi_nor_erase_region {
>> + u64 offset;
>> + u64 size;
>> +};
>> +
>> +#define SNOR_ERASE_TYPE_MAX 4
>> +#define SNOR_ERASE_TYPE_MASK
>> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
>> +
>> +#define SNOR_LAST_REGION BIT(4)
>> +#define SNOR_OVERLAID_REGION BIT(5)
>> +
>> +#define SNOR_ERASE_FLAGS_MAX 6
>> +#define SNOR_ERASE_FLAGS_MASK
>> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
>> +
>> +/**
>> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
>> + * @regions: array of erase regions. The regions are consecutive in
>> + * address space. Walking through the regions is done
>> + * incrementally.
>> + * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform
>> + * sector size (legacy implementation).
>> + * @erase_type: an array of erase types shared by all the regions.
>> + * The erase types are sorted in ascending order, with the
>> + * smallest Erase Type size being the first member in the
>> + * erase_type array.
>> + * @uniform_erase_type: bitmask encoding erase types that can erase
>> the
>> + * entire memory. This member is completed at init by
>> + * uniform and non-uniform SPI NOR flash memories if
>> they
>> + * support at least one erase type that can erase the
>> + * entire memory.
>> + */
>> +struct spi_nor_erase_map {
>> + struct spi_nor_erase_region *regions;
>> + struct spi_nor_erase_region uniform_region;
>> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
>> + u8 uniform_erase_type;
>> +};
>> +
>> +/**
>> * struct flash_info - Forward declaration of a structure used internally by
>> * spi_nor_scan()
>> */
>> @@ -263,6 +351,7 @@ struct flash_info;
>> * @write_proto: the SPI protocol for write operations
>> * @reg_proto the SPI protocol for read_reg/write_reg/erase
>> operations
>> * @cmd_buf: used by the write_reg
>> + * @erase_map: the erase map of the SPI NOR
>> * @prepare: [OPTIONAL] do some preparations for the
>> * read/write/erase/lock/unlock operations
>> * @unprepare: [OPTIONAL] do some post work after the
>> @@ -298,6 +387,7 @@ struct spi_nor {
>> bool sst_write_second;
>> u32 flags;
>> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>> + struct spi_nor_erase_map erase_map;
>>
>> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
>> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@ -
>> 318,6 +408,23 @@ struct spi_nor {
>> void *priv;
>> };
>>
>> +static u64 __maybe_unused
>> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
>> + return region->offset & SNOR_LAST_REGION; }
>> +
>> +static u64 __maybe_unused
>> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
>> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; }
>> +
>> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
>> +spi_nor *nor) {
>> + return !!nor->erase_map.uniform_erase_type;
>> +}
>> +
>> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
>> struct device_node *np)
>> {
>> --
>> 2.9.4
>>
>>
>> ______________________________________________________
>> Linux MTD discussion mailing list
>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.infr
>> adead.org%2Fmailman%2Flistinfo%2Flinux-
>> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
>> 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
>> 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
>> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0
>

2018-10-16 23:43:06

by Cyrille Pitchen

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Tudor,

Le 16/10/2018 à 17:14, Tudor Ambarus a écrit :
> Hi, Yogesh,
>
> On 10/16/2018 12:51 PM, Yogesh Narayan Gaur wrote:
>> Hi Tudor,
>>
>> This patch is breaking the 1-4-4 Read protocol for the spansion flash "s25fl512s".
>>
>> Without this patch read request command for Quad mode, 4-byte enable, is coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
>> But after applying this patch, read request command for Quad mode is coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
>>
>> This flash also supports non-uniform erase.
>> Can you please check and provide some suggestion?
>
> I don't have this memory to test it, but I'll try to help.
>
> Does s25fl512s support non-uniform erase? I'm looking in datasheet[1] at JEDEC
> BFPT table, dwords 8 and 9, page 132/146 and it looks like it supports just
> 256KB uniform erase.
>

s25fS512s supports both uniform and non uniform erase options but s25fL512s is
always uniform. L is an old memory part, S is newer.

Also, the 8th and 9th WORDs of the Basic Flash Parameter Table alone can't tell
you whether or not the memory part can be non uniform.
If the memory can be non uniform then the sector erase map table is mandatory,
hence when the table is missing you know that your memory part is always uniform.

Best regards,

Cyrille

> Thanks,
> ta
>
> [1] http://www.cypress.com/file/177971/download
>
>>
>> --
>> Regards
>> Yogesh Gaur
>>
>>> -----Original Message-----
>>> From: linux-mtd [mailto:[email protected]] On Behalf Of
>>> Tudor Ambarus
>>> Sent: Tuesday, September 11, 2018 9:10 PM
>>> To: [email protected]; [email protected];
>>> [email protected]; [email protected]; [email protected]
>>> Cc: Tudor Ambarus <[email protected]>; linux-
>>> [email protected]; [email protected];
>>> [email protected]; [email protected]; linux-arm-
>>> [email protected]; [email protected]
>>> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR
>>> flash memories
>>>
>>> Based on Cyrille Pitchen's patch
>>> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flkml.or
>>> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
>>> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
>>> c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
>>> 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
>>>
>>> This patch is a transitional patch in introducing the support of SFDP SPI
>>> memories with non-uniform erase sizes like Spansion s25fs512s.
>>> Non-uniform erase maps will be used later when initialized based on the SFDP
>>> data.
>>>
>>> Introduce the memory erase map which splits the memory array into one or
>>> many erase regions. Each erase region supports up to 4 erase types, as defined
>>> by the JEDEC JESD216B (SFDP) specification.
>>>
>>> To be backward compatible, the erase map of uniform SPI NOR flash memories
>>> is initialized so it contains only one erase region and this erase region supports
>>> only one erase command. Hence a single size is used to erase any sector/block
>>> of the memory.
>>>
>>> Besides, since the algorithm used to erase sectors on non-uniform SPI NOR flash
>>> memories is quite expensive, when possible, the erase map is tuned to come
>>> back to the uniform case.
>>>
>>> The 'erase with the best command, move forward and repeat' approach was
>>> suggested by Cristian Birsan in a brainstorm session, so:
>>>
>>> Suggested-by: Cristian Birsan <[email protected]>
>>> Signed-off-by: Tudor Ambarus <[email protected]>
>>> ---
>>> drivers/mtd/spi-nor/spi-nor.c | 594
>>> +++++++++++++++++++++++++++++++++++++++---
>>> include/linux/mtd/spi-nor.h | 107 ++++++++
>>> 2 files changed, 659 insertions(+), 42 deletions(-)
>>>
>>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index
>>> dc8757e..4687345 100644
>>> --- a/drivers/mtd/spi-nor/spi-nor.c
>>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>>> @@ -18,6 +18,7 @@
>>> #include <linux/math64.h>
>>> #include <linux/sizes.h>
>>> #include <linux/slab.h>
>>> +#include <linux/sort.h>
>>>
>>> #include <linux/mtd/mtd.h>
>>> #include <linux/of_platform.h>
>>> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor
>>> *nor,
>>> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
>>> nor->program_opcode = spi_nor_convert_3to4_program(nor-
>>>> program_opcode);
>>> nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
>>> +
>>> + if (!spi_nor_has_uniform_erase(nor)) {
>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>> + struct spi_nor_erase_type *erase;
>>> + int i;
>>> +
>>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
>>> + erase = &map->erase_type[i];
>>> + erase->opcode =
>>> + spi_nor_convert_3to4_erase(erase->opcode);
>>> + }
>>> + }
>>> }
>>>
>>> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275 @@ static
>>> int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) }
>>>
>>> /*
>>> + * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>> + * @dividend: dividend value
>>> + * @remainder: pointer to u32 remainder (will be updated)
>>> + *
>>> + * Returns two values: remainder and the new dividend */ static u64
>>> +spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
>>> + u64 dividend, u32 *remainder)
>>> +{
>>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>>> + *remainder = (u32)dividend & erase->size_mask;
>>> + return dividend >> erase->size_shift;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_find_best_erase_type() - find the best erase type for the
>>> +given
>>> + * offset in the serial flash memory and the number of bytes to erase.
>>> +The
>>> + * region in which the address fits is expected to be provided.
>>> + * @map: the erase map of the SPI NOR
>>> + * @region: pointer to a structure that describes a SPI NOR erase region
>>> + * @addr: offset in the serial flash memory
>>> + * @len: number of bytes to erase
>>> + *
>>> + * Returns a pointer to the best fitted erase type, NULL otherwise.
>>> + */
>>> +static const struct spi_nor_erase_type *
>>> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
>>> + const struct spi_nor_erase_region *region,
>>> + u64 addr, u32 len)
>>> +{
>>> + const struct spi_nor_erase_type *erase;
>>> + u32 rem;
>>> + int i;
>>> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
>>> +
>>> + /*
>>> + * Erase types are ordered by size, with the biggest erase type at
>>> + * index 0.
>>> + */
>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>> + /* Does the erase region support the tested erase type? */
>>> + if (!(erase_mask & BIT(i)))
>>> + continue;
>>> +
>>> + erase = &map->erase_type[i];
>>> +
>>> + /* Don't erase more than what the user has asked for. */
>>> + if (erase->size > len)
>>> + continue;
>>> +
>>> + /* Alignment is not mandatory for overlaid regions */
>>> + if (region->offset & SNOR_OVERLAID_REGION)
>>> + return erase;
>>> +
>>> + spi_nor_div_by_erase_size(erase, addr, &rem);
>>> + if (rem)
>>> + continue;
>>> + else
>>> + return erase;
>>> + }
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_region_next() - get the next spi nor region
>>> + * @region: pointer to a structure that describes a SPI NOR erase region
>>> + *
>>> + * Returns the next spi nor region or NULL if last region.
>>> + */
>>> +static struct spi_nor_erase_region *
>>> +spi_nor_region_next(struct spi_nor_erase_region *region) {
>>> + if (spi_nor_region_is_last(region))
>>> + return NULL;
>>> + region++;
>>> + return region;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_find_erase_region() - find the region of the serial flash
>>> +memory in
>>> + * which the offset fits
>>> + * @map: the erase map of the SPI NOR
>>> + * @addr: offset in the serial flash memory
>>> + *
>>> + * Returns pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
>>> + * otherwise.
>>> + */
>>> +static struct spi_nor_erase_region *
>>> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64
>>> +addr) {
>>> + struct spi_nor_erase_region *region = map->regions;
>>> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>>> + u64 region_end = region_start + region->size;
>>> +
>>> + while (addr < region_start || addr >= region_end) {
>>> + region = spi_nor_region_next(region);
>>> + if (!region)
>>> + return ERR_PTR(-EINVAL);
>>> +
>>> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>>> + region_end = region_start + region->size;
>>> + }
>>> +
>>> + return region;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_init_erase_cmd() - initialize an erase command
>>> + * @region: pointer to a structure that describes a SPI NOR erase region
>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>> + *
>>> + * Returns the pointer to the allocated erase command, ERR_PTR(-errno)
>>> + * otherwise.
>>> + */
>>> +static struct spi_nor_erase_command *
>>> +spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
>>> + const struct spi_nor_erase_type *erase) {
>>> + struct spi_nor_erase_command *cmd;
>>> +
>>> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
>>> + if (!cmd)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + INIT_LIST_HEAD(&cmd->list);
>>> + cmd->opcode = erase->opcode;
>>> + cmd->count = 1;
>>> +
>>> + if (region->offset & SNOR_OVERLAID_REGION)
>>> + cmd->size = region->size;
>>> + else
>>> + cmd->size = erase->size;
>>> +
>>> + return cmd;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
>>> + * @erase_list: list of erase commands
>>> + */
>>> +static void spi_nor_destroy_erase_cmd_list(struct list_head
>>> +*erase_list) {
>>> + struct spi_nor_erase_command *cmd, *next;
>>> +
>>> + list_for_each_entry_safe(cmd, next, erase_list, list) {
>>> + list_del(&cmd->list);
>>> + kfree(cmd);
>>> + }
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_init_erase_cmd_list() - initialize erase command list
>>> + * @nor: pointer to a 'struct spi_nor'
>>> + * @erase_list: list of erase commands to be executed once we validate that
>>> the
>>> + * erase can be performed
>>> + * @addr: offset in the serial flash memory
>>> + * @len: number of bytes to erase
>>> + *
>>> + * Builds the list of best fitted erase commands and verifies if the
>>> +erase can
>>> + * be performed.
>>> + *
>>> + * Returns 0 on success, -errno otherwise.
>>> + */
>>> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
>>> + struct list_head *erase_list,
>>> + u64 addr, u32 len)
>>> +{
>>> + const struct spi_nor_erase_map *map = &nor->erase_map;
>>> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
>>> + struct spi_nor_erase_region *region;
>>> + struct spi_nor_erase_command *cmd = NULL;
>>> + u64 region_end;
>>> + int ret = -EINVAL;
>>> +
>>> + region = spi_nor_find_erase_region(map, addr);
>>> + if (IS_ERR(region))
>>> + return PTR_ERR(region);
>>> +
>>> + region_end = spi_nor_region_end(region);
>>> +
>>> + while (len) {
>>> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
>>> + if (!erase)
>>> + goto destroy_erase_cmd_list;
>>> +
>>> + if (prev_erase != erase ||
>>> + region->offset & SNOR_OVERLAID_REGION) {
>>> + cmd = spi_nor_init_erase_cmd(region, erase);
>>> + if (IS_ERR(cmd)) {
>>> + ret = PTR_ERR(cmd);
>>> + goto destroy_erase_cmd_list;
>>> + }
>>> +
>>> + list_add_tail(&cmd->list, erase_list);
>>> + } else {
>>> + cmd->count++;
>>> + }
>>> +
>>> + addr += cmd->size;
>>> + len -= cmd->size;
>>> +
>>> + if (len && addr >= region_end) {
>>> + region = spi_nor_region_next(region);
>>> + if (!region)
>>> + goto destroy_erase_cmd_list;
>>> + region_end = spi_nor_region_end(region);
>>> + }
>>> +
>>> + prev_erase = erase;
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +destroy_erase_cmd_list:
>>> + spi_nor_destroy_erase_cmd_list(erase_list);
>>> + return ret;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
>>> + * @nor: pointer to a 'struct spi_nor'
>>> + * @addr: offset in the serial flash memory
>>> + * @len: number of bytes to erase
>>> + *
>>> + * Build a list of best fitted erase commands and execute it once we
>>> + * validate that the erase can be performed.
>>> + *
>>> + * Returns 0 on success, -errno otherwise.
>>> + */
>>> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr,
>>> +u32 len) {
>>> + LIST_HEAD(erase_list);
>>> + struct spi_nor_erase_command *cmd, *next;
>>> + int ret;
>>> +
>>> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
>>> + nor->erase_opcode = cmd->opcode;
>>> + while (cmd->count) {
>>> + write_enable(nor);
>>> +
>>> + ret = spi_nor_erase_sector(nor, addr);
>>> + if (ret)
>>> + goto destroy_erase_cmd_list;
>>> +
>>> + addr += cmd->size;
>>> + cmd->count--;
>>> +
>>> + ret = spi_nor_wait_till_ready(nor);
>>> + if (ret)
>>> + goto destroy_erase_cmd_list;
>>> + }
>>> + list_del(&cmd->list);
>>> + kfree(cmd);
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +destroy_erase_cmd_list:
>>> + spi_nor_destroy_erase_cmd_list(&erase_list);
>>> + return ret;
>>> +}
>>> +
>>> +/*
>>> * Erase an address range on the nor chip. The address range may extend
>>> * one or more erase sectors. Return an error is there is a problem erasing.
>>> */
>>> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>>> erase_info *instr)
>>> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>>> (long long)instr->len);
>>>
>>> - div_u64_rem(instr->len, mtd->erasesize, &rem);
>>> - if (rem)
>>> - return -EINVAL;
>>> + if (spi_nor_has_uniform_erase(nor)) {
>>> + div_u64_rem(instr->len, mtd->erasesize, &rem);
>>> + if (rem)
>>> + return -EINVAL;
>>> + }
>>>
>>> addr = instr->addr;
>>> len = instr->len;
>>> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>>> erase_info *instr)
>>> */
>>>
>>> /* "sector"-at-a-time erase */
>>> - } else {
>>> + } else if (spi_nor_has_uniform_erase(nor)) {
>>> while (len) {
>>> write_enable(nor);
>>>
>>> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct
>>> erase_info *instr)
>>> if (ret)
>>> goto erase_err;
>>> }
>>> +
>>> + /* erase multiple sectors */
>>> + } else {
>>> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
>>> + if (ret)
>>> + goto erase_err;
>>> }
>>>
>>> write_disable(nor);
>>> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
>>> sfdp_bfpt_erases[] = {
>>>
>>> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
>>>
>>> +/*
>>> + * spi_nor_set_erase_type() - set a SPI NOR erase type
>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>> + * @size: the size of the sector/block erased by the erase type
>>> + * @opcode: the SPI command op code to erase the sector/block
>>> + */
>>> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
>>> + u32 size, u8 opcode)
>>> +{
>>> + erase->size = size;
>>> + erase->opcode = opcode;
>>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>>> + erase->size_shift = ffs(erase->size) - 1;
>>> + erase->size_mask = (1 << erase->size_shift) - 1; }
>>> +
>>> +/*
>>> + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>> + * @size: the size of the sector/block erased by the erase type
>>> + * @opcode: the SPI command op code to erase the sector/block
>>> + * @i: erase type index as sorted in the Basic Flash Parameter Table
>>> + *
>>> + * The supported Erase Types will be sorted at init in ascending order,
>>> +with
>>> + * the smallest Erase Type size being the first member in the
>>> +erase_type array
>>> + * of the spi_nor_erase_map structure. Save the Erase Type index as
>>> +sorted in
>>> + * the Basic Flash Parameter Table since it will be used later on to
>>> + * synchronize with the supported Erase Types defined in SFDP optional tables.
>>> + */
>>> +static void
>>> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
>>> + u32 size, u8 opcode, u8 i)
>>> +{
>>> + erase->idx = i;
>>> + spi_nor_set_erase_type(erase, size, opcode); }
>>> +
>>> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
>>> + * @l: member in the left half of the map's erase_type array
>>> + * @r: member in the right half of the map's erase_type array
>>> + *
>>> + * Comparison function used in the sort() call to sort in ascending
>>> +order the
>>> + * map's erase types, the smallest erase type size being the first
>>> +member in the
>>> + * sorted erase_type array.
>>> + */
>>> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
>>> + const struct spi_nor_erase_type *left = l, *right = r;
>>> +
>>> + return left->size - right->size;
>>> +}
>>> +
>>> +/*
>>> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
>>> + * @map: the erase map of the SPI NOR
>>> + *
>>> + * Function assumes that the erase types defined in the erase map are
>>> +already
>>> + * sorted in ascending order, with the smallest erase type size being
>>> +the first
>>> + * member in the erase_type array. It replicates the sort done for the
>>> +map's
>>> + * erase types. Each region's erase bitmask will indicate which erase
>>> +types are
>>> + * supported from the sorted erase types defined in the erase map.
>>> + * Sort the all region's erase type at init in order to speed up the
>>> +process of
>>> + * finding the best erase command at runtime.
>>> + */
>>> +static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map
>>> +*map) {
>>> + struct spi_nor_erase_region *region = map->regions;
>>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>>> + int i;
>>> + u8 region_erase_mask, sorted_erase_mask;
>>> +
>>> + while (region) {
>>> + region_erase_mask = region->offset &
>>> SNOR_ERASE_TYPE_MASK;
>>> +
>>> + /* Replicate the sort done for the map's erase types. */
>>> + sorted_erase_mask = 0;
>>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
>>> + if (erase_type[i].size &&
>>> + region_erase_mask & BIT(erase_type[i].idx))
>>> + sorted_erase_mask |= BIT(i);
>>> +
>>> + /* Overwrite erase mask. */
>>> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
>>> |
>>> + sorted_erase_mask;
>>> +
>>> + region = spi_nor_region_next(region);
>>> + }
>>> +}
>>> +
>>> +/*
>>> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
>>> + * @map: the erase map of the SPI NOR
>>> + * @erase_mask: bitmask encoding erase types that can erase
>>> the entire
>>> + * flash memory
>>> + * @flash_size: the spi nor flash memory size
>>> + */
>>> +static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
>>> + u8 erase_mask, u64 flash_size)
>>> +{
>>> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
>>> + map->uniform_region.offset = (erase_mask &
>>> SNOR_ERASE_TYPE_MASK) |
>>> + SNOR_LAST_REGION;
>>> + map->uniform_region.size = flash_size;
>>> + map->regions = &map->uniform_region;
>>> + map->uniform_erase_type = erase_mask;
>>> +}
>>> +
>>> /**
>>> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
>>> * @nor: pointer to a 'struct spi_nor'
>>> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>> const struct sfdp_parameter_header *bfpt_header,
>>> struct spi_nor_flash_parameter *params) {
>>> - struct mtd_info *mtd = &nor->mtd;
>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>>> struct sfdp_bfpt bfpt;
>>> size_t len;
>>> int i, cmd, err;
>>> u32 addr;
>>> u16 half;
>>> + u8 erase_mask;
>>>
>>> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
>>> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
>>> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
>>> }
>>>
>>> - /* Sector Erase settings. */
>>> + /*
>>> + * Sector Erase settings. Reinitialize the uniform erase map using the
>>> + * Erase Types defined in the bfpt table.
>>> + */
>>> + erase_mask = 0;
>>> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
>>> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
>>> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
>>> u32 erasesize;
>>> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>>
>>> erasesize = 1U << erasesize;
>>> opcode = (half >> 8) & 0xff;
>>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>> - if (erasesize == SZ_4K) {
>>> - nor->erase_opcode = opcode;
>>> - mtd->erasesize = erasesize;
>>> - break;
>>> - }
>>> -#endif
>>> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
>>> - nor->erase_opcode = opcode;
>>> - mtd->erasesize = erasesize;
>>> - }
>>> + erase_mask |= BIT(i);
>>> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
>>> erasesize,
>>> + opcode, i);
>>> }
>>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>>> + /*
>>> + * Sort all the map's Erase Types in ascending order with the smallest
>>> + * erase size being the first member in the erase_type array.
>>> + */
>>> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
>>> + spi_nor_map_cmp_erase_type, NULL);
>>> + /*
>>> + * Sort the erase types in the uniform region in order to update the
>>> + * uniform_erase_type bitmask. The bitmask will be used later on when
>>> + * selecting the uniform erase.
>>> + */
>>> + spi_nor_regions_sort_erase_types(map);
>>> + map->uniform_erase_type = map->uniform_region.offset &
>>> + SNOR_ERASE_TYPE_MASK;
>>>
>>> /* Stop here if not JESD216 rev A or later. */
>>> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
>>> @@ static int spi_nor_init_params(struct spi_nor *nor,
>>> const struct flash_info *info,
>>> struct spi_nor_flash_parameter *params) {
>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>> + u8 i, erase_mask;
>>> +
>>> /* Set legacy flash parameters as default. */
>>> memset(params, 0, sizeof(*params));
>>>
>>> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
>>> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
>>> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
>>>
>>> + /*
>>> + * Sector Erase settings. Sort Erase Types in ascending order, with the
>>> + * smallest erase size starting at BIT(0).
>>> + */
>>> + erase_mask = 0;
>>> + i = 0;
>>> + if (info->flags & SECT_4K_PMC) {
>>> + erase_mask |= BIT(i);
>>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>>> + SPINOR_OP_BE_4K_PMC);
>>> + i++;
>>> + } else if (info->flags & SECT_4K) {
>>> + erase_mask |= BIT(i);
>>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>>> + SPINOR_OP_BE_4K);
>>> + i++;
>>> + }
>>> + erase_mask |= BIT(i);
>>> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
>>> + SPINOR_OP_SE);
>>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>>> +
>>> /* Select the procedure to set the Quad Enable bit. */
>>> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>>> SNOR_HWCAPS_PP_QUAD)) {
>>> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor *nor,
>>> params->quad_enable = info->quad_enable;
>>> }
>>>
>>> - /* Override the parameters with data read from SFDP tables. */
>>> - nor->addr_width = 0;
>>> - nor->mtd.erasesize = 0;
>>> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
>>> !(info->flags & SPI_NOR_SKIP_SFDP)) {
>>> struct spi_nor_flash_parameter sfdp_params;
>>> + struct spi_nor_erase_map prev_map;
>>>
>>> memcpy(&sfdp_params, params, sizeof(sfdp_params));
>>> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
>>> - nor->addr_width = 0;
>>> - nor->mtd.erasesize = 0;
>>> - } else {
>>> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
>>> +
>>> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
>>> + /* restore previous erase map */
>>> + memcpy(&nor->erase_map, &prev_map,
>>> + sizeof(nor->erase_map));
>>> + else
>>> memcpy(params, &sfdp_params, sizeof(*params));
>>> - }
>>> }
>>>
>>> return 0;
>>> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor *nor,
>>> return 0;
>>> }
>>>
>>> -static int spi_nor_select_erase(struct spi_nor *nor,
>>> - const struct flash_info *info)
>>> +/*
>>> + * 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.
>>> + *
>>> + * Once the optimum uniform sector erase command is found, disable all
>>> +the
>>> + * other.
>>> + *
>>> + * Return: pointer to erase type on success, NULL otherwise.
>>> + */
>>> +static const struct spi_nor_erase_type *
>>> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
>>> + const u32 wanted_size)
>>> {
>>> - struct mtd_info *mtd = &nor->mtd;
>>> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
>>> + int i;
>>> + u8 uniform_erase_type = map->uniform_erase_type;
>>>
>>> - /* Do nothing if already configured from SFDP. */
>>> - if (mtd->erasesize)
>>> - return 0;
>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>> + if (!(uniform_erase_type & BIT(i)))
>>> + continue;
>>> +
>>> + tested_erase = &map->erase_type[i];
>>> +
>>> + /*
>>> + * If the current erase size is the one, stop here:
>>> + * we have found the right uniform Sector Erase command.
>>> + */
>>> + if (tested_erase->size == wanted_size) {
>>> + erase = tested_erase;
>>> + break;
>>> + }
>>>
>>> + /*
>>> + * Otherwise, the current erase size is still a valid canditate.
>>> + * Select the biggest valid candidate.
>>> + */
>>> + if (!erase && tested_erase->size)
>>> + erase = tested_erase;
>>> + /* keep iterating to find the wanted_size */
>>> + }
>>> +
>>> + if (!erase)
>>> + return NULL;
>>> +
>>> + /* Disable all other Sector Erase commands. */
>>> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
>>> + map->uniform_erase_type |= BIT(erase - map->erase_type);
>>> + return erase;
>>> +}
>>> +
>>> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>> + const struct spi_nor_erase_type *erase = NULL;
>>> + struct mtd_info *mtd = &nor->mtd;
>>> + int i;
>>> +
>>> + /*
>>> + * The previous implementation handling Sector Erase commands
>>> assumed
>>> + * that the SPI flash memory has an uniform layout then used only one
>>> + * of the supported erase sizes for all Sector Erase commands.
>>> + * So to be backward compatible, the new implementation also tries to
>>> + * manage the SPI flash memory as uniform with a single erase sector
>>> + * size, when possible.
>>> + */
>>> #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
>>> + wanted_size = 4096u;
>>> #endif
>>> - {
>>> - nor->erase_opcode = SPINOR_OP_SE;
>>> - mtd->erasesize = info->sector_size;
>>> +
>>> + if (spi_nor_has_uniform_erase(nor)) {
>>> + erase = spi_nor_select_uniform_erase(map, wanted_size);
>>> + if (!erase)
>>> + return -EINVAL;
>>> + nor->erase_opcode = erase->opcode;
>>> + mtd->erasesize = erase->size;
>>> + return 0;
>>> }
>>> +
>>> + /*
>>> + * For non-uniform SPI flash memory, set mtd->erasesize to the
>>> + * maximum erase sector size. No need to set nor->erase_opcode.
>>> + */
>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>> + if (map->erase_type[i].size) {
>>> + erase = &map->erase_type[i];
>>> + break;
>>> + }
>>> + }
>>> +
>>> + if (!erase)
>>> + return -EINVAL;
>>> +
>>> + mtd->erasesize = erase->size;
>>> return 0;
>>> }
>>>
>>> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor, const
>>> struct flash_info *info,
>>> }
>>>
>>> /* Select the Sector Erase command. */
>>> - err = spi_nor_select_erase(nor, info);
>>> + err = spi_nor_select_erase(nor, info->sector_size);
>>> if (err) {
>>> dev_err(nor->dev,
>>> "can't select erase settings supported by both the SPI
>>> controller and memory.\n"); diff --git a/include/linux/mtd/spi-nor.h
>>> b/include/linux/mtd/spi-nor.h index 09a10fd..a873a0b 100644
>>> --- a/include/linux/mtd/spi-nor.h
>>> +++ b/include/linux/mtd/spi-nor.h
>>> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
>>>
>>> /**
>>> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
>>> + * @size: the size of the sector/block erased by the erase type.
>>> + * JEDEC JESD216B imposes erase sizes to be a power of 2.
>>> + * @size_shift: @size is a power of 2, the shift is stored in
>>> + * @size_shift.
>>> + * @size_mask: the size mask based on @size_shift.
>>> + * @opcode: the SPI command op code to erase the sector/block.
>>> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
>>> + * Table. It will be used to synchronize the supported
>>> + * Erase Types with the ones identified in the SFDP
>>> + * optional tables.
>>> + */
>>> +struct spi_nor_erase_type {
>>> + u32 size;
>>> + u32 size_shift;
>>> + u32 size_mask;
>>> + u8 opcode;
>>> + u8 idx;
>>> +};
>>> +
>>> +/**
>>> + * struct spi_nor_erase_command - Used for non-uniform erases
>>> + * The structure is used to describe a list of erase commands to be
>>> +executed
>>> + * once we validate that the erase can be performed. The elements in
>>> +the list
>>> + * are run-length encoded.
>>> + * @list: for inclusion into the list of erase commands.
>>> + * @count: how many times the same erase command should be
>>> + * consecutively used.
>>> + * @size: the size of the sector/block erased by the command.
>>> + * @opcode: the SPI command op code to erase the sector/block.
>>> + */
>>> +struct spi_nor_erase_command {
>>> + struct list_head list;
>>> + u32 count;
>>> + u32 size;
>>> + u8 opcode;
>>> +};
>>> +
>>> +/**
>>> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
>>> + * @offset: the offset in the data array of erase region start.
>>> + * LSB bits are used as a bitmask encoding flags to
>>> + * determine if this region is overlaid, if this region is
>>> + * the last in the SPI NOR flash memory and to indicate
>>> + * all the supported erase commands inside this region.
>>> + * The erase types are sorted in ascending order with the
>>> + * smallest Erase Type size being at BIT(0).
>>> + * @size: the size of the region in bytes.
>>> + */
>>> +struct spi_nor_erase_region {
>>> + u64 offset;
>>> + u64 size;
>>> +};
>>> +
>>> +#define SNOR_ERASE_TYPE_MAX 4
>>> +#define SNOR_ERASE_TYPE_MASK
>>> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
>>> +
>>> +#define SNOR_LAST_REGION BIT(4)
>>> +#define SNOR_OVERLAID_REGION BIT(5)
>>> +
>>> +#define SNOR_ERASE_FLAGS_MAX 6
>>> +#define SNOR_ERASE_FLAGS_MASK
>>> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
>>> +
>>> +/**
>>> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
>>> + * @regions: array of erase regions. The regions are consecutive in
>>> + * address space. Walking through the regions is done
>>> + * incrementally.
>>> + * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform
>>> + * sector size (legacy implementation).
>>> + * @erase_type: an array of erase types shared by all the regions.
>>> + * The erase types are sorted in ascending order, with the
>>> + * smallest Erase Type size being the first member in the
>>> + * erase_type array.
>>> + * @uniform_erase_type: bitmask encoding erase types that can erase
>>> the
>>> + * entire memory. This member is completed at init by
>>> + * uniform and non-uniform SPI NOR flash memories if
>>> they
>>> + * support at least one erase type that can erase the
>>> + * entire memory.
>>> + */
>>> +struct spi_nor_erase_map {
>>> + struct spi_nor_erase_region *regions;
>>> + struct spi_nor_erase_region uniform_region;
>>> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
>>> + u8 uniform_erase_type;
>>> +};
>>> +
>>> +/**
>>> * struct flash_info - Forward declaration of a structure used internally by
>>> * spi_nor_scan()
>>> */
>>> @@ -263,6 +351,7 @@ struct flash_info;
>>> * @write_proto: the SPI protocol for write operations
>>> * @reg_proto the SPI protocol for read_reg/write_reg/erase
>>> operations
>>> * @cmd_buf: used by the write_reg
>>> + * @erase_map: the erase map of the SPI NOR
>>> * @prepare: [OPTIONAL] do some preparations for the
>>> * read/write/erase/lock/unlock operations
>>> * @unprepare: [OPTIONAL] do some post work after the
>>> @@ -298,6 +387,7 @@ struct spi_nor {
>>> bool sst_write_second;
>>> u32 flags;
>>> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>>> + struct spi_nor_erase_map erase_map;
>>>
>>> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
>>> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@ -
>>> 318,6 +408,23 @@ struct spi_nor {
>>> void *priv;
>>> };
>>>
>>> +static u64 __maybe_unused
>>> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
>>> + return region->offset & SNOR_LAST_REGION; }
>>> +
>>> +static u64 __maybe_unused
>>> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
>>> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; }
>>> +
>>> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
>>> +spi_nor *nor) {
>>> + return !!nor->erase_map.uniform_erase_type;
>>> +}
>>> +
>>> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
>>> struct device_node *np)
>>> {
>>> --
>>> 2.9.4
>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.infr
>>> adead.org%2Fmailman%2Flistinfo%2Flinux-
>>> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
>>> 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
>>> 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
>>> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0
>>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>

2018-10-17 01:54:44

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Boris,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Tuesday, October 16, 2018 5:48 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Tudor Ambarus <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Tue, 16 Oct 2018 14:04:11 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Tue, 16 Oct 2018 09:51:47 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > Hi Tudor,
> > >
> > > This patch is breaking the 1-4-4 Read protocol for the spansion flash
> "s25fl512s".
> > >
> > > Without this patch read request command for Quad mode, 4-byte enable, is
> coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> > > But after applying this patch, read request command for Quad mode is
> coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
> > >
> > > This flash also supports non-uniform erase.
> > > Can you please check and provide some suggestion?
> >
> > Are you sure the regression comes from this patch? I suspect your bug
> > comes from 41fe242979e4 ("mtd: spi-nor: fsl-quadspi: fix read error
> > for flash size larger than 16MB").
>
> I guess you're testing with an fsl-qspi controller, right? Can you try with this
> patch?

I am testing nxp-flexspi controller and doing just read of small size 0x200.
Also 1-1-1 protocol i.e. spi-rx/tx-bus-width as 1 is working fine for this flash.

Without this patch read from 1-4-4 protocol is working correctly.

--
Regards
Yogesh Gaur

>
> --->8---
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 1ff3430f82c8..c47fe70c9f98 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -477,9 +477,6 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int
> fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) {
> switch (cmd) {
> - case SPINOR_OP_READ_1_1_4:
> - case SPINOR_OP_READ_1_1_4_4B:
> - return SEQID_READ;
> case SPINOR_OP_WREN:
> return SEQID_WREN;
> case SPINOR_OP_WRDI:
> @@ -490,8 +487,6 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> return SEQID_SE;
> case SPINOR_OP_CHIP_ERASE:
> return SEQID_CHIP_ERASE;
> - case SPINOR_OP_PP:
> - return SEQID_PP;
> case SPINOR_OP_RDID:
> return SEQID_RDID;
> case SPINOR_OP_WRSR:
> @@ -503,7 +498,11 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> case SPINOR_OP_BRWR:
> return SEQID_BRWR;
> default:
> - if (cmd == q->nor[0].erase_opcode)
> + if (cmd == q->nor[0].read_opcode)
> + return SEQID_READ;
> + else if (cmd == q->nor[0].program_opcode)
> + return SEQID_PP;
> + else if (cmd == q->nor[0].erase_opcode)
> return SEQID_SE;
> dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> break;

2018-10-17 02:10:08

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Tudor,

> -----Original Message-----
> From: Cyrille Pitchen [mailto:[email protected]]
> Sent: Tuesday, October 16, 2018 10:04 PM
> To: Tudor Ambarus <[email protected]>; Yogesh Narayan Gaur
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> Hi Tudor,
>
> Le 16/10/2018 à 17:14, Tudor Ambarus a écrit :
> > Hi, Yogesh,
> >
> > On 10/16/2018 12:51 PM, Yogesh Narayan Gaur wrote:
> >> Hi Tudor,
> >>
> >> This patch is breaking the 1-4-4 Read protocol for the spansion flash
> "s25fl512s".
> >>
> >> Without this patch read request command for Quad mode, 4-byte enable, is
> coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> >> But after applying this patch, read request command for Quad mode is
> coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
> >>
> >> This flash also supports non-uniform erase.
> >> Can you please check and provide some suggestion?
> >
> > I don't have this memory to test it, but I'll try to help.
> >
> > Does s25fl512s support non-uniform erase? I'm looking in datasheet[1]
> > at JEDEC BFPT table, dwords 8 and 9, page 132/146 and it looks like it
> > supports just 256KB uniform erase.
> >
>
Actually there is no entry of s25fs512s in current spi-nor.c file.
For my connected flash part, jedec ID read points to s25fl512s. I have asked my board team to confirm the name of exact connected flash part.
When I check the data sheet of s25fs512s, it also points to the same Jedec ID information.
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, ....}

But as stated earlier, if I skip reading SFDP or read using 1-1-1 protocol then read are always correct.
For 1-4-4 protocol read are wrong and on further debugging found that Read code of 0x6C is being send as opcode instead of 0xEC.

If I revert this patch, reads are working fine.

--
Regards
Yogesh Gaur

> s25fS512s supports both uniform and non uniform erase options but s25fL512s is
> always uniform. L is an old memory part, S is newer.
>
> Also, the 8th and 9th WORDs of the Basic Flash Parameter Table alone can't tell
> you whether or not the memory part can be non uniform.
> If the memory can be non uniform then the sector erase map table is mandatory,
> hence when the table is missing you know that your memory part is always
> uniform.
>
> Best regards,
>
> Cyrille
>
> > Thanks,
> > ta
> >
> > [1]
> > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> >
> cypress.com%2Ffile%2F177971%2Fdownload&amp;data=02%7C01%7Cyogeshn
> araya
> >
> n.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%7C686ea1d3bc2
> b4c6f
> >
> a92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;sdata=cioC98EH
> OGlFbg
> > XPhoIIJ72K3JrNUnzA1pYhSB9jDwg%3D&amp;reserved=0
> >
> >>
> >> --
> >> Regards
> >> Yogesh Gaur
> >>
> >>> -----Original Message-----
> >>> From: linux-mtd [mailto:[email protected]] On
> >>> Behalf Of Tudor Ambarus
> >>> Sent: Tuesday, September 11, 2018 9:10 PM
> >>> To: [email protected]; [email protected];
> >>> [email protected]; [email protected];
> >>> [email protected]
> >>> Cc: Tudor Ambarus <[email protected]>; linux-
> >>> [email protected]; [email protected];
> >>> [email protected]; [email protected];
> >>> linux-arm- [email protected]; [email protected]
> >>> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> >>> SFDP SPI NOR flash memories
> >>>
> >>> Based on Cyrille Pitchen's patch
> >>> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fl
> >>> kml.or
> >>>
> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
> >>>
> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
> >>>
> c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
> >>>
> 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
> >>>
> >>> This patch is a transitional patch in introducing the support of
> >>> SFDP SPI memories with non-uniform erase sizes like Spansion s25fs512s.
> >>> Non-uniform erase maps will be used later when initialized based on
> >>> the SFDP data.
> >>>
> >>> Introduce the memory erase map which splits the memory array into
> >>> one or many erase regions. Each erase region supports up to 4 erase
> >>> types, as defined by the JEDEC JESD216B (SFDP) specification.
> >>>
> >>> To be backward compatible, the erase map of uniform SPI NOR flash
> >>> memories is initialized so it contains only one erase region and
> >>> this erase region supports only one erase command. Hence a single
> >>> size is used to erase any sector/block of the memory.
> >>>
> >>> Besides, since the algorithm used to erase sectors on non-uniform
> >>> SPI NOR flash memories is quite expensive, when possible, the erase
> >>> map is tuned to come back to the uniform case.
> >>>
> >>> The 'erase with the best command, move forward and repeat' approach
> >>> was suggested by Cristian Birsan in a brainstorm session, so:
> >>>
> >>> Suggested-by: Cristian Birsan <[email protected]>
> >>> Signed-off-by: Tudor Ambarus <[email protected]>
> >>> ---
> >>> drivers/mtd/spi-nor/spi-nor.c | 594
> >>> +++++++++++++++++++++++++++++++++++++++---
> >>> include/linux/mtd/spi-nor.h | 107 ++++++++
> >>> 2 files changed, 659 insertions(+), 42 deletions(-)
> >>>
> >>> diff --git a/drivers/mtd/spi-nor/spi-nor.c
> >>> b/drivers/mtd/spi-nor/spi-nor.c index
> >>> dc8757e..4687345 100644
> >>> --- a/drivers/mtd/spi-nor/spi-nor.c
> >>> +++ b/drivers/mtd/spi-nor/spi-nor.c
> >>> @@ -18,6 +18,7 @@
> >>> #include <linux/math64.h>
> >>> #include <linux/sizes.h>
> >>> #include <linux/slab.h>
> >>> +#include <linux/sort.h>
> >>>
> >>> #include <linux/mtd/mtd.h>
> >>> #include <linux/of_platform.h>
> >>> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct
> >>> spi_nor *nor,
> >>> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
> >>> nor->program_opcode = spi_nor_convert_3to4_program(nor-
> >>>> program_opcode);
> >>> nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
> >>> +
> >>> + if (!spi_nor_has_uniform_erase(nor)) {
> >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> >>> + struct spi_nor_erase_type *erase;
> >>> + int i;
> >>> +
> >>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
> >>> + erase = &map->erase_type[i];
> >>> + erase->opcode =
> >>> + spi_nor_convert_3to4_erase(erase->opcode);
> >>> + }
> >>> + }
> >>> }
> >>>
> >>> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275 @@
> >>> static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) }
> >>>
> >>> /*
> >>> + * spi_nor_div_by_erase_size() - calculate remainder and update new
> dividend
> >>> + * @erase: pointer to a structure that describes a SPI NOR erase type
> >>> + * @dividend: dividend value
> >>> + * @remainder: pointer to u32 remainder (will be updated)
> >>> + *
> >>> + * Returns two values: remainder and the new dividend */ static
> >>> +u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
> >>> + u64 dividend, u32 *remainder) {
> >>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> >>> + *remainder = (u32)dividend & erase->size_mask;
> >>> + return dividend >> erase->size_shift; }
> >>> +
> >>> +/*
> >>> + * spi_nor_find_best_erase_type() - find the best erase type for
> >>> +the given
> >>> + * offset in the serial flash memory and the number of bytes to erase.
> >>> +The
> >>> + * region in which the address fits is expected to be provided.
> >>> + * @map: the erase map of the SPI NOR
> >>> + * @region: pointer to a structure that describes a SPI NOR erase
> region
> >>> + * @addr: offset in the serial flash memory
> >>> + * @len: number of bytes to erase
> >>> + *
> >>> + * Returns a pointer to the best fitted erase type, NULL otherwise.
> >>> + */
> >>> +static const struct spi_nor_erase_type *
> >>> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
> >>> + const struct spi_nor_erase_region *region,
> >>> + u64 addr, u32 len)
> >>> +{
> >>> + const struct spi_nor_erase_type *erase;
> >>> + u32 rem;
> >>> + int i;
> >>> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
> >>> +
> >>> + /*
> >>> + * Erase types are ordered by size, with the biggest erase type at
> >>> + * index 0.
> >>> + */
> >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> >>> + /* Does the erase region support the tested erase type? */
> >>> + if (!(erase_mask & BIT(i)))
> >>> + continue;
> >>> +
> >>> + erase = &map->erase_type[i];
> >>> +
> >>> + /* Don't erase more than what the user has asked for. */
> >>> + if (erase->size > len)
> >>> + continue;
> >>> +
> >>> + /* Alignment is not mandatory for overlaid regions */
> >>> + if (region->offset & SNOR_OVERLAID_REGION)
> >>> + return erase;
> >>> +
> >>> + spi_nor_div_by_erase_size(erase, addr, &rem);
> >>> + if (rem)
> >>> + continue;
> >>> + else
> >>> + return erase;
> >>> + }
> >>> +
> >>> + return NULL;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_region_next() - get the next spi nor region
> >>> + * @region: pointer to a structure that describes a SPI NOR erase
> region
> >>> + *
> >>> + * Returns the next spi nor region or NULL if last region.
> >>> + */
> >>> +static struct spi_nor_erase_region * spi_nor_region_next(struct
> >>> +spi_nor_erase_region *region) {
> >>> + if (spi_nor_region_is_last(region))
> >>> + return NULL;
> >>> + region++;
> >>> + return region;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_find_erase_region() - find the region of the serial
> >>> +flash memory in
> >>> + * which the offset fits
> >>> + * @map: the erase map of the SPI NOR
> >>> + * @addr: offset in the serial flash memory
> >>> + *
> >>> + * Returns pointer to the spi_nor_erase_region struct,
> >>> +ERR_PTR(-errno)
> >>> + * otherwise.
> >>> + */
> >>> +static struct spi_nor_erase_region *
> >>> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64
> >>> +addr) {
> >>> + struct spi_nor_erase_region *region = map->regions;
> >>> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> >>> + u64 region_end = region_start + region->size;
> >>> +
> >>> + while (addr < region_start || addr >= region_end) {
> >>> + region = spi_nor_region_next(region);
> >>> + if (!region)
> >>> + return ERR_PTR(-EINVAL);
> >>> +
> >>> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> >>> + region_end = region_start + region->size;
> >>> + }
> >>> +
> >>> + return region;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_init_erase_cmd() - initialize an erase command
> >>> + * @region: pointer to a structure that describes a SPI NOR erase
> region
> >>> + * @erase: pointer to a structure that describes a SPI NOR erase type
> >>> + *
> >>> + * Returns the pointer to the allocated erase command,
> >>> +ERR_PTR(-errno)
> >>> + * otherwise.
> >>> + */
> >>> +static struct spi_nor_erase_command * spi_nor_init_erase_cmd(const
> >>> +struct spi_nor_erase_region *region,
> >>> + const struct spi_nor_erase_type *erase) {
> >>> + struct spi_nor_erase_command *cmd;
> >>> +
> >>> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
> >>> + if (!cmd)
> >>> + return ERR_PTR(-ENOMEM);
> >>> +
> >>> + INIT_LIST_HEAD(&cmd->list);
> >>> + cmd->opcode = erase->opcode;
> >>> + cmd->count = 1;
> >>> +
> >>> + if (region->offset & SNOR_OVERLAID_REGION)
> >>> + cmd->size = region->size;
> >>> + else
> >>> + cmd->size = erase->size;
> >>> +
> >>> + return cmd;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
> >>> + * @erase_list: list of erase commands
> >>> + */
> >>> +static void spi_nor_destroy_erase_cmd_list(struct list_head
> >>> +*erase_list) {
> >>> + struct spi_nor_erase_command *cmd, *next;
> >>> +
> >>> + list_for_each_entry_safe(cmd, next, erase_list, list) {
> >>> + list_del(&cmd->list);
> >>> + kfree(cmd);
> >>> + }
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_init_erase_cmd_list() - initialize erase command list
> >>> + * @nor: pointer to a 'struct spi_nor'
> >>> + * @erase_list: list of erase commands to be executed once we
> validate that
> >>> the
> >>> + * erase can be performed
> >>> + * @addr: offset in the serial flash memory
> >>> + * @len: number of bytes to erase
> >>> + *
> >>> + * Builds the list of best fitted erase commands and verifies if
> >>> +the erase can
> >>> + * be performed.
> >>> + *
> >>> + * Returns 0 on success, -errno otherwise.
> >>> + */
> >>> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
> >>> + struct list_head *erase_list,
> >>> + u64 addr, u32 len)
> >>> +{
> >>> + const struct spi_nor_erase_map *map = &nor->erase_map;
> >>> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
> >>> + struct spi_nor_erase_region *region;
> >>> + struct spi_nor_erase_command *cmd = NULL;
> >>> + u64 region_end;
> >>> + int ret = -EINVAL;
> >>> +
> >>> + region = spi_nor_find_erase_region(map, addr);
> >>> + if (IS_ERR(region))
> >>> + return PTR_ERR(region);
> >>> +
> >>> + region_end = spi_nor_region_end(region);
> >>> +
> >>> + while (len) {
> >>> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
> >>> + if (!erase)
> >>> + goto destroy_erase_cmd_list;
> >>> +
> >>> + if (prev_erase != erase ||
> >>> + region->offset & SNOR_OVERLAID_REGION) {
> >>> + cmd = spi_nor_init_erase_cmd(region, erase);
> >>> + if (IS_ERR(cmd)) {
> >>> + ret = PTR_ERR(cmd);
> >>> + goto destroy_erase_cmd_list;
> >>> + }
> >>> +
> >>> + list_add_tail(&cmd->list, erase_list);
> >>> + } else {
> >>> + cmd->count++;
> >>> + }
> >>> +
> >>> + addr += cmd->size;
> >>> + len -= cmd->size;
> >>> +
> >>> + if (len && addr >= region_end) {
> >>> + region = spi_nor_region_next(region);
> >>> + if (!region)
> >>> + goto destroy_erase_cmd_list;
> >>> + region_end = spi_nor_region_end(region);
> >>> + }
> >>> +
> >>> + prev_erase = erase;
> >>> + }
> >>> +
> >>> + return 0;
> >>> +
> >>> +destroy_erase_cmd_list:
> >>> + spi_nor_destroy_erase_cmd_list(erase_list);
> >>> + return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
> >>> + * @nor: pointer to a 'struct spi_nor'
> >>> + * @addr: offset in the serial flash memory
> >>> + * @len: number of bytes to erase
> >>> + *
> >>> + * Build a list of best fitted erase commands and execute it once
> >>> +we
> >>> + * validate that the erase can be performed.
> >>> + *
> >>> + * Returns 0 on success, -errno otherwise.
> >>> + */
> >>> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64
> >>> +addr,
> >>> +u32 len) {
> >>> + LIST_HEAD(erase_list);
> >>> + struct spi_nor_erase_command *cmd, *next;
> >>> + int ret;
> >>> +
> >>> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
> >>> + nor->erase_opcode = cmd->opcode;
> >>> + while (cmd->count) {
> >>> + write_enable(nor);
> >>> +
> >>> + ret = spi_nor_erase_sector(nor, addr);
> >>> + if (ret)
> >>> + goto destroy_erase_cmd_list;
> >>> +
> >>> + addr += cmd->size;
> >>> + cmd->count--;
> >>> +
> >>> + ret = spi_nor_wait_till_ready(nor);
> >>> + if (ret)
> >>> + goto destroy_erase_cmd_list;
> >>> + }
> >>> + list_del(&cmd->list);
> >>> + kfree(cmd);
> >>> + }
> >>> +
> >>> + return 0;
> >>> +
> >>> +destroy_erase_cmd_list:
> >>> + spi_nor_destroy_erase_cmd_list(&erase_list);
> >>> + return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> * Erase an address range on the nor chip. The address range may extend
> >>> * one or more erase sectors. Return an error is there is a problem erasing.
> >>> */
> >>> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd,
> >>> struct erase_info *instr)
> >>> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
> >>> (long long)instr->len);
> >>>
> >>> - div_u64_rem(instr->len, mtd->erasesize, &rem);
> >>> - if (rem)
> >>> - return -EINVAL;
> >>> + if (spi_nor_has_uniform_erase(nor)) {
> >>> + div_u64_rem(instr->len, mtd->erasesize, &rem);
> >>> + if (rem)
> >>> + return -EINVAL;
> >>> + }
> >>>
> >>> addr = instr->addr;
> >>> len = instr->len;
> >>> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd,
> >>> struct erase_info *instr)
> >>> */
> >>>
> >>> /* "sector"-at-a-time erase */
> >>> - } else {
> >>> + } else if (spi_nor_has_uniform_erase(nor)) {
> >>> while (len) {
> >>> write_enable(nor);
> >>>
> >>> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd,
> >>> struct erase_info *instr)
> >>> if (ret)
> >>> goto erase_err;
> >>> }
> >>> +
> >>> + /* erase multiple sectors */
> >>> + } else {
> >>> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
> >>> + if (ret)
> >>> + goto erase_err;
> >>> }
> >>>
> >>> write_disable(nor);
> >>> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
> >>> sfdp_bfpt_erases[] = {
> >>>
> >>> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
> >>>
> >>> +/*
> >>> + * spi_nor_set_erase_type() - set a SPI NOR erase type
> >>> + * @erase: pointer to a structure that describes a SPI NOR erase type
> >>> + * @size: the size of the sector/block erased by the erase type
> >>> + * @opcode: the SPI command op code to erase the sector/block
> >>> + */
> >>> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
> >>> + u32 size, u8 opcode)
> >>> +{
> >>> + erase->size = size;
> >>> + erase->opcode = opcode;
> >>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> >>> + erase->size_shift = ffs(erase->size) - 1;
> >>> + erase->size_mask = (1 << erase->size_shift) - 1; }
> >>> +
> >>> +/*
> >>> + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from
> BFPT
> >>> + * @erase: pointer to a structure that describes a SPI NOR erase type
> >>> + * @size: the size of the sector/block erased by the erase type
> >>> + * @opcode: the SPI command op code to erase the sector/block
> >>> + * @i: erase type index as sorted in the Basic Flash Parameter
> Table
> >>> + *
> >>> + * The supported Erase Types will be sorted at init in ascending
> >>> +order, with
> >>> + * the smallest Erase Type size being the first member in the
> >>> +erase_type array
> >>> + * of the spi_nor_erase_map structure. Save the Erase Type index as
> >>> +sorted in
> >>> + * the Basic Flash Parameter Table since it will be used later on
> >>> +to
> >>> + * synchronize with the supported Erase Types defined in SFDP optional
> tables.
> >>> + */
> >>> +static void
> >>> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
> >>> + u32 size, u8 opcode, u8 i) {
> >>> + erase->idx = i;
> >>> + spi_nor_set_erase_type(erase, size, opcode); }
> >>> +
> >>> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
> >>> + * @l: member in the left half of the map's erase_type array
> >>> + * @r: member in the right half of the map's erase_type array
> >>> + *
> >>> + * Comparison function used in the sort() call to sort in ascending
> >>> +order the
> >>> + * map's erase types, the smallest erase type size being the first
> >>> +member in the
> >>> + * sorted erase_type array.
> >>> + */
> >>> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
> >>> + const struct spi_nor_erase_type *left = l, *right = r;
> >>> +
> >>> + return left->size - right->size;
> >>> +}
> >>> +
> >>> +/*
> >>> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
> >>> + * @map: the erase map of the SPI NOR
> >>> + *
> >>> + * Function assumes that the erase types defined in the erase map
> >>> +are already
> >>> + * sorted in ascending order, with the smallest erase type size
> >>> +being the first
> >>> + * member in the erase_type array. It replicates the sort done for
> >>> +the map's
> >>> + * erase types. Each region's erase bitmask will indicate which
> >>> +erase types are
> >>> + * supported from the sorted erase types defined in the erase map.
> >>> + * Sort the all region's erase type at init in order to speed up
> >>> +the process of
> >>> + * finding the best erase command at runtime.
> >>> + */
> >>> +static void spi_nor_regions_sort_erase_types(struct
> >>> +spi_nor_erase_map
> >>> +*map) {
> >>> + struct spi_nor_erase_region *region = map->regions;
> >>> + struct spi_nor_erase_type *erase_type = map->erase_type;
> >>> + int i;
> >>> + u8 region_erase_mask, sorted_erase_mask;
> >>> +
> >>> + while (region) {
> >>> + region_erase_mask = region->offset &
> >>> SNOR_ERASE_TYPE_MASK;
> >>> +
> >>> + /* Replicate the sort done for the map's erase types. */
> >>> + sorted_erase_mask = 0;
> >>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
> >>> + if (erase_type[i].size &&
> >>> + region_erase_mask & BIT(erase_type[i].idx))
> >>> + sorted_erase_mask |= BIT(i);
> >>> +
> >>> + /* Overwrite erase mask. */
> >>> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
> >>> |
> >>> + sorted_erase_mask;
> >>> +
> >>> + region = spi_nor_region_next(region);
> >>> + }
> >>> +}
> >>> +
> >>> +/*
> >>> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
> >>> + * @map: the erase map of the SPI NOR
> >>> + * @erase_mask: bitmask encoding erase types that can erase
> >>> the entire
> >>> + * flash memory
> >>> + * @flash_size: the spi nor flash memory size
> >>> + */
> >>> +static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map
> *map,
> >>> + u8 erase_mask, u64 flash_size) {
> >>> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
> >>> + map->uniform_region.offset = (erase_mask &
> >>> SNOR_ERASE_TYPE_MASK) |
> >>> + SNOR_LAST_REGION;
> >>> + map->uniform_region.size = flash_size;
> >>> + map->regions = &map->uniform_region;
> >>> + map->uniform_erase_type = erase_mask; }
> >>> +
> >>> /**
> >>> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
> >>> * @nor: pointer to a 'struct spi_nor'
> >>> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor
> *nor,
> >>> const struct sfdp_parameter_header *bfpt_header,
> >>> struct spi_nor_flash_parameter *params) {
> >>> - struct mtd_info *mtd = &nor->mtd;
> >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> >>> + struct spi_nor_erase_type *erase_type = map->erase_type;
> >>> struct sfdp_bfpt bfpt;
> >>> size_t len;
> >>> int i, cmd, err;
> >>> u32 addr;
> >>> u16 half;
> >>> + u8 erase_mask;
> >>>
> >>> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
> >>> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
> >>> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
> >>> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
> >>> }
> >>>
> >>> - /* Sector Erase settings. */
> >>> + /*
> >>> + * Sector Erase settings. Reinitialize the uniform erase map using the
> >>> + * Erase Types defined in the bfpt table.
> >>> + */
> >>> + erase_mask = 0;
> >>> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
> >>> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
> >>> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
> >>> u32 erasesize;
> >>> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor
> >>> *nor,
> >>>
> >>> erasesize = 1U << erasesize;
> >>> opcode = (half >> 8) & 0xff;
> >>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> >>> - if (erasesize == SZ_4K) {
> >>> - nor->erase_opcode = opcode;
> >>> - mtd->erasesize = erasesize;
> >>> - break;
> >>> - }
> >>> -#endif
> >>> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
> >>> - nor->erase_opcode = opcode;
> >>> - mtd->erasesize = erasesize;
> >>> - }
> >>> + erase_mask |= BIT(i);
> >>> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
> >>> erasesize,
> >>> + opcode, i);
> >>> }
> >>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> >>> + /*
> >>> + * Sort all the map's Erase Types in ascending order with the smallest
> >>> + * erase size being the first member in the erase_type array.
> >>> + */
> >>> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
> >>> + spi_nor_map_cmp_erase_type, NULL);
> >>> + /*
> >>> + * Sort the erase types in the uniform region in order to update the
> >>> + * uniform_erase_type bitmask. The bitmask will be used later on when
> >>> + * selecting the uniform erase.
> >>> + */
> >>> + spi_nor_regions_sort_erase_types(map);
> >>> + map->uniform_erase_type = map->uniform_region.offset &
> >>> + SNOR_ERASE_TYPE_MASK;
> >>>
> >>> /* Stop here if not JESD216 rev A or later. */
> >>> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
> @@
> >>> static int spi_nor_init_params(struct spi_nor *nor,
> >>> const struct flash_info *info,
> >>> struct spi_nor_flash_parameter *params) {
> >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> >>> + u8 i, erase_mask;
> >>> +
> >>> /* Set legacy flash parameters as default. */
> >>> memset(params, 0, sizeof(*params));
> >>>
> >>> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor
> *nor,
> >>> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
> >>> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
> >>>
> >>> + /*
> >>> + * Sector Erase settings. Sort Erase Types in ascending order, with the
> >>> + * smallest erase size starting at BIT(0).
> >>> + */
> >>> + erase_mask = 0;
> >>> + i = 0;
> >>> + if (info->flags & SECT_4K_PMC) {
> >>> + erase_mask |= BIT(i);
> >>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> >>> + SPINOR_OP_BE_4K_PMC);
> >>> + i++;
> >>> + } else if (info->flags & SECT_4K) {
> >>> + erase_mask |= BIT(i);
> >>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> >>> + SPINOR_OP_BE_4K);
> >>> + i++;
> >>> + }
> >>> + erase_mask |= BIT(i);
> >>> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
> >>> + SPINOR_OP_SE);
> >>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> >>> +
> >>> /* Select the procedure to set the Quad Enable bit. */
> >>> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> >>> SNOR_HWCAPS_PP_QUAD)) {
> >>> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor
> *nor,
> >>> params->quad_enable = info->quad_enable;
> >>> }
> >>>
> >>> - /* Override the parameters with data read from SFDP tables. */
> >>> - nor->addr_width = 0;
> >>> - nor->mtd.erasesize = 0;
> >>> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
> >>> !(info->flags & SPI_NOR_SKIP_SFDP)) {
> >>> struct spi_nor_flash_parameter sfdp_params;
> >>> + struct spi_nor_erase_map prev_map;
> >>>
> >>> memcpy(&sfdp_params, params, sizeof(sfdp_params));
> >>> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
> >>> - nor->addr_width = 0;
> >>> - nor->mtd.erasesize = 0;
> >>> - } else {
> >>> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
> >>> +
> >>> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
> >>> + /* restore previous erase map */
> >>> + memcpy(&nor->erase_map, &prev_map,
> >>> + sizeof(nor->erase_map));
> >>> + else
> >>> memcpy(params, &sfdp_params, sizeof(*params));
> >>> - }
> >>> }
> >>>
> >>> return 0;
> >>> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor
> *nor,
> >>> return 0;
> >>> }
> >>>
> >>> -static int spi_nor_select_erase(struct spi_nor *nor,
> >>> - const struct flash_info *info)
> >>> +/*
> >>> + * 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.
> >>> + *
> >>> + * Once the optimum uniform sector erase command is found, disable
> >>> +all the
> >>> + * other.
> >>> + *
> >>> + * Return: pointer to erase type on success, NULL otherwise.
> >>> + */
> >>> +static const struct spi_nor_erase_type *
> >>> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
> >>> + const u32 wanted_size)
> >>> {
> >>> - struct mtd_info *mtd = &nor->mtd;
> >>> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
> >>> + int i;
> >>> + u8 uniform_erase_type = map->uniform_erase_type;
> >>>
> >>> - /* Do nothing if already configured from SFDP. */
> >>> - if (mtd->erasesize)
> >>> - return 0;
> >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> >>> + if (!(uniform_erase_type & BIT(i)))
> >>> + continue;
> >>> +
> >>> + tested_erase = &map->erase_type[i];
> >>> +
> >>> + /*
> >>> + * If the current erase size is the one, stop here:
> >>> + * we have found the right uniform Sector Erase command.
> >>> + */
> >>> + if (tested_erase->size == wanted_size) {
> >>> + erase = tested_erase;
> >>> + break;
> >>> + }
> >>>
> >>> + /*
> >>> + * Otherwise, the current erase size is still a valid canditate.
> >>> + * Select the biggest valid candidate.
> >>> + */
> >>> + if (!erase && tested_erase->size)
> >>> + erase = tested_erase;
> >>> + /* keep iterating to find the wanted_size */
> >>> + }
> >>> +
> >>> + if (!erase)
> >>> + return NULL;
> >>> +
> >>> + /* Disable all other Sector Erase commands. */
> >>> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
> >>> + map->uniform_erase_type |= BIT(erase - map->erase_type);
> >>> + return erase;
> >>> +}
> >>> +
> >>> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
> >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> >>> + const struct spi_nor_erase_type *erase = NULL;
> >>> + struct mtd_info *mtd = &nor->mtd;
> >>> + int i;
> >>> +
> >>> + /*
> >>> + * The previous implementation handling Sector Erase commands
> >>> assumed
> >>> + * that the SPI flash memory has an uniform layout then used only one
> >>> + * of the supported erase sizes for all Sector Erase commands.
> >>> + * So to be backward compatible, the new implementation also tries to
> >>> + * manage the SPI flash memory as uniform with a single erase sector
> >>> + * size, when possible.
> >>> + */
> >>> #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
> >>> + wanted_size = 4096u;
> >>> #endif
> >>> - {
> >>> - nor->erase_opcode = SPINOR_OP_SE;
> >>> - mtd->erasesize = info->sector_size;
> >>> +
> >>> + if (spi_nor_has_uniform_erase(nor)) {
> >>> + erase = spi_nor_select_uniform_erase(map, wanted_size);
> >>> + if (!erase)
> >>> + return -EINVAL;
> >>> + nor->erase_opcode = erase->opcode;
> >>> + mtd->erasesize = erase->size;
> >>> + return 0;
> >>> }
> >>> +
> >>> + /*
> >>> + * For non-uniform SPI flash memory, set mtd->erasesize to the
> >>> + * maximum erase sector size. No need to set nor->erase_opcode.
> >>> + */
> >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> >>> + if (map->erase_type[i].size) {
> >>> + erase = &map->erase_type[i];
> >>> + break;
> >>> + }
> >>> + }
> >>> +
> >>> + if (!erase)
> >>> + return -EINVAL;
> >>> +
> >>> + mtd->erasesize = erase->size;
> >>> return 0;
> >>> }
> >>>
> >>> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor,
> >>> const struct flash_info *info,
> >>> }
> >>>
> >>> /* Select the Sector Erase command. */
> >>> - err = spi_nor_select_erase(nor, info);
> >>> + err = spi_nor_select_erase(nor, info->sector_size);
> >>> if (err) {
> >>> dev_err(nor->dev,
> >>> "can't select erase settings supported by both the SPI
> >>> controller and memory.\n"); diff --git a/include/linux/mtd/spi-nor.h
> >>> b/include/linux/mtd/spi-nor.h index 09a10fd..a873a0b 100644
> >>> --- a/include/linux/mtd/spi-nor.h
> >>> +++ b/include/linux/mtd/spi-nor.h
> >>> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
> >>>
> >>> /**
> >>> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
> >>> + * @size: the size of the sector/block erased by the erase type.
> >>> + * JEDEC JESD216B imposes erase sizes to be a power of 2.
> >>> + * @size_shift: @size is a power of 2, the shift is stored in
> >>> + * @size_shift.
> >>> + * @size_mask: the size mask based on @size_shift.
> >>> + * @opcode: the SPI command op code to erase the
> sector/block.
> >>> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
> >>> + * Table. It will be used to synchronize the supported
> >>> + * Erase Types with the ones identified in the SFDP
> >>> + * optional tables.
> >>> + */
> >>> +struct spi_nor_erase_type {
> >>> + u32 size;
> >>> + u32 size_shift;
> >>> + u32 size_mask;
> >>> + u8 opcode;
> >>> + u8 idx;
> >>> +};
> >>> +
> >>> +/**
> >>> + * struct spi_nor_erase_command - Used for non-uniform erases
> >>> + * The structure is used to describe a list of erase commands to be
> >>> +executed
> >>> + * once we validate that the erase can be performed. The elements
> >>> +in the list
> >>> + * are run-length encoded.
> >>> + * @list: for inclusion into the list of erase commands.
> >>> + * @count: how many times the same erase command
> should be
> >>> + * consecutively used.
> >>> + * @size: the size of the sector/block erased by the command.
> >>> + * @opcode: the SPI command op code to erase the
> sector/block.
> >>> + */
> >>> +struct spi_nor_erase_command {
> >>> + struct list_head list;
> >>> + u32 count;
> >>> + u32 size;
> >>> + u8 opcode;
> >>> +};
> >>> +
> >>> +/**
> >>> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase
> region
> >>> + * @offset: the offset in the data array of erase region
> start.
> >>> + * LSB bits are used as a bitmask encoding flags to
> >>> + * determine if this region is overlaid, if this region is
> >>> + * the last in the SPI NOR flash memory and to indicate
> >>> + * all the supported erase commands inside this region.
> >>> + * The erase types are sorted in ascending order with the
> >>> + * smallest Erase Type size being at BIT(0).
> >>> + * @size: the size of the region in bytes.
> >>> + */
> >>> +struct spi_nor_erase_region {
> >>> + u64 offset;
> >>> + u64 size;
> >>> +};
> >>> +
> >>> +#define SNOR_ERASE_TYPE_MAX 4
> >>> +#define SNOR_ERASE_TYPE_MASK
> >>> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
> >>> +
> >>> +#define SNOR_LAST_REGION BIT(4)
> >>> +#define SNOR_OVERLAID_REGION BIT(5)
> >>> +
> >>> +#define SNOR_ERASE_FLAGS_MAX 6
> >>> +#define SNOR_ERASE_FLAGS_MASK
> >>> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
> >>> +
> >>> +/**
> >>> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
> >>> + * @regions: array of erase regions. The regions are
> consecutive in
> >>> + * address space. Walking through the regions is done
> >>> + * incrementally.
> >>> + * @uniform_region: a pre-allocated erase region for SPI NOR with a
> uniform
> >>> + * sector size (legacy implementation).
> >>> + * @erase_type: an array of erase types shared by all the regions.
> >>> + * The erase types are sorted in ascending order, with the
> >>> + * smallest Erase Type size being the first member in the
> >>> + * erase_type array.
> >>> + * @uniform_erase_type: bitmask encoding erase types that can erase
> >>> the
> >>> + * entire memory. This member is completed at init by
> >>> + * uniform and non-uniform SPI NOR flash memories if
> >>> they
> >>> + * support at least one erase type that can erase the
> >>> + * entire memory.
> >>> + */
> >>> +struct spi_nor_erase_map {
> >>> + struct spi_nor_erase_region *regions;
> >>> + struct spi_nor_erase_region uniform_region;
> >>> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
> >>> + u8 uniform_erase_type;
> >>> +};
> >>> +
> >>> +/**
> >>> * struct flash_info - Forward declaration of a structure used internally by
> >>> * spi_nor_scan()
> >>> */
> >>> @@ -263,6 +351,7 @@ struct flash_info;
> >>> * @write_proto: the SPI protocol for write operations
> >>> * @reg_proto the SPI protocol for read_reg/write_reg/erase
> >>> operations
> >>> * @cmd_buf: used by the write_reg
> >>> + * @erase_map: the erase map of the SPI NOR
> >>> * @prepare: [OPTIONAL] do some preparations for the
> >>> * read/write/erase/lock/unlock operations
> >>> * @unprepare: [OPTIONAL] do some post work after the
> >>> @@ -298,6 +387,7 @@ struct spi_nor {
> >>> bool sst_write_second;
> >>> u32 flags;
> >>> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> >>> + struct spi_nor_erase_map erase_map;
> >>>
> >>> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
> >>> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@ -
> >>> 318,6 +408,23 @@ struct spi_nor {
> >>> void *priv;
> >>> };
> >>>
> >>> +static u64 __maybe_unused
> >>> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
> >>> + return region->offset & SNOR_LAST_REGION; }
> >>> +
> >>> +static u64 __maybe_unused
> >>> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
> >>> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; }
> >>> +
> >>> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
> >>> +spi_nor *nor) {
> >>> + return !!nor->erase_map.uniform_erase_type;
> >>> +}
> >>> +
> >>> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
> >>> struct device_node *np)
> >>> {
> >>> --
> >>> 2.9.4
> >>>
> >>>
> >>> ______________________________________________________
> >>> Linux MTD discussion mailing list
> >>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fli
> >>> sts.infr
> >>> adead.org%2Fmailman%2Flistinfo%2Flinux-
> >>>
> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
> >>>
> 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
> >>>
> 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
> >>> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0
> >>
> >
> > ______________________________________________________
> > Linux MTD discussion mailing list
> > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flist
> > s.infradead.org%2Fmailman%2Flistinfo%2Flinux-
> mtd%2F&amp;data=02%7C01%7
> >
> Cyogeshnarayan.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%
> 7C686
> >
> ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;s
> data=0
> >
> vdEcONHlufYQW%2BD7K6lVaPByXMuDH5YAyx%2FE%2FC3eno%3D&amp;reserv
> ed=0
> >

2018-10-17 03:55:03

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Tudor,

> -----Original Message-----
> From: Yogesh Narayan Gaur
> Sent: Wednesday, October 17, 2018 7:38 AM
> To: 'Cyrille Pitchen' <[email protected]>; Tudor Ambarus
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> Hi Tudor,
>
> > -----Original Message-----
> > From: Cyrille Pitchen [mailto:[email protected]]
> > Sent: Tuesday, October 16, 2018 10:04 PM
> > To: Tudor Ambarus <[email protected]>; Yogesh Narayan Gaur
> > <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]
> > Cc: [email protected]; [email protected];
> > [email protected]; [email protected];
> > linux-arm- [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > SFDP SPI NOR flash memories
> >
> > Hi Tudor,
> >
> > Le 16/10/2018 à 17:14, Tudor Ambarus a écrit :
> > > Hi, Yogesh,
> > >
> > > On 10/16/2018 12:51 PM, Yogesh Narayan Gaur wrote:
> > >> Hi Tudor,
> > >>
> > >> This patch is breaking the 1-4-4 Read protocol for the spansion
> > >> flash
> > "s25fl512s".
> > >>
> > >> Without this patch read request command for Quad mode, 4-byte
> > >> enable, is
> > coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
> > >> But after applying this patch, read request command for Quad mode
> > >> is
> > coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
> > >>
> > >> This flash also supports non-uniform erase.
> > >> Can you please check and provide some suggestion?
> > >
> > > I don't have this memory to test it, but I'll try to help.
> > >
> > > Does s25fl512s support non-uniform erase? I'm looking in
> > > datasheet[1] at JEDEC BFPT table, dwords 8 and 9, page 132/146 and
> > > it looks like it supports just 256KB uniform erase.
> > >
> >
> Actually there is no entry of s25fs512s in current spi-nor.c file.
> For my connected flash part, jedec ID read points to s25fl512s. I have asked my
> board team to confirm the name of exact connected flash part.

Cypress connected flash part on my target is S25FS512SAGBHV210.

--
Regards
Yogesh Gaur

> When I check the data sheet of s25fs512s, it also points to the same Jedec ID
> information.
> { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, ....}
>
> But as stated earlier, if I skip reading SFDP or read using 1-1-1 protocol then read
> are always correct.
> For 1-4-4 protocol read are wrong and on further debugging found that Read
> code of 0x6C is being send as opcode instead of 0xEC.
>
> If I revert this patch, reads are working fine.
>
> --
> Regards
> Yogesh Gaur
>
> > s25fS512s supports both uniform and non uniform erase options but
> > s25fL512s is always uniform. L is an old memory part, S is newer.
> >
> > Also, the 8th and 9th WORDs of the Basic Flash Parameter Table alone
> > can't tell you whether or not the memory part can be non uniform.
> > If the memory can be non uniform then the sector erase map table is
> > mandatory, hence when the table is missing you know that your memory
> > part is always uniform.
> >
> > Best regards,
> >
> > Cyrille
> >
> > > Thanks,
> > > ta
> > >
> > > [1]
> > >
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> > >
> >
> cypress.com%2Ffile%2F177971%2Fdownload&amp;data=02%7C01%7Cyogeshn
> > araya
> > >
> >
> n.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%7C686ea1d3bc2
> > b4c6f
> > >
> >
> a92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;sdata=cioC98EH
> > OGlFbg
> > > XPhoIIJ72K3JrNUnzA1pYhSB9jDwg%3D&amp;reserved=0
> > >
> > >>
> > >> --
> > >> Regards
> > >> Yogesh Gaur
> > >>
> > >>> -----Original Message-----
> > >>> From: linux-mtd [mailto:[email protected]] On
> > >>> Behalf Of Tudor Ambarus
> > >>> Sent: Tuesday, September 11, 2018 9:10 PM
> > >>> To: [email protected]; [email protected];
> > >>> [email protected]; [email protected];
> > >>> [email protected]
> > >>> Cc: Tudor Ambarus <[email protected]>; linux-
> > >>> [email protected]; [email protected];
> > >>> [email protected]; [email protected];
> > >>> linux-arm- [email protected];
> > >>> [email protected]
> > >>> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > >>> SFDP SPI NOR flash memories
> > >>>
> > >>> Based on Cyrille Pitchen's patch
> > >>> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2
> > >>> Fl
> > >>> kml.or
> > >>>
> >
> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
> > >>>
> >
> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
> > >>>
> > c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
> > >>>
> > 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
> > >>>
> > >>> This patch is a transitional patch in introducing the support of
> > >>> SFDP SPI memories with non-uniform erase sizes like Spansion s25fs512s.
> > >>> Non-uniform erase maps will be used later when initialized based
> > >>> on the SFDP data.
> > >>>
> > >>> Introduce the memory erase map which splits the memory array into
> > >>> one or many erase regions. Each erase region supports up to 4
> > >>> erase types, as defined by the JEDEC JESD216B (SFDP) specification.
> > >>>
> > >>> To be backward compatible, the erase map of uniform SPI NOR flash
> > >>> memories is initialized so it contains only one erase region and
> > >>> this erase region supports only one erase command. Hence a single
> > >>> size is used to erase any sector/block of the memory.
> > >>>
> > >>> Besides, since the algorithm used to erase sectors on non-uniform
> > >>> SPI NOR flash memories is quite expensive, when possible, the
> > >>> erase map is tuned to come back to the uniform case.
> > >>>
> > >>> The 'erase with the best command, move forward and repeat'
> > >>> approach was suggested by Cristian Birsan in a brainstorm session, so:
> > >>>
> > >>> Suggested-by: Cristian Birsan <[email protected]>
> > >>> Signed-off-by: Tudor Ambarus <[email protected]>
> > >>> ---
> > >>> drivers/mtd/spi-nor/spi-nor.c | 594
> > >>> +++++++++++++++++++++++++++++++++++++++---
> > >>> include/linux/mtd/spi-nor.h | 107 ++++++++
> > >>> 2 files changed, 659 insertions(+), 42 deletions(-)
> > >>>
> > >>> diff --git a/drivers/mtd/spi-nor/spi-nor.c
> > >>> b/drivers/mtd/spi-nor/spi-nor.c index
> > >>> dc8757e..4687345 100644
> > >>> --- a/drivers/mtd/spi-nor/spi-nor.c
> > >>> +++ b/drivers/mtd/spi-nor/spi-nor.c
> > >>> @@ -18,6 +18,7 @@
> > >>> #include <linux/math64.h>
> > >>> #include <linux/sizes.h>
> > >>> #include <linux/slab.h>
> > >>> +#include <linux/sort.h>
> > >>>
> > >>> #include <linux/mtd/mtd.h>
> > >>> #include <linux/of_platform.h>
> > >>> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct
> > >>> spi_nor *nor,
> > >>> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
> > >>> nor->program_opcode = spi_nor_convert_3to4_program(nor-
> > >>>> program_opcode);
> > >>> nor->erase_opcode =
> > >>> spi_nor_convert_3to4_erase(nor->erase_opcode);
> > >>> +
> > >>> + if (!spi_nor_has_uniform_erase(nor)) {
> > >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> > >>> + struct spi_nor_erase_type *erase;
> > >>> + int i;
> > >>> +
> > >>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
> > >>> + erase = &map->erase_type[i];
> > >>> + erase->opcode =
> > >>> + spi_nor_convert_3to4_erase(erase->opcode);
> > >>> + }
> > >>> + }
> > >>> }
> > >>>
> > >>> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275
> > >>> @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
> > >>> }
> > >>>
> > >>> /*
> > >>> + * spi_nor_div_by_erase_size() - calculate remainder and update
> > >>> + new
> > dividend
> > >>> + * @erase: pointer to a structure that describes a SPI NOR erase
> type
> > >>> + * @dividend: dividend value
> > >>> + * @remainder: pointer to u32 remainder (will be updated)
> > >>> + *
> > >>> + * Returns two values: remainder and the new dividend */ static
> > >>> +u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
> > >>> + u64 dividend, u32 *remainder) {
> > >>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> > >>> + *remainder = (u32)dividend & erase->size_mask;
> > >>> + return dividend >> erase->size_shift; }
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_find_best_erase_type() - find the best erase type for
> > >>> +the given
> > >>> + * offset in the serial flash memory and the number of bytes to erase.
> > >>> +The
> > >>> + * region in which the address fits is expected to be provided.
> > >>> + * @map: the erase map of the SPI NOR
> > >>> + * @region: pointer to a structure that describes a SPI NOR erase
> > region
> > >>> + * @addr: offset in the serial flash memory
> > >>> + * @len: number of bytes to erase
> > >>> + *
> > >>> + * Returns a pointer to the best fitted erase type, NULL otherwise.
> > >>> + */
> > >>> +static const struct spi_nor_erase_type *
> > >>> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
> > >>> + const struct spi_nor_erase_region *region,
> > >>> + u64 addr, u32 len)
> > >>> +{
> > >>> + const struct spi_nor_erase_type *erase;
> > >>> + u32 rem;
> > >>> + int i;
> > >>> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
> > >>> +
> > >>> + /*
> > >>> + * Erase types are ordered by size, with the biggest erase type at
> > >>> + * index 0.
> > >>> + */
> > >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> > >>> + /* Does the erase region support the tested erase type? */
> > >>> + if (!(erase_mask & BIT(i)))
> > >>> + continue;
> > >>> +
> > >>> + erase = &map->erase_type[i];
> > >>> +
> > >>> + /* Don't erase more than what the user has asked for. */
> > >>> + if (erase->size > len)
> > >>> + continue;
> > >>> +
> > >>> + /* Alignment is not mandatory for overlaid regions */
> > >>> + if (region->offset & SNOR_OVERLAID_REGION)
> > >>> + return erase;
> > >>> +
> > >>> + spi_nor_div_by_erase_size(erase, addr, &rem);
> > >>> + if (rem)
> > >>> + continue;
> > >>> + else
> > >>> + return erase;
> > >>> + }
> > >>> +
> > >>> + return NULL;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_region_next() - get the next spi nor region
> > >>> + * @region: pointer to a structure that describes a SPI NOR erase
> > region
> > >>> + *
> > >>> + * Returns the next spi nor region or NULL if last region.
> > >>> + */
> > >>> +static struct spi_nor_erase_region * spi_nor_region_next(struct
> > >>> +spi_nor_erase_region *region) {
> > >>> + if (spi_nor_region_is_last(region))
> > >>> + return NULL;
> > >>> + region++;
> > >>> + return region;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_find_erase_region() - find the region of the serial
> > >>> +flash memory in
> > >>> + * which the offset fits
> > >>> + * @map: the erase map of the SPI NOR
> > >>> + * @addr: offset in the serial flash memory
> > >>> + *
> > >>> + * Returns pointer to the spi_nor_erase_region struct,
> > >>> +ERR_PTR(-errno)
> > >>> + * otherwise.
> > >>> + */
> > >>> +static struct spi_nor_erase_region *
> > >>> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map,
> > >>> +u64
> > >>> +addr) {
> > >>> + struct spi_nor_erase_region *region = map->regions;
> > >>> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> > >>> + u64 region_end = region_start + region->size;
> > >>> +
> > >>> + while (addr < region_start || addr >= region_end) {
> > >>> + region = spi_nor_region_next(region);
> > >>> + if (!region)
> > >>> + return ERR_PTR(-EINVAL);
> > >>> +
> > >>> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
> > >>> + region_end = region_start + region->size;
> > >>> + }
> > >>> +
> > >>> + return region;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_init_erase_cmd() - initialize an erase command
> > >>> + * @region: pointer to a structure that describes a SPI NOR erase
> > region
> > >>> + * @erase: pointer to a structure that describes a SPI NOR erase
> type
> > >>> + *
> > >>> + * Returns the pointer to the allocated erase command,
> > >>> +ERR_PTR(-errno)
> > >>> + * otherwise.
> > >>> + */
> > >>> +static struct spi_nor_erase_command *
> > >>> +spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
> > >>> + const struct spi_nor_erase_type *erase) {
> > >>> + struct spi_nor_erase_command *cmd;
> > >>> +
> > >>> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
> > >>> + if (!cmd)
> > >>> + return ERR_PTR(-ENOMEM);
> > >>> +
> > >>> + INIT_LIST_HEAD(&cmd->list);
> > >>> + cmd->opcode = erase->opcode;
> > >>> + cmd->count = 1;
> > >>> +
> > >>> + if (region->offset & SNOR_OVERLAID_REGION)
> > >>> + cmd->size = region->size;
> > >>> + else
> > >>> + cmd->size = erase->size;
> > >>> +
> > >>> + return cmd;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
> > >>> + * @erase_list: list of erase commands
> > >>> + */
> > >>> +static void spi_nor_destroy_erase_cmd_list(struct list_head
> > >>> +*erase_list) {
> > >>> + struct spi_nor_erase_command *cmd, *next;
> > >>> +
> > >>> + list_for_each_entry_safe(cmd, next, erase_list, list) {
> > >>> + list_del(&cmd->list);
> > >>> + kfree(cmd);
> > >>> + }
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_init_erase_cmd_list() - initialize erase command list
> > >>> + * @nor: pointer to a 'struct spi_nor'
> > >>> + * @erase_list: list of erase commands to be executed once we
> > validate that
> > >>> the
> > >>> + * erase can be performed
> > >>> + * @addr: offset in the serial flash memory
> > >>> + * @len: number of bytes to erase
> > >>> + *
> > >>> + * Builds the list of best fitted erase commands and verifies if
> > >>> +the erase can
> > >>> + * be performed.
> > >>> + *
> > >>> + * Returns 0 on success, -errno otherwise.
> > >>> + */
> > >>> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
> > >>> + struct list_head *erase_list,
> > >>> + u64 addr, u32 len)
> > >>> +{
> > >>> + const struct spi_nor_erase_map *map = &nor->erase_map;
> > >>> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
> > >>> + struct spi_nor_erase_region *region;
> > >>> + struct spi_nor_erase_command *cmd = NULL;
> > >>> + u64 region_end;
> > >>> + int ret = -EINVAL;
> > >>> +
> > >>> + region = spi_nor_find_erase_region(map, addr);
> > >>> + if (IS_ERR(region))
> > >>> + return PTR_ERR(region);
> > >>> +
> > >>> + region_end = spi_nor_region_end(region);
> > >>> +
> > >>> + while (len) {
> > >>> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
> > >>> + if (!erase)
> > >>> + goto destroy_erase_cmd_list;
> > >>> +
> > >>> + if (prev_erase != erase ||
> > >>> + region->offset & SNOR_OVERLAID_REGION) {
> > >>> + cmd = spi_nor_init_erase_cmd(region, erase);
> > >>> + if (IS_ERR(cmd)) {
> > >>> + ret = PTR_ERR(cmd);
> > >>> + goto destroy_erase_cmd_list;
> > >>> + }
> > >>> +
> > >>> + list_add_tail(&cmd->list, erase_list);
> > >>> + } else {
> > >>> + cmd->count++;
> > >>> + }
> > >>> +
> > >>> + addr += cmd->size;
> > >>> + len -= cmd->size;
> > >>> +
> > >>> + if (len && addr >= region_end) {
> > >>> + region = spi_nor_region_next(region);
> > >>> + if (!region)
> > >>> + goto destroy_erase_cmd_list;
> > >>> + region_end = spi_nor_region_end(region);
> > >>> + }
> > >>> +
> > >>> + prev_erase = erase;
> > >>> + }
> > >>> +
> > >>> + return 0;
> > >>> +
> > >>> +destroy_erase_cmd_list:
> > >>> + spi_nor_destroy_erase_cmd_list(erase_list);
> > >>> + return ret;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
> > >>> + * @nor: pointer to a 'struct spi_nor'
> > >>> + * @addr: offset in the serial flash memory
> > >>> + * @len: number of bytes to erase
> > >>> + *
> > >>> + * Build a list of best fitted erase commands and execute it once
> > >>> +we
> > >>> + * validate that the erase can be performed.
> > >>> + *
> > >>> + * Returns 0 on success, -errno otherwise.
> > >>> + */
> > >>> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64
> > >>> +addr,
> > >>> +u32 len) {
> > >>> + LIST_HEAD(erase_list);
> > >>> + struct spi_nor_erase_command *cmd, *next;
> > >>> + int ret;
> > >>> +
> > >>> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
> > >>> + if (ret)
> > >>> + return ret;
> > >>> +
> > >>> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
> > >>> + nor->erase_opcode = cmd->opcode;
> > >>> + while (cmd->count) {
> > >>> + write_enable(nor);
> > >>> +
> > >>> + ret = spi_nor_erase_sector(nor, addr);
> > >>> + if (ret)
> > >>> + goto destroy_erase_cmd_list;
> > >>> +
> > >>> + addr += cmd->size;
> > >>> + cmd->count--;
> > >>> +
> > >>> + ret = spi_nor_wait_till_ready(nor);
> > >>> + if (ret)
> > >>> + goto destroy_erase_cmd_list;
> > >>> + }
> > >>> + list_del(&cmd->list);
> > >>> + kfree(cmd);
> > >>> + }
> > >>> +
> > >>> + return 0;
> > >>> +
> > >>> +destroy_erase_cmd_list:
> > >>> + spi_nor_destroy_erase_cmd_list(&erase_list);
> > >>> + return ret;
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> * Erase an address range on the nor chip. The address range may extend
> > >>> * one or more erase sectors. Return an error is there is a problem
> erasing.
> > >>> */
> > >>> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info
> > >>> *mtd, struct erase_info *instr)
> > >>> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
> > >>> (long long)instr->len);
> > >>>
> > >>> - div_u64_rem(instr->len, mtd->erasesize, &rem);
> > >>> - if (rem)
> > >>> - return -EINVAL;
> > >>> + if (spi_nor_has_uniform_erase(nor)) {
> > >>> + div_u64_rem(instr->len, mtd->erasesize, &rem);
> > >>> + if (rem)
> > >>> + return -EINVAL;
> > >>> + }
> > >>>
> > >>> addr = instr->addr;
> > >>> len = instr->len;
> > >>> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd,
> > >>> struct erase_info *instr)
> > >>> */
> > >>>
> > >>> /* "sector"-at-a-time erase */
> > >>> - } else {
> > >>> + } else if (spi_nor_has_uniform_erase(nor)) {
> > >>> while (len) {
> > >>> write_enable(nor);
> > >>>
> > >>> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info
> > >>> *mtd, struct erase_info *instr)
> > >>> if (ret)
> > >>> goto erase_err;
> > >>> }
> > >>> +
> > >>> + /* erase multiple sectors */
> > >>> + } else {
> > >>> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
> > >>> + if (ret)
> > >>> + goto erase_err;
> > >>> }
> > >>>
> > >>> write_disable(nor);
> > >>> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
> > >>> sfdp_bfpt_erases[] = {
> > >>>
> > >>> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
> > >>>
> > >>> +/*
> > >>> + * spi_nor_set_erase_type() - set a SPI NOR erase type
> > >>> + * @erase: pointer to a structure that describes a SPI NOR erase
> type
> > >>> + * @size: the size of the sector/block erased by the erase type
> > >>> + * @opcode: the SPI command op code to erase the sector/block
> > >>> + */
> > >>> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
> > >>> + u32 size, u8 opcode)
> > >>> +{
> > >>> + erase->size = size;
> > >>> + erase->opcode = opcode;
> > >>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
> > >>> + erase->size_shift = ffs(erase->size) - 1;
> > >>> + erase->size_mask = (1 << erase->size_shift) - 1; }
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_set_erase_settings_from_bfpt() - set erase type
> > >>> +settings from
> > BFPT
> > >>> + * @erase: pointer to a structure that describes a SPI NOR erase
> type
> > >>> + * @size: the size of the sector/block erased by the erase type
> > >>> + * @opcode: the SPI command op code to erase the sector/block
> > >>> + * @i: erase type index as sorted in the Basic Flash Parameter
> > Table
> > >>> + *
> > >>> + * The supported Erase Types will be sorted at init in ascending
> > >>> +order, with
> > >>> + * the smallest Erase Type size being the first member in the
> > >>> +erase_type array
> > >>> + * of the spi_nor_erase_map structure. Save the Erase Type index
> > >>> +as sorted in
> > >>> + * the Basic Flash Parameter Table since it will be used later on
> > >>> +to
> > >>> + * synchronize with the supported Erase Types defined in SFDP
> > >>> +optional
> > tables.
> > >>> + */
> > >>> +static void
> > >>> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
> > >>> + u32 size, u8 opcode, u8 i) {
> > >>> + erase->idx = i;
> > >>> + spi_nor_set_erase_type(erase, size, opcode); }
> > >>> +
> > >>> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by
> size
> > >>> + * @l: member in the left half of the map's erase_type array
> > >>> + * @r: member in the right half of the map's erase_type array
> > >>> + *
> > >>> + * Comparison function used in the sort() call to sort in
> > >>> +ascending order the
> > >>> + * map's erase types, the smallest erase type size being the
> > >>> +first member in the
> > >>> + * sorted erase_type array.
> > >>> + */
> > >>> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
> > >>> + const struct spi_nor_erase_type *left = l, *right = r;
> > >>> +
> > >>> + return left->size - right->size; }
> > >>> +
> > >>> +/*
> > >>> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
> > >>> + * @map: the erase map of the SPI NOR
> > >>> + *
> > >>> + * Function assumes that the erase types defined in the erase map
> > >>> +are already
> > >>> + * sorted in ascending order, with the smallest erase type size
> > >>> +being the first
> > >>> + * member in the erase_type array. It replicates the sort done
> > >>> +for the map's
> > >>> + * erase types. Each region's erase bitmask will indicate which
> > >>> +erase types are
> > >>> + * supported from the sorted erase types defined in the erase map.
> > >>> + * Sort the all region's erase type at init in order to speed up
> > >>> +the process of
> > >>> + * finding the best erase command at runtime.
> > >>> + */
> > >>> +static void spi_nor_regions_sort_erase_types(struct
> > >>> +spi_nor_erase_map
> > >>> +*map) {
> > >>> + struct spi_nor_erase_region *region = map->regions;
> > >>> + struct spi_nor_erase_type *erase_type = map->erase_type;
> > >>> + int i;
> > >>> + u8 region_erase_mask, sorted_erase_mask;
> > >>> +
> > >>> + while (region) {
> > >>> + region_erase_mask = region->offset &
> > >>> SNOR_ERASE_TYPE_MASK;
> > >>> +
> > >>> + /* Replicate the sort done for the map's erase types. */
> > >>> + sorted_erase_mask = 0;
> > >>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
> > >>> + if (erase_type[i].size &&
> > >>> + region_erase_mask & BIT(erase_type[i].idx))
> > >>> + sorted_erase_mask |= BIT(i);
> > >>> +
> > >>> + /* Overwrite erase mask. */
> > >>> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
> > >>> |
> > >>> + sorted_erase_mask;
> > >>> +
> > >>> + region = spi_nor_region_next(region);
> > >>> + }
> > >>> +}
> > >>> +
> > >>> +/*
> > >>> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
> > >>> + * @map: the erase map of the SPI NOR
> > >>> + * @erase_mask: bitmask encoding erase types that can erase
> > >>> the entire
> > >>> + * flash memory
> > >>> + * @flash_size: the spi nor flash memory size
> > >>> + */
> > >>> +static void spi_nor_init_uniform_erase_map(struct
> > >>> +spi_nor_erase_map
> > *map,
> > >>> + u8 erase_mask, u64 flash_size) {
> > >>> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
> > >>> + map->uniform_region.offset = (erase_mask &
> > >>> SNOR_ERASE_TYPE_MASK) |
> > >>> + SNOR_LAST_REGION;
> > >>> + map->uniform_region.size = flash_size;
> > >>> + map->regions = &map->uniform_region;
> > >>> + map->uniform_erase_type = erase_mask; }
> > >>> +
> > >>> /**
> > >>> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
> > >>> * @nor: pointer to a 'struct spi_nor'
> > >>> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct
> > >>> spi_nor
> > *nor,
> > >>> const struct sfdp_parameter_header *bfpt_header,
> > >>> struct spi_nor_flash_parameter *params) {
> > >>> - struct mtd_info *mtd = &nor->mtd;
> > >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> > >>> + struct spi_nor_erase_type *erase_type = map->erase_type;
> > >>> struct sfdp_bfpt bfpt;
> > >>> size_t len;
> > >>> int i, cmd, err;
> > >>> u32 addr;
> > >>> u16 half;
> > >>> + u8 erase_mask;
> > >>>
> > >>> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
> > >>> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
> > >>> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
> > >>> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
> > >>> }
> > >>>
> > >>> - /* Sector Erase settings. */
> > >>> + /*
> > >>> + * Sector Erase settings. Reinitialize the uniform erase map using the
> > >>> + * Erase Types defined in the bfpt table.
> > >>> + */
> > >>> + erase_mask = 0;
> > >>> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
> > >>> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
> > >>> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
> > >>> u32 erasesize;
> > >>> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct
> > >>> spi_nor *nor,
> > >>>
> > >>> erasesize = 1U << erasesize;
> > >>> opcode = (half >> 8) & 0xff;
> > >>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> > >>> - if (erasesize == SZ_4K) {
> > >>> - nor->erase_opcode = opcode;
> > >>> - mtd->erasesize = erasesize;
> > >>> - break;
> > >>> - }
> > >>> -#endif
> > >>> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
> > >>> - nor->erase_opcode = opcode;
> > >>> - mtd->erasesize = erasesize;
> > >>> - }
> > >>> + erase_mask |= BIT(i);
> > >>> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
> > >>> erasesize,
> > >>> + opcode, i);
> > >>> }
> > >>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> > >>> + /*
> > >>> + * Sort all the map's Erase Types in ascending order with the smallest
> > >>> + * erase size being the first member in the erase_type array.
> > >>> + */
> > >>> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
> > >>> + spi_nor_map_cmp_erase_type, NULL);
> > >>> + /*
> > >>> + * Sort the erase types in the uniform region in order to update the
> > >>> + * uniform_erase_type bitmask. The bitmask will be used later on when
> > >>> + * selecting the uniform erase.
> > >>> + */
> > >>> + spi_nor_regions_sort_erase_types(map);
> > >>> + map->uniform_erase_type = map->uniform_region.offset &
> > >>> + SNOR_ERASE_TYPE_MASK;
> > >>>
> > >>> /* Stop here if not JESD216 rev A or later. */
> > >>> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
> > @@
> > >>> static int spi_nor_init_params(struct spi_nor *nor,
> > >>> const struct flash_info *info,
> > >>> struct spi_nor_flash_parameter *params) {
> > >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> > >>> + u8 i, erase_mask;
> > >>> +
> > >>> /* Set legacy flash parameters as default. */
> > >>> memset(params, 0, sizeof(*params));
> > >>>
> > >>> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct
> > >>> spi_nor
> > *nor,
> > >>> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
> > >>> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
> > >>>
> > >>> + /*
> > >>> + * Sector Erase settings. Sort Erase Types in ascending order, with the
> > >>> + * smallest erase size starting at BIT(0).
> > >>> + */
> > >>> + erase_mask = 0;
> > >>> + i = 0;
> > >>> + if (info->flags & SECT_4K_PMC) {
> > >>> + erase_mask |= BIT(i);
> > >>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> > >>> + SPINOR_OP_BE_4K_PMC);
> > >>> + i++;
> > >>> + } else if (info->flags & SECT_4K) {
> > >>> + erase_mask |= BIT(i);
> > >>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
> > >>> + SPINOR_OP_BE_4K);
> > >>> + i++;
> > >>> + }
> > >>> + erase_mask |= BIT(i);
> > >>> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
> > >>> + SPINOR_OP_SE);
> > >>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
> > >>> +
> > >>> /* Select the procedure to set the Quad Enable bit. */
> > >>> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> > >>> SNOR_HWCAPS_PP_QUAD)) {
> > >>> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct
> > >>> spi_nor
> > *nor,
> > >>> params->quad_enable = info->quad_enable;
> > >>> }
> > >>>
> > >>> - /* Override the parameters with data read from SFDP tables. */
> > >>> - nor->addr_width = 0;
> > >>> - nor->mtd.erasesize = 0;
> > >>> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
> > >>> !(info->flags & SPI_NOR_SKIP_SFDP)) {
> > >>> struct spi_nor_flash_parameter sfdp_params;
> > >>> + struct spi_nor_erase_map prev_map;
> > >>>
> > >>> memcpy(&sfdp_params, params, sizeof(sfdp_params));
> > >>> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
> > >>> - nor->addr_width = 0;
> > >>> - nor->mtd.erasesize = 0;
> > >>> - } else {
> > >>> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
> > >>> +
> > >>> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
> > >>> + /* restore previous erase map */
> > >>> + memcpy(&nor->erase_map, &prev_map,
> > >>> + sizeof(nor->erase_map));
> > >>> + else
> > >>> memcpy(params, &sfdp_params, sizeof(*params));
> > >>> - }
> > >>> }
> > >>>
> > >>> return 0;
> > >>> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct
> > >>> spi_nor
> > *nor,
> > >>> return 0;
> > >>> }
> > >>>
> > >>> -static int spi_nor_select_erase(struct spi_nor *nor,
> > >>> - const struct flash_info *info)
> > >>> +/*
> > >>> + * 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.
> > >>> + *
> > >>> + * Once the optimum uniform sector erase command is found,
> > >>> +disable all the
> > >>> + * other.
> > >>> + *
> > >>> + * Return: pointer to erase type on success, NULL otherwise.
> > >>> + */
> > >>> +static const struct spi_nor_erase_type *
> > >>> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
> > >>> + const u32 wanted_size)
> > >>> {
> > >>> - struct mtd_info *mtd = &nor->mtd;
> > >>> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
> > >>> + int i;
> > >>> + u8 uniform_erase_type = map->uniform_erase_type;
> > >>>
> > >>> - /* Do nothing if already configured from SFDP. */
> > >>> - if (mtd->erasesize)
> > >>> - return 0;
> > >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> > >>> + if (!(uniform_erase_type & BIT(i)))
> > >>> + continue;
> > >>> +
> > >>> + tested_erase = &map->erase_type[i];
> > >>> +
> > >>> + /*
> > >>> + * If the current erase size is the one, stop here:
> > >>> + * we have found the right uniform Sector Erase command.
> > >>> + */
> > >>> + if (tested_erase->size == wanted_size) {
> > >>> + erase = tested_erase;
> > >>> + break;
> > >>> + }
> > >>>
> > >>> + /*
> > >>> + * Otherwise, the current erase size is still a valid canditate.
> > >>> + * Select the biggest valid candidate.
> > >>> + */
> > >>> + if (!erase && tested_erase->size)
> > >>> + erase = tested_erase;
> > >>> + /* keep iterating to find the wanted_size */
> > >>> + }
> > >>> +
> > >>> + if (!erase)
> > >>> + return NULL;
> > >>> +
> > >>> + /* Disable all other Sector Erase commands. */
> > >>> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
> > >>> + map->uniform_erase_type |= BIT(erase - map->erase_type);
> > >>> + return erase;
> > >>> +}
> > >>> +
> > >>> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
> > >>> + struct spi_nor_erase_map *map = &nor->erase_map;
> > >>> + const struct spi_nor_erase_type *erase = NULL;
> > >>> + struct mtd_info *mtd = &nor->mtd;
> > >>> + int i;
> > >>> +
> > >>> + /*
> > >>> + * The previous implementation handling Sector Erase commands
> > >>> assumed
> > >>> + * that the SPI flash memory has an uniform layout then used only one
> > >>> + * of the supported erase sizes for all Sector Erase commands.
> > >>> + * So to be backward compatible, the new implementation also tries to
> > >>> + * manage the SPI flash memory as uniform with a single erase sector
> > >>> + * size, when possible.
> > >>> + */
> > >>> #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
> > >>> + wanted_size = 4096u;
> > >>> #endif
> > >>> - {
> > >>> - nor->erase_opcode = SPINOR_OP_SE;
> > >>> - mtd->erasesize = info->sector_size;
> > >>> +
> > >>> + if (spi_nor_has_uniform_erase(nor)) {
> > >>> + erase = spi_nor_select_uniform_erase(map, wanted_size);
> > >>> + if (!erase)
> > >>> + return -EINVAL;
> > >>> + nor->erase_opcode = erase->opcode;
> > >>> + mtd->erasesize = erase->size;
> > >>> + return 0;
> > >>> }
> > >>> +
> > >>> + /*
> > >>> + * For non-uniform SPI flash memory, set mtd->erasesize to the
> > >>> + * maximum erase sector size. No need to set nor->erase_opcode.
> > >>> + */
> > >>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
> > >>> + if (map->erase_type[i].size) {
> > >>> + erase = &map->erase_type[i];
> > >>> + break;
> > >>> + }
> > >>> + }
> > >>> +
> > >>> + if (!erase)
> > >>> + return -EINVAL;
> > >>> +
> > >>> + mtd->erasesize = erase->size;
> > >>> return 0;
> > >>> }
> > >>>
> > >>> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor
> > >>> *nor, const struct flash_info *info,
> > >>> }
> > >>>
> > >>> /* Select the Sector Erase command. */
> > >>> - err = spi_nor_select_erase(nor, info);
> > >>> + err = spi_nor_select_erase(nor, info->sector_size);
> > >>> if (err) {
> > >>> dev_err(nor->dev,
> > >>> "can't select erase settings supported by both the SPI
> > >>> controller and memory.\n"); diff --git
> > >>> a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index
> > >>> 09a10fd..a873a0b 100644
> > >>> --- a/include/linux/mtd/spi-nor.h
> > >>> +++ b/include/linux/mtd/spi-nor.h
> > >>> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
> > >>>
> > >>> /**
> > >>> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
> > >>> + * @size: the size of the sector/block erased by the erase type.
> > >>> + * JEDEC JESD216B imposes erase sizes to be a
> power of 2.
> > >>> + * @size_shift: @size is a power of 2, the shift is stored in
> > >>> + * @size_shift.
> > >>> + * @size_mask: the size mask based on @size_shift.
> > >>> + * @opcode: the SPI command op code to erase the
> > sector/block.
> > >>> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
> > >>> + * Table. It will be used to synchronize the
> supported
> > >>> + * Erase Types with the ones identified in the SFDP
> > >>> + * optional tables.
> > >>> + */
> > >>> +struct spi_nor_erase_type {
> > >>> + u32 size;
> > >>> + u32 size_shift;
> > >>> + u32 size_mask;
> > >>> + u8 opcode;
> > >>> + u8 idx;
> > >>> +};
> > >>> +
> > >>> +/**
> > >>> + * struct spi_nor_erase_command - Used for non-uniform erases
> > >>> + * The structure is used to describe a list of erase commands to
> > >>> +be executed
> > >>> + * once we validate that the erase can be performed. The elements
> > >>> +in the list
> > >>> + * are run-length encoded.
> > >>> + * @list: for inclusion into the list of erase commands.
> > >>> + * @count: how many times the same erase command
> > should be
> > >>> + * consecutively used.
> > >>> + * @size: the size of the sector/block erased by the command.
> > >>> + * @opcode: the SPI command op code to erase the
> > sector/block.
> > >>> + */
> > >>> +struct spi_nor_erase_command {
> > >>> + struct list_head list;
> > >>> + u32 count;
> > >>> + u32 size;
> > >>> + u8 opcode;
> > >>> +};
> > >>> +
> > >>> +/**
> > >>> + * struct spi_nor_erase_region - Structure to describe a SPI NOR
> > >>> +erase
> > region
> > >>> + * @offset: the offset in the data array of erase region
> > start.
> > >>> + * LSB bits are used as a bitmask encoding flags to
> > >>> + * determine if this region is overlaid, if this region
> is
> > >>> + * the last in the SPI NOR flash memory and to
> indicate
> > >>> + * all the supported erase commands inside this
> region.
> > >>> + * The erase types are sorted in ascending order
> with the
> > >>> + * smallest Erase Type size being at BIT(0).
> > >>> + * @size: the size of the region in bytes.
> > >>> + */
> > >>> +struct spi_nor_erase_region {
> > >>> + u64 offset;
> > >>> + u64 size;
> > >>> +};
> > >>> +
> > >>> +#define SNOR_ERASE_TYPE_MAX 4
> > >>> +#define SNOR_ERASE_TYPE_MASK
> > >>> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
> > >>> +
> > >>> +#define SNOR_LAST_REGION BIT(4)
> > >>> +#define SNOR_OVERLAID_REGION BIT(5)
> > >>> +
> > >>> +#define SNOR_ERASE_FLAGS_MAX 6
> > >>> +#define SNOR_ERASE_FLAGS_MASK
> > >>> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
> > >>> +
> > >>> +/**
> > >>> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase
> map
> > >>> + * @regions: array of erase regions. The regions are
> > consecutive in
> > >>> + * address space. Walking through the regions is
> done
> > >>> + * incrementally.
> > >>> + * @uniform_region: a pre-allocated erase region for SPI NOR with a
> > uniform
> > >>> + * sector size (legacy implementation).
> > >>> + * @erase_type: an array of erase types shared by all the regions.
> > >>> + * The erase types are sorted in ascending order,
> with the
> > >>> + * smallest Erase Type size being the first member
> in the
> > >>> + * erase_type array.
> > >>> + * @uniform_erase_type: bitmask encoding erase types that can
> erase
> > >>> the
> > >>> + * entire memory. This member is completed at
> init by
> > >>> + * uniform and non-uniform SPI NOR flash
> memories if
> > >>> they
> > >>> + * support at least one erase type that can erase
> the
> > >>> + * entire memory.
> > >>> + */
> > >>> +struct spi_nor_erase_map {
> > >>> + struct spi_nor_erase_region *regions;
> > >>> + struct spi_nor_erase_region uniform_region;
> > >>> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
> > >>> + u8 uniform_erase_type;
> > >>> +};
> > >>> +
> > >>> +/**
> > >>> * struct flash_info - Forward declaration of a structure used internally by
> > >>> * spi_nor_scan()
> > >>> */
> > >>> @@ -263,6 +351,7 @@ struct flash_info;
> > >>> * @write_proto: the SPI protocol for write operations
> > >>> * @reg_proto the SPI protocol for read_reg/write_reg/erase
> > >>> operations
> > >>> * @cmd_buf: used by the write_reg
> > >>> + * @erase_map: the erase map of the SPI NOR
> > >>> * @prepare: [OPTIONAL] do some preparations for the
> > >>> * read/write/erase/lock/unlock operations
> > >>> * @unprepare: [OPTIONAL] do some post work after the
> > >>> @@ -298,6 +387,7 @@ struct spi_nor {
> > >>> bool sst_write_second;
> > >>> u32 flags;
> > >>> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> > >>> + struct spi_nor_erase_map erase_map;
> > >>>
> > >>> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
> > >>> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@
> > >>> -
> > >>> 318,6 +408,23 @@ struct spi_nor {
> > >>> void *priv;
> > >>> };
> > >>>
> > >>> +static u64 __maybe_unused
> > >>> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
> > >>> + return region->offset & SNOR_LAST_REGION; }
> > >>> +
> > >>> +static u64 __maybe_unused
> > >>> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
> > >>> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
> > >>> +}
> > >>> +
> > >>> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
> > >>> +spi_nor *nor) {
> > >>> + return !!nor->erase_map.uniform_erase_type;
> > >>> +}
> > >>> +
> > >>> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
> > >>> struct device_node *np)
> > >>> {
> > >>> --
> > >>> 2.9.4
> > >>>
> > >>>
> > >>> ______________________________________________________
> > >>> Linux MTD discussion mailing list
> > >>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2F
> > >>> li
> > >>> sts.infr
> > >>> adead.org%2Fmailman%2Flistinfo%2Flinux-
> > >>>
> >
> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
> > >>>
> > 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
> > >>>
> > 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
> > >>> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0
> > >>
> > >
> > > ______________________________________________________
> > > Linux MTD discussion mailing list
> > > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fli
> > > st
> > > s.infradead.org%2Fmailman%2Flistinfo%2Flinux-
> > mtd%2F&amp;data=02%7C01%7
> > >
> > Cyogeshnarayan.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%
> > 7C686
> > >
> >
> ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;s
> > data=0
> > >
> >
> vdEcONHlufYQW%2BD7K6lVaPByXMuDH5YAyx%2FE%2FC3eno%3D&amp;reserv
> > ed=0
> > >

2018-10-17 07:08:07

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 02:07:43 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> >
> Actually there is no entry of s25fs512s in current spi-nor.c file.
> For my connected flash part, jedec ID read points to s25fl512s. I
> have asked my board team to confirm the name of exact connected flash
> part. When I check the data sheet of s25fs512s, it also points to the
> same Jedec ID information. { "s25fl512s", INFO(0x010220, 0x4d00, 256
> * 1024, 256, ....}
>
> But as stated earlier, if I skip reading SFDP or read using 1-1-1
> protocol then read are always correct. For 1-4-4 protocol read are
> wrong and on further debugging found that Read code of 0x6C is being
> send as opcode instead of 0xEC.
>
> If I revert this patch, reads are working fine.

Can you try with the following patch?

Also, can you add a trace to check whether you're reaching this point
[1] or not.

[1]https://elixir.bootlin.com/linux/v4.19-rc8/source/drivers/mtd/spi-nor/spi-nor.c#L2227

--->8---
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9407ca5f9443..49278c1491a6 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2643,6 +2643,8 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
break;

case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:
+ case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4:
+ nor->flags |= SNOR_F_4B_OPCODES;
nor->addr_width = 4;
break;

@@ -3552,7 +3554,7 @@ static int spi_nor_init(struct spi_nor *nor)

if ((nor->addr_width == 4) &&
(JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
- !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
+ !(nor->flags & SNOR_F_4B_OPCODES)) {
/*
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
@@ -3586,7 +3588,7 @@ void spi_nor_restore(struct spi_nor *nor)
/* restore the addressing mode */
if ((nor->addr_width == 4) &&
(JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
- !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
+ !(nor->flags & SNOR_F_4B_OPCODES) &&
(nor->flags & SNOR_F_BROKEN_RESET))
set_4byte(nor, nor->info, 0);
}
@@ -3724,6 +3726,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (info->flags & SPI_NOR_NO_FR)
params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;

+ if (info->flags & SPI_NOR_4B_OPCODES)
+ nor->flags |= SNOR_F_4B_OPCODES;
+
/*
* Configure the SPI memory:
* - select op codes for (Fast) Read, Page Program and Sector Erase.
@@ -3742,13 +3747,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
} else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
- if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
- info->flags & SPI_NOR_4B_OPCODES)
- spi_nor_set_4byte_opcodes(nor, info);
+ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION)
+ nor->flags |= SNOR_F_4B_OPCODES;
} else {
nor->addr_width = 3;
}

+ if (info->addr_width == 4 &&
+ nor->flags & SNOR_F_4B_OPCODES)
+ spi_nor_set_4byte_opcodes(nor, info);
+
if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
dev_err(dev, "address width is too large: %u\n",
nor->addr_width);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 7f0c7303575e..4ffb165f4f85 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -236,6 +236,7 @@ enum spi_nor_option_flags {
SNOR_F_READY_XSR_RDY = BIT(4),
SNOR_F_USE_CLSR = BIT(5),
SNOR_F_BROKEN_RESET = BIT(6),
+ SNOR_F_4B_OPCODES = BIT(7)
};

/**

2018-10-17 07:11:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 09:07:24 +0200
Boris Brezillon <[email protected]> wrote:

> On Wed, 17 Oct 2018 02:07:43 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > >
> > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > For my connected flash part, jedec ID read points to s25fl512s. I
> > have asked my board team to confirm the name of exact connected flash
> > part. When I check the data sheet of s25fs512s, it also points to the
> > same Jedec ID information. { "s25fl512s", INFO(0x010220, 0x4d00, 256
> > * 1024, 256, ....}
> >
> > But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > protocol then read are always correct. For 1-4-4 protocol read are
> > wrong and on further debugging found that Read code of 0x6C is being
> > send as opcode instead of 0xEC.
> >
> > If I revert this patch, reads are working fine.
>
> Can you try with the following patch?
>

Hm, nevermind. The problem is actually not related to 4B vs non-4B mode
but 1-1-4 vs 1-4-4 modes.


2018-10-17 07:20:18

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 09:10:45 +0200
Boris Brezillon <[email protected]> wrote:

> On Wed, 17 Oct 2018 09:07:24 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Wed, 17 Oct 2018 02:07:43 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > >
> > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > For my connected flash part, jedec ID read points to s25fl512s. I
> > > have asked my board team to confirm the name of exact connected flash
> > > part. When I check the data sheet of s25fs512s, it also points to the
> > > same Jedec ID information. { "s25fl512s", INFO(0x010220, 0x4d00, 256
> > > * 1024, 256, ....}
> > >
> > > But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > > protocol then read are always correct. For 1-4-4 protocol read are
> > > wrong and on further debugging found that Read code of 0x6C is being
> > > send as opcode instead of 0xEC.
> > >
> > > If I revert this patch, reads are working fine.
> >
> > Can you try with the following patch?
> >
>
> Hm, nevermind. The problem is actually not related to 4B vs non-4B mode
> but 1-1-4 vs 1-4-4 modes.
>

Can you add traces in this loop [1] to see which features are flagged
as supported and which ones are not.

[1]https://elixir.bootlin.com/linux/v4.19-rc8/source/drivers/mtd/spi-nor/spi-nor.c#L2259


2018-10-17 07:30:56

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 09:10:45 +0200
Boris Brezillon <[email protected]> wrote:

> On Wed, 17 Oct 2018 09:07:24 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Wed, 17 Oct 2018 02:07:43 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > >
> > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > For my connected flash part, jedec ID read points to s25fl512s. I
> > > have asked my board team to confirm the name of exact connected flash
> > > part. When I check the data sheet of s25fs512s, it also points to the
> > > same Jedec ID information. { "s25fl512s", INFO(0x010220, 0x4d00, 256
> > > * 1024, 256, ....}
> > >
> > > But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > > protocol then read are always correct. For 1-4-4 protocol read are
> > > wrong and on further debugging found that Read code of 0x6C is being
> > > send as opcode instead of 0xEC.
> > >
> > > If I revert this patch, reads are working fine.
> >
> > Can you try with the following patch?
> >
>
> Hm, nevermind. The problem is actually not related to 4B vs non-4B mode
> but 1-1-4 vs 1-4-4 modes.
>

Can you try with this patch applied?

--->8---
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9407ca5f9443..cf33d834698c 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -3132,6 +3132,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
switch (SFDP_PARAM_HEADER_ID(param_header)) {
case SFDP_SECTOR_MAP_ID:
err = spi_nor_parse_smpt(nor, param_header);
+ if (err) {
+ dev_warn(dev,
+ "failed to parse SMPT (err = %d)\n",
+ err);
+ /*
+ * SMPT parsing is optional, let's not drop
+ * all information we extracted so far just
+ * because it failed.
+ */
+ err = 0;
+ }
break;

default:

2018-10-17 07:48:32

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Boris,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Wednesday, October 17, 2018 1:00 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Wed, 17 Oct 2018 09:10:45 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Wed, 17 Oct 2018 09:07:24 +0200
> > Boris Brezillon <[email protected]> wrote:
> >
> > > On Wed, 17 Oct 2018 02:07:43 +0000
> > > Yogesh Narayan Gaur <[email protected]> wrote:
> > >
> > > > >
> > > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > > For my connected flash part, jedec ID read points to s25fl512s. I
> > > > have asked my board team to confirm the name of exact connected
> > > > flash part. When I check the data sheet of s25fs512s, it also
> > > > points to the same Jedec ID information. { "s25fl512s",
> > > > INFO(0x010220, 0x4d00, 256
> > > > * 1024, 256, ....}
> > > >
> > > > But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > > > protocol then read are always correct. For 1-4-4 protocol read are
> > > > wrong and on further debugging found that Read code of 0x6C is
> > > > being send as opcode instead of 0xEC.
> > > >
> > > > If I revert this patch, reads are working fine.
> > >
> > > Can you try with the following patch?
> > >
> >
> > Hm, nevermind. The problem is actually not related to 4B vs non-4B
> > mode but 1-1-4 vs 1-4-4 modes.
Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read opcode is being sent for 1-1-4 mode.
> >
>
> Can you try with this patch applied?
>
With suggested patch, read for protocol 1-4-4 working correctly.

[ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
[ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
[ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
[ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)

Without this patch, param_headers are getting freed and restoring previous erase map i.e. opcode related to 1-1-4 protocol.

--
Regards
Yogesh Gaur

> --->8---
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index
> 9407ca5f9443..cf33d834698c 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -3132,6 +3132,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
> switch (SFDP_PARAM_HEADER_ID(param_header)) {
> case SFDP_SECTOR_MAP_ID:
> err = spi_nor_parse_smpt(nor, param_header);
> + if (err) {
> + dev_warn(dev,
> + "failed to parse SMPT (err = %d)\n",
> + err);
> + /*
> + * SMPT parsing is optional, let's not drop
> + * all information we extracted so far just
> + * because it failed.
> + */
> + err = 0;
> + }
> break;
>
> default:

2018-10-17 08:01:52

by Tudor Ambarus

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi, Yogesh,

On 10/17/2018 10:46 AM, Yogesh Narayan Gaur wrote:
> Hi Boris,
>
>> -----Original Message-----
>> From: Boris Brezillon [mailto:[email protected]]
>> Sent: Wednesday, October 17, 2018 1:00 PM
>> To: Yogesh Narayan Gaur <[email protected]>
>> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
>> <[email protected]>; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected];
>> [email protected]; [email protected]; linux-arm-
>> [email protected]; [email protected]
>> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
>> NOR flash memories
>>
>> On Wed, 17 Oct 2018 09:10:45 +0200
>> Boris Brezillon <[email protected]> wrote:
>>
>>> On Wed, 17 Oct 2018 09:07:24 +0200
>>> Boris Brezillon <[email protected]> wrote:
>>>
>>>> On Wed, 17 Oct 2018 02:07:43 +0000
>>>> Yogesh Narayan Gaur <[email protected]> wrote:
>>>>
>>>>>>
>>>>> Actually there is no entry of s25fs512s in current spi-nor.c file.
>>>>> For my connected flash part, jedec ID read points to s25fl512s. I
>>>>> have asked my board team to confirm the name of exact connected
>>>>> flash part. When I check the data sheet of s25fs512s, it also
>>>>> points to the same Jedec ID information. { "s25fl512s",
>>>>> INFO(0x010220, 0x4d00, 256
>>>>> * 1024, 256, ....}
>>>>>
>>>>> But as stated earlier, if I skip reading SFDP or read using 1-1-1
>>>>> protocol then read are always correct. For 1-4-4 protocol read are
>>>>> wrong and on further debugging found that Read code of 0x6C is
>>>>> being send as opcode instead of 0xEC.
>>>>>
>>>>> If I revert this patch, reads are working fine.
>>>>
>>>> Can you try with the following patch?
>>>>
>>>
>>> Hm, nevermind. The problem is actually not related to 4B vs non-4B
>>> mode but 1-1-4 vs 1-4-4 modes.
> Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read opcode is being sent for 1-1-4 mode.
>>>
>>
>> Can you try with this patch applied?
>>
> With suggested patch, read for protocol 1-4-4 working correctly.
>
> [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
>
> Without this patch, param_headers are getting freed and restoring previous erase map i.e. opcode related to 1-1-4 protocol.
>

Can you add some prints in spi_nor_parse_smpt() to isolate what's failing? We
should understand whether it's something wrong in spi_nor_parse_smpt() or the
s25fs512s smpt table does not respect the standard.

Thanks,
ta

>
>> --->8---
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index
>> 9407ca5f9443..cf33d834698c 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -3132,6 +3132,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
>> switch (SFDP_PARAM_HEADER_ID(param_header)) {
>> case SFDP_SECTOR_MAP_ID:
>> err = spi_nor_parse_smpt(nor, param_header);
>> + if (err) {
>> + dev_warn(dev,
>> + "failed to parse SMPT (err = %d)\n",
>> + err);
>> + /*
>> + * SMPT parsing is optional, let's not drop
>> + * all information we extracted so far just
>> + * because it failed.
>> + */
>> + err = 0;
>> + }
>> break;
>>
>> default:
>

2018-10-17 08:21:38

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Tudor,

> -----Original Message-----
> From: Tudor Ambarus [mailto:[email protected]]
> Sent: Wednesday, October 17, 2018 1:31 PM
> To: Yogesh Narayan Gaur <[email protected]>; Boris Brezillon
> <[email protected]>
> Cc: Cyrille Pitchen <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> Hi, Yogesh,
>
> On 10/17/2018 10:46 AM, Yogesh Narayan Gaur wrote:
> > Hi Boris,
> >
> >> -----Original Message-----
> >> From: Boris Brezillon [mailto:[email protected]]
> >> Sent: Wednesday, October 17, 2018 1:00 PM
> >> To: Yogesh Narayan Gaur <[email protected]>
> >> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> >> <[email protected]>; [email protected];
> >> [email protected]; [email protected]; [email protected];
> >> [email protected]; [email protected];
> >> [email protected]; [email protected];
> >> linux-arm- [email protected]; [email protected]
> >> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> >> SFDP SPI NOR flash memories
> >>
> >> On Wed, 17 Oct 2018 09:10:45 +0200
> >> Boris Brezillon <[email protected]> wrote:
> >>
> >>> On Wed, 17 Oct 2018 09:07:24 +0200
> >>> Boris Brezillon <[email protected]> wrote:
> >>>
> >>>> On Wed, 17 Oct 2018 02:07:43 +0000
> >>>> Yogesh Narayan Gaur <[email protected]> wrote:
> >>>>
> >>>>>>
> >>>>> Actually there is no entry of s25fs512s in current spi-nor.c file.
> >>>>> For my connected flash part, jedec ID read points to s25fl512s. I
> >>>>> have asked my board team to confirm the name of exact connected
> >>>>> flash part. When I check the data sheet of s25fs512s, it also
> >>>>> points to the same Jedec ID information. { "s25fl512s",
> >>>>> INFO(0x010220, 0x4d00, 256
> >>>>> * 1024, 256, ....}
> >>>>>
> >>>>> But as stated earlier, if I skip reading SFDP or read using 1-1-1
> >>>>> protocol then read are always correct. For 1-4-4 protocol read are
> >>>>> wrong and on further debugging found that Read code of 0x6C is
> >>>>> being send as opcode instead of 0xEC.
> >>>>>
> >>>>> If I revert this patch, reads are working fine.
> >>>>
> >>>> Can you try with the following patch?
> >>>>
> >>>
> >>> Hm, nevermind. The problem is actually not related to 4B vs non-4B
> >>> mode but 1-1-4 vs 1-4-4 modes.
> > Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read
> opcode is being sent for 1-1-4 mode.
> >>>
> >>
> >> Can you try with this patch applied?
> >>
> > With suggested patch, read for protocol 1-4-4 working correctly.
> >
> > [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> > [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> > [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> > [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
> >
> > Without this patch, param_headers are getting freed and restoring previous
> erase map i.e. opcode related to 1-1-4 protocol.
> >
>
> Can you add some prints in spi_nor_parse_smpt() to isolate what's failing? We
> should understand whether it's something wrong in spi_nor_parse_smpt() or the
> s25fs512s smpt table does not respect the standard.
>

It's returning failure from below point in func spi_nor_get_map_in_use()

/* Find the matching configuration map */
while (SMPT_MAP_ID(smpt[i]) != map_id) {
if (smpt[i] & SMPT_DESC_END) {
printk("%d %s \n", __LINE__, __func__);
goto out;
}
--
Regards
Yogesh Gaur.

> Thanks,
> ta
>

2018-10-17 08:49:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 08:20:19 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi Tudor,
>
> > -----Original Message-----
> > From: Tudor Ambarus [mailto:[email protected]]
> > Sent: Wednesday, October 17, 2018 1:31 PM
> > To: Yogesh Narayan Gaur <[email protected]>; Boris Brezillon
> > <[email protected]>
> > Cc: Cyrille Pitchen <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; linux-arm-
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > Hi, Yogesh,
> >
> > On 10/17/2018 10:46 AM, Yogesh Narayan Gaur wrote:
> > > Hi Boris,
> > >
> > >> -----Original Message-----
> > >> From: Boris Brezillon [mailto:[email protected]]
> > >> Sent: Wednesday, October 17, 2018 1:00 PM
> > >> To: Yogesh Narayan Gaur <[email protected]>
> > >> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > >> <[email protected]>; [email protected];
> > >> [email protected]; [email protected]; [email protected];
> > >> [email protected]; [email protected];
> > >> [email protected]; [email protected];
> > >> linux-arm- [email protected]; [email protected]
> > >> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > >> SFDP SPI NOR flash memories
> > >>
> > >> On Wed, 17 Oct 2018 09:10:45 +0200
> > >> Boris Brezillon <[email protected]> wrote:
> > >>
> > >>> On Wed, 17 Oct 2018 09:07:24 +0200
> > >>> Boris Brezillon <[email protected]> wrote:
> > >>>
> > >>>> On Wed, 17 Oct 2018 02:07:43 +0000
> > >>>> Yogesh Narayan Gaur <[email protected]> wrote:
> > >>>>
> > >>>>>>
> > >>>>> Actually there is no entry of s25fs512s in current spi-nor.c file.
> > >>>>> For my connected flash part, jedec ID read points to s25fl512s. I
> > >>>>> have asked my board team to confirm the name of exact connected
> > >>>>> flash part. When I check the data sheet of s25fs512s, it also
> > >>>>> points to the same Jedec ID information. { "s25fl512s",
> > >>>>> INFO(0x010220, 0x4d00, 256
> > >>>>> * 1024, 256, ....}
> > >>>>>
> > >>>>> But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > >>>>> protocol then read are always correct. For 1-4-4 protocol read are
> > >>>>> wrong and on further debugging found that Read code of 0x6C is
> > >>>>> being send as opcode instead of 0xEC.
> > >>>>>
> > >>>>> If I revert this patch, reads are working fine.
> > >>>>
> > >>>> Can you try with the following patch?
> > >>>>
> > >>>
> > >>> Hm, nevermind. The problem is actually not related to 4B vs non-4B
> > >>> mode but 1-1-4 vs 1-4-4 modes.
> > > Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read
> > opcode is being sent for 1-1-4 mode.
> > >>>
> > >>
> > >> Can you try with this patch applied?
> > >>
> > > With suggested patch, read for protocol 1-4-4 working correctly.
> > >
> > > [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> > > [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > > [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> > > [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> > > [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
> > >
> > > Without this patch, param_headers are getting freed and restoring previous
> > erase map i.e. opcode related to 1-1-4 protocol.
> > >
> >
> > Can you add some prints in spi_nor_parse_smpt() to isolate what's failing? We
> > should understand whether it's something wrong in spi_nor_parse_smpt() or the
> > s25fs512s smpt table does not respect the standard.
> >
>
> It's returning failure from below point in func spi_nor_get_map_in_use()
>
> /* Find the matching configuration map */
> while (SMPT_MAP_ID(smpt[i]) != map_id) {
> if (smpt[i] & SMPT_DESC_END) {
> printk("%d %s \n", __LINE__, __func__);
> goto out;
> }

Can you dump the smpt array just before calling
spi_nor_get_map_in_use()?

> --
> Regards
> Yogesh Gaur.
>
> > Thanks,
> > ta
> >
>


2018-10-17 09:15:10

by Cyrille Pitchen

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Yogesh, Tudor,

Le 17/10/2018 à 04:07, Yogesh Narayan Gaur a écrit :
> Hi Tudor,
>
>> -----Original Message-----
>> From: Cyrille Pitchen [mailto:[email protected]]
>> Sent: Tuesday, October 16, 2018 10:04 PM
>> To: Tudor Ambarus <[email protected]>; Yogesh Narayan Gaur
>> <[email protected]>; [email protected];
>> [email protected]; [email protected];
>> [email protected]; [email protected]
>> Cc: [email protected]; [email protected];
>> [email protected]; [email protected]; linux-arm-
>> [email protected]; [email protected]
>> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
>> NOR flash memories
>>
>> Hi Tudor,
>>
>> Le 16/10/2018 à 17:14, Tudor Ambarus a écrit :
>>> Hi, Yogesh,
>>>
>>> On 10/16/2018 12:51 PM, Yogesh Narayan Gaur wrote:
>>>> Hi Tudor,
>>>>
>>>> This patch is breaking the 1-4-4 Read protocol for the spansion flash
>> "s25fl512s".
>>>>
>>>> Without this patch read request command for Quad mode, 4-byte enable, is
>> coming as 0xEC i.e. SPINOR_OP_READ_1_4_4_4B.
>>>> But after applying this patch, read request command for Quad mode is
>> coming as 0x6C i.e. SPINOR_OP_READ_1_1_4_4B.
>>>>
>>>> This flash also supports non-uniform erase.
>>>> Can you please check and provide some suggestion?
>>>
>>> I don't have this memory to test it, but I'll try to help.
>>>
>>> Does s25fl512s support non-uniform erase? I'm looking in datasheet[1]
>>> at JEDEC BFPT table, dwords 8 and 9, page 132/146 and it looks like it
>>> supports just 256KB uniform erase.
>>>
>>
> Actually there is no entry of s25fs512s in current spi-nor.c file.
> For my connected flash part, jedec ID read points to s25fl512s. I have asked my board team to confirm the name of exact connected flash part.
> When I check the data sheet of s25fs512s, it also points to the same Jedec ID information.
> { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, ....}
>

At least the 6th byte of the JEDEC ID, the Cypress Family ID, is different
between S25FL512S (0x80) and S25FS512S (0x81).

Hence the INFO6() macro can be used to create separated entries in
the spi_nor_ids[] array.

Warning: the exact value of the 4th byte of the JEDEC ID for S25FL512S
memory parts is not provided by the Cypress datasheet I read. It's only
written that "The value is OPN dependent".

Maybe you can do the magic by placing a new INFO6() entry for the S25FL512S,
just before the legacy INFO() entry for S25FL512S. Indeed entries are tested
in the order they appear inside the spi_nor_ids[] array from spi_nor_read_id(),
so the INFO6() entry would be tested 1st, trying to match 6 bytes, then the
INFO() entry only trying to match 3 bytes.

Maybe it won't solve your issue but since this is not the 1st time that
someone needs to make the difference between those 2 memory parts...

Best regards,

Cyrille


> But as stated earlier, if I skip reading SFDP or read using 1-1-1 protocol then read are always correct.
> For 1-4-4 protocol read are wrong and on further debugging found that Read code of 0x6C is being send as opcode instead of 0xEC.
>
> If I revert this patch, reads are working fine.
>
> --
> Regards
> Yogesh Gaur
>
>> s25fS512s supports both uniform and non uniform erase options but s25fL512s is
>> always uniform. L is an old memory part, S is newer.
>>
>> Also, the 8th and 9th WORDs of the Basic Flash Parameter Table alone can't tell
>> you whether or not the memory part can be non uniform.
>> If the memory can be non uniform then the sector erase map table is mandatory,
>> hence when the table is missing you know that your memory part is always
>> uniform.
>>
>> Best regards,
>>
>> Cyrille
>>
>>> Thanks,
>>> ta
>>>
>>> [1]
>>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
>>>
>> cypress.com%2Ffile%2F177971%2Fdownload&amp;data=02%7C01%7Cyogeshn
>> araya
>>>
>> n.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%7C686ea1d3bc2
>> b4c6f
>>>
>> a92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;sdata=cioC98EH
>> OGlFbg
>>> XPhoIIJ72K3JrNUnzA1pYhSB9jDwg%3D&amp;reserved=0
>>>
>>>>
>>>> --
>>>> Regards
>>>> Yogesh Gaur
>>>>
>>>>> -----Original Message-----
>>>>> From: linux-mtd [mailto:[email protected]] On
>>>>> Behalf Of Tudor Ambarus
>>>>> Sent: Tuesday, September 11, 2018 9:10 PM
>>>>> To: [email protected]; [email protected];
>>>>> [email protected]; [email protected];
>>>>> [email protected]
>>>>> Cc: Tudor Ambarus <[email protected]>; linux-
>>>>> [email protected]; [email protected];
>>>>> [email protected]; [email protected];
>>>>> linux-arm- [email protected]; [email protected]
>>>>> Subject: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
>>>>> SFDP SPI NOR flash memories
>>>>>
>>>>> Based on Cyrille Pitchen's patch
>>>>> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fl
>>>>> kml.or
>>>>>
>> g%2Flkml%2F2017%2F3%2F22%2F935&amp;data=02%7C01%7Cyogeshnarayan.
>>>>>
>> gaur%40nxp.com%7C3c782e52b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4
>>>>>
>> c6fa92cd99c5c301635%7C0%7C0%7C636722774108718782&amp;sdata=szyc%
>>>>>
>> 2FTumG6eYAmBd0oW3IL7v1yLh9E1SAZqL%2BCWczOA%3D&amp;reserved=0.
>>>>>
>>>>> This patch is a transitional patch in introducing the support of
>>>>> SFDP SPI memories with non-uniform erase sizes like Spansion s25fs512s.
>>>>> Non-uniform erase maps will be used later when initialized based on
>>>>> the SFDP data.
>>>>>
>>>>> Introduce the memory erase map which splits the memory array into
>>>>> one or many erase regions. Each erase region supports up to 4 erase
>>>>> types, as defined by the JEDEC JESD216B (SFDP) specification.
>>>>>
>>>>> To be backward compatible, the erase map of uniform SPI NOR flash
>>>>> memories is initialized so it contains only one erase region and
>>>>> this erase region supports only one erase command. Hence a single
>>>>> size is used to erase any sector/block of the memory.
>>>>>
>>>>> Besides, since the algorithm used to erase sectors on non-uniform
>>>>> SPI NOR flash memories is quite expensive, when possible, the erase
>>>>> map is tuned to come back to the uniform case.
>>>>>
>>>>> The 'erase with the best command, move forward and repeat' approach
>>>>> was suggested by Cristian Birsan in a brainstorm session, so:
>>>>>
>>>>> Suggested-by: Cristian Birsan <[email protected]>
>>>>> Signed-off-by: Tudor Ambarus <[email protected]>
>>>>> ---
>>>>> drivers/mtd/spi-nor/spi-nor.c | 594
>>>>> +++++++++++++++++++++++++++++++++++++++---
>>>>> include/linux/mtd/spi-nor.h | 107 ++++++++
>>>>> 2 files changed, 659 insertions(+), 42 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mtd/spi-nor/spi-nor.c
>>>>> b/drivers/mtd/spi-nor/spi-nor.c index
>>>>> dc8757e..4687345 100644
>>>>> --- a/drivers/mtd/spi-nor/spi-nor.c
>>>>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>>>>> @@ -18,6 +18,7 @@
>>>>> #include <linux/math64.h>
>>>>> #include <linux/sizes.h>
>>>>> #include <linux/slab.h>
>>>>> +#include <linux/sort.h>
>>>>>
>>>>> #include <linux/mtd/mtd.h>
>>>>> #include <linux/of_platform.h>
>>>>> @@ -261,6 +262,18 @@ static void spi_nor_set_4byte_opcodes(struct
>>>>> spi_nor *nor,
>>>>> nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
>>>>> nor->program_opcode = spi_nor_convert_3to4_program(nor-
>>>>>> program_opcode);
>>>>> nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
>>>>> +
>>>>> + if (!spi_nor_has_uniform_erase(nor)) {
>>>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>>>> + struct spi_nor_erase_type *erase;
>>>>> + int i;
>>>>> +
>>>>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
>>>>> + erase = &map->erase_type[i];
>>>>> + erase->opcode =
>>>>> + spi_nor_convert_3to4_erase(erase->opcode);
>>>>> + }
>>>>> + }
>>>>> }
>>>>>
>>>>> /* Enable/disable 4-byte addressing mode. */ @@ -499,6 +512,275 @@
>>>>> static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) }
>>>>>
>>>>> /*
>>>>> + * spi_nor_div_by_erase_size() - calculate remainder and update new
>> dividend
>>>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>>>> + * @dividend: dividend value
>>>>> + * @remainder: pointer to u32 remainder (will be updated)
>>>>> + *
>>>>> + * Returns two values: remainder and the new dividend */ static
>>>>> +u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
>>>>> + u64 dividend, u32 *remainder) {
>>>>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>>>>> + *remainder = (u32)dividend & erase->size_mask;
>>>>> + return dividend >> erase->size_shift; }
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_find_best_erase_type() - find the best erase type for
>>>>> +the given
>>>>> + * offset in the serial flash memory and the number of bytes to erase.
>>>>> +The
>>>>> + * region in which the address fits is expected to be provided.
>>>>> + * @map: the erase map of the SPI NOR
>>>>> + * @region: pointer to a structure that describes a SPI NOR erase
>> region
>>>>> + * @addr: offset in the serial flash memory
>>>>> + * @len: number of bytes to erase
>>>>> + *
>>>>> + * Returns a pointer to the best fitted erase type, NULL otherwise.
>>>>> + */
>>>>> +static const struct spi_nor_erase_type *
>>>>> +spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
>>>>> + const struct spi_nor_erase_region *region,
>>>>> + u64 addr, u32 len)
>>>>> +{
>>>>> + const struct spi_nor_erase_type *erase;
>>>>> + u32 rem;
>>>>> + int i;
>>>>> + u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
>>>>> +
>>>>> + /*
>>>>> + * Erase types are ordered by size, with the biggest erase type at
>>>>> + * index 0.
>>>>> + */
>>>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>>>> + /* Does the erase region support the tested erase type? */
>>>>> + if (!(erase_mask & BIT(i)))
>>>>> + continue;
>>>>> +
>>>>> + erase = &map->erase_type[i];
>>>>> +
>>>>> + /* Don't erase more than what the user has asked for. */
>>>>> + if (erase->size > len)
>>>>> + continue;
>>>>> +
>>>>> + /* Alignment is not mandatory for overlaid regions */
>>>>> + if (region->offset & SNOR_OVERLAID_REGION)
>>>>> + return erase;
>>>>> +
>>>>> + spi_nor_div_by_erase_size(erase, addr, &rem);
>>>>> + if (rem)
>>>>> + continue;
>>>>> + else
>>>>> + return erase;
>>>>> + }
>>>>> +
>>>>> + return NULL;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_region_next() - get the next spi nor region
>>>>> + * @region: pointer to a structure that describes a SPI NOR erase
>> region
>>>>> + *
>>>>> + * Returns the next spi nor region or NULL if last region.
>>>>> + */
>>>>> +static struct spi_nor_erase_region * spi_nor_region_next(struct
>>>>> +spi_nor_erase_region *region) {
>>>>> + if (spi_nor_region_is_last(region))
>>>>> + return NULL;
>>>>> + region++;
>>>>> + return region;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_find_erase_region() - find the region of the serial
>>>>> +flash memory in
>>>>> + * which the offset fits
>>>>> + * @map: the erase map of the SPI NOR
>>>>> + * @addr: offset in the serial flash memory
>>>>> + *
>>>>> + * Returns pointer to the spi_nor_erase_region struct,
>>>>> +ERR_PTR(-errno)
>>>>> + * otherwise.
>>>>> + */
>>>>> +static struct spi_nor_erase_region *
>>>>> +spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64
>>>>> +addr) {
>>>>> + struct spi_nor_erase_region *region = map->regions;
>>>>> + u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>>>>> + u64 region_end = region_start + region->size;
>>>>> +
>>>>> + while (addr < region_start || addr >= region_end) {
>>>>> + region = spi_nor_region_next(region);
>>>>> + if (!region)
>>>>> + return ERR_PTR(-EINVAL);
>>>>> +
>>>>> + region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
>>>>> + region_end = region_start + region->size;
>>>>> + }
>>>>> +
>>>>> + return region;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_init_erase_cmd() - initialize an erase command
>>>>> + * @region: pointer to a structure that describes a SPI NOR erase
>> region
>>>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>>>> + *
>>>>> + * Returns the pointer to the allocated erase command,
>>>>> +ERR_PTR(-errno)
>>>>> + * otherwise.
>>>>> + */
>>>>> +static struct spi_nor_erase_command * spi_nor_init_erase_cmd(const
>>>>> +struct spi_nor_erase_region *region,
>>>>> + const struct spi_nor_erase_type *erase) {
>>>>> + struct spi_nor_erase_command *cmd;
>>>>> +
>>>>> + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
>>>>> + if (!cmd)
>>>>> + return ERR_PTR(-ENOMEM);
>>>>> +
>>>>> + INIT_LIST_HEAD(&cmd->list);
>>>>> + cmd->opcode = erase->opcode;
>>>>> + cmd->count = 1;
>>>>> +
>>>>> + if (region->offset & SNOR_OVERLAID_REGION)
>>>>> + cmd->size = region->size;
>>>>> + else
>>>>> + cmd->size = erase->size;
>>>>> +
>>>>> + return cmd;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_destroy_erase_cmd_list() - destroy erase command list
>>>>> + * @erase_list: list of erase commands
>>>>> + */
>>>>> +static void spi_nor_destroy_erase_cmd_list(struct list_head
>>>>> +*erase_list) {
>>>>> + struct spi_nor_erase_command *cmd, *next;
>>>>> +
>>>>> + list_for_each_entry_safe(cmd, next, erase_list, list) {
>>>>> + list_del(&cmd->list);
>>>>> + kfree(cmd);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_init_erase_cmd_list() - initialize erase command list
>>>>> + * @nor: pointer to a 'struct spi_nor'
>>>>> + * @erase_list: list of erase commands to be executed once we
>> validate that
>>>>> the
>>>>> + * erase can be performed
>>>>> + * @addr: offset in the serial flash memory
>>>>> + * @len: number of bytes to erase
>>>>> + *
>>>>> + * Builds the list of best fitted erase commands and verifies if
>>>>> +the erase can
>>>>> + * be performed.
>>>>> + *
>>>>> + * Returns 0 on success, -errno otherwise.
>>>>> + */
>>>>> +static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
>>>>> + struct list_head *erase_list,
>>>>> + u64 addr, u32 len)
>>>>> +{
>>>>> + const struct spi_nor_erase_map *map = &nor->erase_map;
>>>>> + const struct spi_nor_erase_type *erase, *prev_erase = NULL;
>>>>> + struct spi_nor_erase_region *region;
>>>>> + struct spi_nor_erase_command *cmd = NULL;
>>>>> + u64 region_end;
>>>>> + int ret = -EINVAL;
>>>>> +
>>>>> + region = spi_nor_find_erase_region(map, addr);
>>>>> + if (IS_ERR(region))
>>>>> + return PTR_ERR(region);
>>>>> +
>>>>> + region_end = spi_nor_region_end(region);
>>>>> +
>>>>> + while (len) {
>>>>> + erase = spi_nor_find_best_erase_type(map, region, addr, len);
>>>>> + if (!erase)
>>>>> + goto destroy_erase_cmd_list;
>>>>> +
>>>>> + if (prev_erase != erase ||
>>>>> + region->offset & SNOR_OVERLAID_REGION) {
>>>>> + cmd = spi_nor_init_erase_cmd(region, erase);
>>>>> + if (IS_ERR(cmd)) {
>>>>> + ret = PTR_ERR(cmd);
>>>>> + goto destroy_erase_cmd_list;
>>>>> + }
>>>>> +
>>>>> + list_add_tail(&cmd->list, erase_list);
>>>>> + } else {
>>>>> + cmd->count++;
>>>>> + }
>>>>> +
>>>>> + addr += cmd->size;
>>>>> + len -= cmd->size;
>>>>> +
>>>>> + if (len && addr >= region_end) {
>>>>> + region = spi_nor_region_next(region);
>>>>> + if (!region)
>>>>> + goto destroy_erase_cmd_list;
>>>>> + region_end = spi_nor_region_end(region);
>>>>> + }
>>>>> +
>>>>> + prev_erase = erase;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +
>>>>> +destroy_erase_cmd_list:
>>>>> + spi_nor_destroy_erase_cmd_list(erase_list);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_erase_multi_sectors() - perform a non-uniform erase
>>>>> + * @nor: pointer to a 'struct spi_nor'
>>>>> + * @addr: offset in the serial flash memory
>>>>> + * @len: number of bytes to erase
>>>>> + *
>>>>> + * Build a list of best fitted erase commands and execute it once
>>>>> +we
>>>>> + * validate that the erase can be performed.
>>>>> + *
>>>>> + * Returns 0 on success, -errno otherwise.
>>>>> + */
>>>>> +static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64
>>>>> +addr,
>>>>> +u32 len) {
>>>>> + LIST_HEAD(erase_list);
>>>>> + struct spi_nor_erase_command *cmd, *next;
>>>>> + int ret;
>>>>> +
>>>>> + ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + list_for_each_entry_safe(cmd, next, &erase_list, list) {
>>>>> + nor->erase_opcode = cmd->opcode;
>>>>> + while (cmd->count) {
>>>>> + write_enable(nor);
>>>>> +
>>>>> + ret = spi_nor_erase_sector(nor, addr);
>>>>> + if (ret)
>>>>> + goto destroy_erase_cmd_list;
>>>>> +
>>>>> + addr += cmd->size;
>>>>> + cmd->count--;
>>>>> +
>>>>> + ret = spi_nor_wait_till_ready(nor);
>>>>> + if (ret)
>>>>> + goto destroy_erase_cmd_list;
>>>>> + }
>>>>> + list_del(&cmd->list);
>>>>> + kfree(cmd);
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +
>>>>> +destroy_erase_cmd_list:
>>>>> + spi_nor_destroy_erase_cmd_list(&erase_list);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> * Erase an address range on the nor chip. The address range may extend
>>>>> * one or more erase sectors. Return an error is there is a problem erasing.
>>>>> */
>>>>> @@ -512,9 +794,11 @@ static int spi_nor_erase(struct mtd_info *mtd,
>>>>> struct erase_info *instr)
>>>>> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>>>>> (long long)instr->len);
>>>>>
>>>>> - div_u64_rem(instr->len, mtd->erasesize, &rem);
>>>>> - if (rem)
>>>>> - return -EINVAL;
>>>>> + if (spi_nor_has_uniform_erase(nor)) {
>>>>> + div_u64_rem(instr->len, mtd->erasesize, &rem);
>>>>> + if (rem)
>>>>> + return -EINVAL;
>>>>> + }
>>>>>
>>>>> addr = instr->addr;
>>>>> len = instr->len;
>>>>> @@ -553,7 +837,7 @@ static int spi_nor_erase(struct mtd_info *mtd,
>>>>> struct erase_info *instr)
>>>>> */
>>>>>
>>>>> /* "sector"-at-a-time erase */
>>>>> - } else {
>>>>> + } else if (spi_nor_has_uniform_erase(nor)) {
>>>>> while (len) {
>>>>> write_enable(nor);
>>>>>
>>>>> @@ -568,6 +852,12 @@ static int spi_nor_erase(struct mtd_info *mtd,
>>>>> struct erase_info *instr)
>>>>> if (ret)
>>>>> goto erase_err;
>>>>> }
>>>>> +
>>>>> + /* erase multiple sectors */
>>>>> + } else {
>>>>> + ret = spi_nor_erase_multi_sectors(nor, addr, len);
>>>>> + if (ret)
>>>>> + goto erase_err;
>>>>> }
>>>>>
>>>>> write_disable(nor);
>>>>> @@ -2190,6 +2480,113 @@ static const struct sfdp_bfpt_erase
>>>>> sfdp_bfpt_erases[] = {
>>>>>
>>>>> static int spi_nor_hwcaps_read2cmd(u32 hwcaps);
>>>>>
>>>>> +/*
>>>>> + * spi_nor_set_erase_type() - set a SPI NOR erase type
>>>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>>>> + * @size: the size of the sector/block erased by the erase type
>>>>> + * @opcode: the SPI command op code to erase the sector/block
>>>>> + */
>>>>> +static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
>>>>> + u32 size, u8 opcode)
>>>>> +{
>>>>> + erase->size = size;
>>>>> + erase->opcode = opcode;
>>>>> + /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
>>>>> + erase->size_shift = ffs(erase->size) - 1;
>>>>> + erase->size_mask = (1 << erase->size_shift) - 1; }
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from
>> BFPT
>>>>> + * @erase: pointer to a structure that describes a SPI NOR erase type
>>>>> + * @size: the size of the sector/block erased by the erase type
>>>>> + * @opcode: the SPI command op code to erase the sector/block
>>>>> + * @i: erase type index as sorted in the Basic Flash Parameter
>> Table
>>>>> + *
>>>>> + * The supported Erase Types will be sorted at init in ascending
>>>>> +order, with
>>>>> + * the smallest Erase Type size being the first member in the
>>>>> +erase_type array
>>>>> + * of the spi_nor_erase_map structure. Save the Erase Type index as
>>>>> +sorted in
>>>>> + * the Basic Flash Parameter Table since it will be used later on
>>>>> +to
>>>>> + * synchronize with the supported Erase Types defined in SFDP optional
>> tables.
>>>>> + */
>>>>> +static void
>>>>> +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
>>>>> + u32 size, u8 opcode, u8 i) {
>>>>> + erase->idx = i;
>>>>> + spi_nor_set_erase_type(erase, size, opcode); }
>>>>> +
>>>>> +/* spi_nor_map_cmp_erase_type() - compare the map's erase types by size
>>>>> + * @l: member in the left half of the map's erase_type array
>>>>> + * @r: member in the right half of the map's erase_type array
>>>>> + *
>>>>> + * Comparison function used in the sort() call to sort in ascending
>>>>> +order the
>>>>> + * map's erase types, the smallest erase type size being the first
>>>>> +member in the
>>>>> + * sorted erase_type array.
>>>>> + */
>>>>> +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) {
>>>>> + const struct spi_nor_erase_type *left = l, *right = r;
>>>>> +
>>>>> + return left->size - right->size;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * spi_nor_regions_sort_erase_types() - sort erase types in each region
>>>>> + * @map: the erase map of the SPI NOR
>>>>> + *
>>>>> + * Function assumes that the erase types defined in the erase map
>>>>> +are already
>>>>> + * sorted in ascending order, with the smallest erase type size
>>>>> +being the first
>>>>> + * member in the erase_type array. It replicates the sort done for
>>>>> +the map's
>>>>> + * erase types. Each region's erase bitmask will indicate which
>>>>> +erase types are
>>>>> + * supported from the sorted erase types defined in the erase map.
>>>>> + * Sort the all region's erase type at init in order to speed up
>>>>> +the process of
>>>>> + * finding the best erase command at runtime.
>>>>> + */
>>>>> +static void spi_nor_regions_sort_erase_types(struct
>>>>> +spi_nor_erase_map
>>>>> +*map) {
>>>>> + struct spi_nor_erase_region *region = map->regions;
>>>>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>>>>> + int i;
>>>>> + u8 region_erase_mask, sorted_erase_mask;
>>>>> +
>>>>> + while (region) {
>>>>> + region_erase_mask = region->offset &
>>>>> SNOR_ERASE_TYPE_MASK;
>>>>> +
>>>>> + /* Replicate the sort done for the map's erase types. */
>>>>> + sorted_erase_mask = 0;
>>>>> + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
>>>>> + if (erase_type[i].size &&
>>>>> + region_erase_mask & BIT(erase_type[i].idx))
>>>>> + sorted_erase_mask |= BIT(i);
>>>>> +
>>>>> + /* Overwrite erase mask. */
>>>>> + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK)
>>>>> |
>>>>> + sorted_erase_mask;
>>>>> +
>>>>> + region = spi_nor_region_next(region);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + *spi_nor_init_uniform_erase_map() - Initialize uniform erase map
>>>>> + * @map: the erase map of the SPI NOR
>>>>> + * @erase_mask: bitmask encoding erase types that can erase
>>>>> the entire
>>>>> + * flash memory
>>>>> + * @flash_size: the spi nor flash memory size
>>>>> + */
>>>>> +static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map
>> *map,
>>>>> + u8 erase_mask, u64 flash_size) {
>>>>> + /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
>>>>> + map->uniform_region.offset = (erase_mask &
>>>>> SNOR_ERASE_TYPE_MASK) |
>>>>> + SNOR_LAST_REGION;
>>>>> + map->uniform_region.size = flash_size;
>>>>> + map->regions = &map->uniform_region;
>>>>> + map->uniform_erase_type = erase_mask; }
>>>>> +
>>>>> /**
>>>>> * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
>>>>> * @nor: pointer to a 'struct spi_nor'
>>>>> @@ -2224,12 +2621,14 @@ static int spi_nor_parse_bfpt(struct spi_nor
>> *nor,
>>>>> const struct sfdp_parameter_header *bfpt_header,
>>>>> struct spi_nor_flash_parameter *params) {
>>>>> - struct mtd_info *mtd = &nor->mtd;
>>>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>>>> + struct spi_nor_erase_type *erase_type = map->erase_type;
>>>>> struct sfdp_bfpt bfpt;
>>>>> size_t len;
>>>>> int i, cmd, err;
>>>>> u32 addr;
>>>>> u16 half;
>>>>> + u8 erase_mask;
>>>>>
>>>>> /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
>>>>> if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) @@ -2298,7
>>>>> +2697,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>>>> spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
>>>>> }
>>>>>
>>>>> - /* Sector Erase settings. */
>>>>> + /*
>>>>> + * Sector Erase settings. Reinitialize the uniform erase map using the
>>>>> + * Erase Types defined in the bfpt table.
>>>>> + */
>>>>> + erase_mask = 0;
>>>>> + memset(&nor->erase_map, 0, sizeof(nor->erase_map));
>>>>> for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
>>>>> const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
>>>>> u32 erasesize;
>>>>> @@ -2313,18 +2717,25 @@ static int spi_nor_parse_bfpt(struct spi_nor
>>>>> *nor,
>>>>>
>>>>> erasesize = 1U << erasesize;
>>>>> opcode = (half >> 8) & 0xff;
>>>>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>>>> - if (erasesize == SZ_4K) {
>>>>> - nor->erase_opcode = opcode;
>>>>> - mtd->erasesize = erasesize;
>>>>> - break;
>>>>> - }
>>>>> -#endif
>>>>> - if (!mtd->erasesize || mtd->erasesize < erasesize) {
>>>>> - nor->erase_opcode = opcode;
>>>>> - mtd->erasesize = erasesize;
>>>>> - }
>>>>> + erase_mask |= BIT(i);
>>>>> + spi_nor_set_erase_settings_from_bfpt(&erase_type[i],
>>>>> erasesize,
>>>>> + opcode, i);
>>>>> }
>>>>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>>>>> + /*
>>>>> + * Sort all the map's Erase Types in ascending order with the smallest
>>>>> + * erase size being the first member in the erase_type array.
>>>>> + */
>>>>> + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
>>>>> + spi_nor_map_cmp_erase_type, NULL);
>>>>> + /*
>>>>> + * Sort the erase types in the uniform region in order to update the
>>>>> + * uniform_erase_type bitmask. The bitmask will be used later on when
>>>>> + * selecting the uniform erase.
>>>>> + */
>>>>> + spi_nor_regions_sort_erase_types(map);
>>>>> + map->uniform_erase_type = map->uniform_region.offset &
>>>>> + SNOR_ERASE_TYPE_MASK;
>>>>>
>>>>> /* Stop here if not JESD216 rev A or later. */
>>>>> if (bfpt_header->length < BFPT_DWORD_MAX) @@ -2480,6 +2891,9
>> @@
>>>>> static int spi_nor_init_params(struct spi_nor *nor,
>>>>> const struct flash_info *info,
>>>>> struct spi_nor_flash_parameter *params) {
>>>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>>>> + u8 i, erase_mask;
>>>>> +
>>>>> /* Set legacy flash parameters as default. */
>>>>> memset(params, 0, sizeof(*params));
>>>>>
>>>>> @@ -2519,6 +2933,28 @@ static int spi_nor_init_params(struct spi_nor
>> *nor,
>>>>> spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
>>>>> SPINOR_OP_PP, SNOR_PROTO_1_1_1);
>>>>>
>>>>> + /*
>>>>> + * Sector Erase settings. Sort Erase Types in ascending order, with the
>>>>> + * smallest erase size starting at BIT(0).
>>>>> + */
>>>>> + erase_mask = 0;
>>>>> + i = 0;
>>>>> + if (info->flags & SECT_4K_PMC) {
>>>>> + erase_mask |= BIT(i);
>>>>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>>>>> + SPINOR_OP_BE_4K_PMC);
>>>>> + i++;
>>>>> + } else if (info->flags & SECT_4K) {
>>>>> + erase_mask |= BIT(i);
>>>>> + spi_nor_set_erase_type(&map->erase_type[i], 4096u,
>>>>> + SPINOR_OP_BE_4K);
>>>>> + i++;
>>>>> + }
>>>>> + erase_mask |= BIT(i);
>>>>> + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
>>>>> + SPINOR_OP_SE);
>>>>> + spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
>>>>> +
>>>>> /* Select the procedure to set the Quad Enable bit. */
>>>>> if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>>>>> SNOR_HWCAPS_PP_QUAD)) {
>>>>> @@ -2546,20 +2982,20 @@ static int spi_nor_init_params(struct spi_nor
>> *nor,
>>>>> params->quad_enable = info->quad_enable;
>>>>> }
>>>>>
>>>>> - /* Override the parameters with data read from SFDP tables. */
>>>>> - nor->addr_width = 0;
>>>>> - nor->mtd.erasesize = 0;
>>>>> if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
>>>>> !(info->flags & SPI_NOR_SKIP_SFDP)) {
>>>>> struct spi_nor_flash_parameter sfdp_params;
>>>>> + struct spi_nor_erase_map prev_map;
>>>>>
>>>>> memcpy(&sfdp_params, params, sizeof(sfdp_params));
>>>>> - if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
>>>>> - nor->addr_width = 0;
>>>>> - nor->mtd.erasesize = 0;
>>>>> - } else {
>>>>> + memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
>>>>> +
>>>>> + if (spi_nor_parse_sfdp(nor, &sfdp_params))
>>>>> + /* restore previous erase map */
>>>>> + memcpy(&nor->erase_map, &prev_map,
>>>>> + sizeof(nor->erase_map));
>>>>> + else
>>>>> memcpy(params, &sfdp_params, sizeof(*params));
>>>>> - }
>>>>> }
>>>>>
>>>>> return 0;
>>>>> @@ -2668,29 +3104,103 @@ static int spi_nor_select_pp(struct spi_nor
>> *nor,
>>>>> return 0;
>>>>> }
>>>>>
>>>>> -static int spi_nor_select_erase(struct spi_nor *nor,
>>>>> - const struct flash_info *info)
>>>>> +/*
>>>>> + * 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.
>>>>> + *
>>>>> + * Once the optimum uniform sector erase command is found, disable
>>>>> +all the
>>>>> + * other.
>>>>> + *
>>>>> + * Return: pointer to erase type on success, NULL otherwise.
>>>>> + */
>>>>> +static const struct spi_nor_erase_type *
>>>>> +spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
>>>>> + const u32 wanted_size)
>>>>> {
>>>>> - struct mtd_info *mtd = &nor->mtd;
>>>>> + const struct spi_nor_erase_type *tested_erase, *erase = NULL;
>>>>> + int i;
>>>>> + u8 uniform_erase_type = map->uniform_erase_type;
>>>>>
>>>>> - /* Do nothing if already configured from SFDP. */
>>>>> - if (mtd->erasesize)
>>>>> - return 0;
>>>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>>>> + if (!(uniform_erase_type & BIT(i)))
>>>>> + continue;
>>>>> +
>>>>> + tested_erase = &map->erase_type[i];
>>>>> +
>>>>> + /*
>>>>> + * If the current erase size is the one, stop here:
>>>>> + * we have found the right uniform Sector Erase command.
>>>>> + */
>>>>> + if (tested_erase->size == wanted_size) {
>>>>> + erase = tested_erase;
>>>>> + break;
>>>>> + }
>>>>>
>>>>> + /*
>>>>> + * Otherwise, the current erase size is still a valid canditate.
>>>>> + * Select the biggest valid candidate.
>>>>> + */
>>>>> + if (!erase && tested_erase->size)
>>>>> + erase = tested_erase;
>>>>> + /* keep iterating to find the wanted_size */
>>>>> + }
>>>>> +
>>>>> + if (!erase)
>>>>> + return NULL;
>>>>> +
>>>>> + /* Disable all other Sector Erase commands. */
>>>>> + map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
>>>>> + map->uniform_erase_type |= BIT(erase - map->erase_type);
>>>>> + return erase;
>>>>> +}
>>>>> +
>>>>> +static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) {
>>>>> + struct spi_nor_erase_map *map = &nor->erase_map;
>>>>> + const struct spi_nor_erase_type *erase = NULL;
>>>>> + struct mtd_info *mtd = &nor->mtd;
>>>>> + int i;
>>>>> +
>>>>> + /*
>>>>> + * The previous implementation handling Sector Erase commands
>>>>> assumed
>>>>> + * that the SPI flash memory has an uniform layout then used only one
>>>>> + * of the supported erase sizes for all Sector Erase commands.
>>>>> + * So to be backward compatible, the new implementation also tries to
>>>>> + * manage the SPI flash memory as uniform with a single erase sector
>>>>> + * size, when possible.
>>>>> + */
>>>>> #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
>>>>> + wanted_size = 4096u;
>>>>> #endif
>>>>> - {
>>>>> - nor->erase_opcode = SPINOR_OP_SE;
>>>>> - mtd->erasesize = info->sector_size;
>>>>> +
>>>>> + if (spi_nor_has_uniform_erase(nor)) {
>>>>> + erase = spi_nor_select_uniform_erase(map, wanted_size);
>>>>> + if (!erase)
>>>>> + return -EINVAL;
>>>>> + nor->erase_opcode = erase->opcode;
>>>>> + mtd->erasesize = erase->size;
>>>>> + return 0;
>>>>> }
>>>>> +
>>>>> + /*
>>>>> + * For non-uniform SPI flash memory, set mtd->erasesize to the
>>>>> + * maximum erase sector size. No need to set nor->erase_opcode.
>>>>> + */
>>>>> + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
>>>>> + if (map->erase_type[i].size) {
>>>>> + erase = &map->erase_type[i];
>>>>> + break;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + if (!erase)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + mtd->erasesize = erase->size;
>>>>> return 0;
>>>>> }
>>>>>
>>>>> @@ -2737,7 +3247,7 @@ static int spi_nor_setup(struct spi_nor *nor,
>>>>> const struct flash_info *info,
>>>>> }
>>>>>
>>>>> /* Select the Sector Erase command. */
>>>>> - err = spi_nor_select_erase(nor, info);
>>>>> + err = spi_nor_select_erase(nor, info->sector_size);
>>>>> if (err) {
>>>>> dev_err(nor->dev,
>>>>> "can't select erase settings supported by both the SPI
>>>>> controller and memory.\n"); diff --git a/include/linux/mtd/spi-nor.h
>>>>> b/include/linux/mtd/spi-nor.h index 09a10fd..a873a0b 100644
>>>>> --- a/include/linux/mtd/spi-nor.h
>>>>> +++ b/include/linux/mtd/spi-nor.h
>>>>> @@ -240,6 +240,94 @@ enum spi_nor_option_flags { };
>>>>>
>>>>> /**
>>>>> + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
>>>>> + * @size: the size of the sector/block erased by the erase type.
>>>>> + * JEDEC JESD216B imposes erase sizes to be a power of 2.
>>>>> + * @size_shift: @size is a power of 2, the shift is stored in
>>>>> + * @size_shift.
>>>>> + * @size_mask: the size mask based on @size_shift.
>>>>> + * @opcode: the SPI command op code to erase the
>> sector/block.
>>>>> + * @idx: Erase Type index as sorted in the Basic Flash Parameter
>>>>> + * Table. It will be used to synchronize the supported
>>>>> + * Erase Types with the ones identified in the SFDP
>>>>> + * optional tables.
>>>>> + */
>>>>> +struct spi_nor_erase_type {
>>>>> + u32 size;
>>>>> + u32 size_shift;
>>>>> + u32 size_mask;
>>>>> + u8 opcode;
>>>>> + u8 idx;
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * struct spi_nor_erase_command - Used for non-uniform erases
>>>>> + * The structure is used to describe a list of erase commands to be
>>>>> +executed
>>>>> + * once we validate that the erase can be performed. The elements
>>>>> +in the list
>>>>> + * are run-length encoded.
>>>>> + * @list: for inclusion into the list of erase commands.
>>>>> + * @count: how many times the same erase command
>> should be
>>>>> + * consecutively used.
>>>>> + * @size: the size of the sector/block erased by the command.
>>>>> + * @opcode: the SPI command op code to erase the
>> sector/block.
>>>>> + */
>>>>> +struct spi_nor_erase_command {
>>>>> + struct list_head list;
>>>>> + u32 count;
>>>>> + u32 size;
>>>>> + u8 opcode;
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase
>> region
>>>>> + * @offset: the offset in the data array of erase region
>> start.
>>>>> + * LSB bits are used as a bitmask encoding flags to
>>>>> + * determine if this region is overlaid, if this region is
>>>>> + * the last in the SPI NOR flash memory and to indicate
>>>>> + * all the supported erase commands inside this region.
>>>>> + * The erase types are sorted in ascending order with the
>>>>> + * smallest Erase Type size being at BIT(0).
>>>>> + * @size: the size of the region in bytes.
>>>>> + */
>>>>> +struct spi_nor_erase_region {
>>>>> + u64 offset;
>>>>> + u64 size;
>>>>> +};
>>>>> +
>>>>> +#define SNOR_ERASE_TYPE_MAX 4
>>>>> +#define SNOR_ERASE_TYPE_MASK
>>>>> GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
>>>>> +
>>>>> +#define SNOR_LAST_REGION BIT(4)
>>>>> +#define SNOR_OVERLAID_REGION BIT(5)
>>>>> +
>>>>> +#define SNOR_ERASE_FLAGS_MAX 6
>>>>> +#define SNOR_ERASE_FLAGS_MASK
>>>>> GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
>>>>> +
>>>>> +/**
>>>>> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
>>>>> + * @regions: array of erase regions. The regions are
>> consecutive in
>>>>> + * address space. Walking through the regions is done
>>>>> + * incrementally.
>>>>> + * @uniform_region: a pre-allocated erase region for SPI NOR with a
>> uniform
>>>>> + * sector size (legacy implementation).
>>>>> + * @erase_type: an array of erase types shared by all the regions.
>>>>> + * The erase types are sorted in ascending order, with the
>>>>> + * smallest Erase Type size being the first member in the
>>>>> + * erase_type array.
>>>>> + * @uniform_erase_type: bitmask encoding erase types that can erase
>>>>> the
>>>>> + * entire memory. This member is completed at init by
>>>>> + * uniform and non-uniform SPI NOR flash memories if
>>>>> they
>>>>> + * support at least one erase type that can erase the
>>>>> + * entire memory.
>>>>> + */
>>>>> +struct spi_nor_erase_map {
>>>>> + struct spi_nor_erase_region *regions;
>>>>> + struct spi_nor_erase_region uniform_region;
>>>>> + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
>>>>> + u8 uniform_erase_type;
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> * struct flash_info - Forward declaration of a structure used internally by
>>>>> * spi_nor_scan()
>>>>> */
>>>>> @@ -263,6 +351,7 @@ struct flash_info;
>>>>> * @write_proto: the SPI protocol for write operations
>>>>> * @reg_proto the SPI protocol for read_reg/write_reg/erase
>>>>> operations
>>>>> * @cmd_buf: used by the write_reg
>>>>> + * @erase_map: the erase map of the SPI NOR
>>>>> * @prepare: [OPTIONAL] do some preparations for the
>>>>> * read/write/erase/lock/unlock operations
>>>>> * @unprepare: [OPTIONAL] do some post work after the
>>>>> @@ -298,6 +387,7 @@ struct spi_nor {
>>>>> bool sst_write_second;
>>>>> u32 flags;
>>>>> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>>>>> + struct spi_nor_erase_map erase_map;
>>>>>
>>>>> int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
>>>>> void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); @@ -
>>>>> 318,6 +408,23 @@ struct spi_nor {
>>>>> void *priv;
>>>>> };
>>>>>
>>>>> +static u64 __maybe_unused
>>>>> +spi_nor_region_is_last(const struct spi_nor_erase_region *region) {
>>>>> + return region->offset & SNOR_LAST_REGION; }
>>>>> +
>>>>> +static u64 __maybe_unused
>>>>> +spi_nor_region_end(const struct spi_nor_erase_region *region) {
>>>>> + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; }
>>>>> +
>>>>> +static bool __maybe_unused spi_nor_has_uniform_erase(const struct
>>>>> +spi_nor *nor) {
>>>>> + return !!nor->erase_map.uniform_erase_type;
>>>>> +}
>>>>> +
>>>>> static inline void spi_nor_set_flash_node(struct spi_nor *nor,
>>>>> struct device_node *np)
>>>>> {
>>>>> --
>>>>> 2.9.4
>>>>>
>>>>>
>>>>> ______________________________________________________
>>>>> Linux MTD discussion mailing list
>>>>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fli
>>>>> sts.infr
>>>>> adead.org%2Fmailman%2Flistinfo%2Flinux-
>>>>>
>> mtd%2F&amp;data=02%7C01%7Cyogeshnarayan.gaur%40nxp.com%7C3c782e5
>>>>>
>> 2b7fd4a8b9af008d617fd5154%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
>>>>>
>> 7C0%7C636722774108718782&amp;sdata=cSpHUDMi0LDV%2FxAYj6i6piSi3gn%
>>>>> 2BDGAMWKoOx3%2F5%2BsU%3D&amp;reserved=0
>>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flist
>>> s.infradead.org%2Fmailman%2Flistinfo%2Flinux-
>> mtd%2F&amp;data=02%7C01%7
>>>
>> Cyogeshnarayan.gaur%40nxp.com%7C76e7e1555f4a4cda378008d63385480b%
>> 7C686
>>>
>> ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636753044876199155&amp;s
>> data=0
>>>
>> vdEcONHlufYQW%2BD7K6lVaPByXMuDH5YAyx%2FE%2FC3eno%3D&amp;reserv
>> ed=0
>>>

2018-10-17 09:54:01

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Wed, 17 Oct 2018 07:46:30 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi Boris,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Wednesday, October 17, 2018 1:00 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; linux-arm-
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Wed, 17 Oct 2018 09:10:45 +0200
> > Boris Brezillon <[email protected]> wrote:
> >
> > > On Wed, 17 Oct 2018 09:07:24 +0200
> > > Boris Brezillon <[email protected]> wrote:
> > >
> > > > On Wed, 17 Oct 2018 02:07:43 +0000
> > > > Yogesh Narayan Gaur <[email protected]> wrote:
> > > >
> > > > > >
> > > > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > > > For my connected flash part, jedec ID read points to s25fl512s. I
> > > > > have asked my board team to confirm the name of exact connected
> > > > > flash part. When I check the data sheet of s25fs512s, it also
> > > > > points to the same Jedec ID information. { "s25fl512s",
> > > > > INFO(0x010220, 0x4d00, 256
> > > > > * 1024, 256, ....}
> > > > >
> > > > > But as stated earlier, if I skip reading SFDP or read using 1-1-1
> > > > > protocol then read are always correct. For 1-4-4 protocol read are
> > > > > wrong and on further debugging found that Read code of 0x6C is
> > > > > being send as opcode instead of 0xEC.
> > > > >
> > > > > If I revert this patch, reads are working fine.
> > > >
> > > > Can you try with the following patch?
> > > >
> > >
> > > Hm, nevermind. The problem is actually not related to 4B vs non-4B
> > > mode but 1-1-4 vs 1-4-4 modes.
> Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read opcode is being sent for 1-1-4 mode.
> > >
> >
> > Can you try with this patch applied?
> >
> With suggested patch, read for protocol 1-4-4 working correctly.
>
> [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
>

Okay, let's try this one to see why the SMPT parsing fails.

--->8---
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9407ca5f9443..564882c87641 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2855,23 +2855,31 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
* spi_nor_get_map_in_use() - get the configuration map in use
* @nor: pointer to a 'struct spi_nor'
* @smpt: pointer to the sector map parameter table
+ * @smpt_len: number of entries in the smpt array
*/
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
+ u32 smpt_len)
{
const u32 *ret = NULL;
- u32 i, addr;
+ u32 i, addr, nmaps;
int err;
u8 addr_width, read_opcode, read_dummy;
u8 read_data_mask, data_byte, map_id;
+ bool map_id_is_valid = false;

addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode;

+ for (i = 0; i < smpt_len; i++)
+ pr_info("%s:%i smpt[%d] = %08x\n", __func__, __LINE__, i, smpt[i]);
+
map_id = 0;
- i = 0;
/* Determine if there are any optional Detection Command Descriptors */
- while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ for (i = 0; i < smpt_len; i++) {
+ if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
+ break;
+
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
@@ -2887,18 +2895,40 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
* Configuration that is currently in use.
*/
map_id = map_id << 1 | !!(data_byte & read_data_mask);
+ map_id_is_valid = true;
i = i + 2;
}

+ if (map_id_is_valid)
+ pr_info("%s:%i map_id = %d\n", __func__, __LINE__, map_id);
+ else
+ pr_info("%s:%i no map_id\n", __func__, __LINE__);
+
/* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
- if (smpt[i] & SMPT_DESC_END)
- goto out;
+ for (nmaps = 0; i < smpt_len; nmaps++) {
+ if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
+ continue;
+
+ if (!map_id_is_valid) {
+ /*
+ * Command descriptors are optional but if we didn't
+ * find any, we should ensure we have only one map.
+ */
+ if (nmaps) {
+ ret = NULL;
+ break;
+ }
+
+ ret = smpt + i;
+ } else if (map_id == SMPT_MAP_ID(smpt[i])) {
+ ret = smpt + i;
+ break;
+ }
+
/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}

- ret = smpt + i;
/* fall through */
out:
nor->addr_width = addr_width;
@@ -3020,7 +3050,7 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
for (i = 0; i < smpt_header->length; i++)
smpt[i] = le32_to_cpu(smpt[i]);

- sector_map = spi_nor_get_map_in_use(nor, smpt);
+ sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
if (!sector_map) {
ret = -EINVAL;
goto out;
@@ -3132,6 +3162,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
switch (SFDP_PARAM_HEADER_ID(param_header)) {
case SFDP_SECTOR_MAP_ID:
err = spi_nor_parse_smpt(nor, param_header);
+ if (err) {
+ dev_warn(dev,
+ "failed to parse SMPT (err = %d)\n",
+ err);
+ /*
+ * SMPT parsing is optional, let's not drop
+ * all information we extracted so far just
+ * because it failed.
+ */
+ err = 0;
+ }
break;

default:

2018-10-22 06:05:04

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi Boris, Tudor,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Wednesday, October 17, 2018 3:23 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Wed, 17 Oct 2018 07:46:30 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi Boris,
> >
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:[email protected]]
> > > Sent: Wednesday, October 17, 2018 1:00 PM
> > > To: Yogesh Narayan Gaur <[email protected]>
> > > Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > > <[email protected]>; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > linux-arm- [email protected]; [email protected]
> > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > SFDP SPI NOR flash memories
> > >
> > > On Wed, 17 Oct 2018 09:10:45 +0200
> > > Boris Brezillon <[email protected]> wrote:
> > >
> > > > On Wed, 17 Oct 2018 09:07:24 +0200 Boris Brezillon
> > > > <[email protected]> wrote:
> > > >
> > > > > On Wed, 17 Oct 2018 02:07:43 +0000 Yogesh Narayan Gaur
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > >
> > > > > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > > > > For my connected flash part, jedec ID read points to
> > > > > > s25fl512s. I have asked my board team to confirm the name of
> > > > > > exact connected flash part. When I check the data sheet of
> > > > > > s25fs512s, it also points to the same Jedec ID information. {
> > > > > > "s25fl512s", INFO(0x010220, 0x4d00, 256
> > > > > > * 1024, 256, ....}
> > > > > >
> > > > > > But as stated earlier, if I skip reading SFDP or read using
> > > > > > 1-1-1 protocol then read are always correct. For 1-4-4
> > > > > > protocol read are wrong and on further debugging found that
> > > > > > Read code of 0x6C is being send as opcode instead of 0xEC.
> > > > > >
> > > > > > If I revert this patch, reads are working fine.
> > > > >
> > > > > Can you try with the following patch?
> > > > >
> > > >
> > > > Hm, nevermind. The problem is actually not related to 4B vs non-4B
> > > > mode but 1-1-4 vs 1-4-4 modes.
> > Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read
> opcode is being sent for 1-1-4 mode.
> > > >
> > >
> > > Can you try with this patch applied?
> > >
> > With suggested patch, read for protocol 1-4-4 working correctly.
> >
> > [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> > [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> > [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> > [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
> >
>
> Okay, let's try this one to see why the SMPT parsing fails.
>

Please find the below info for the SMPT table read from the flash
[ 1.631085] spi_nor_get_map_in_use:2880 smpt[0]=08ff65fc
[ 1.636393] spi_nor_get_map_in_use:2880 smpt[1]=00000004
[ 1.641696] spi_nor_get_map_in_use:2880 smpt[2]=04ff65fc
[ 1.646998] spi_nor_get_map_in_use:2880 smpt[3]=00000002
[ 1.652302] spi_nor_get_map_in_use:2880 smpt[4]=02ff65fd
[ 1.657606] spi_nor_get_map_in_use:2880 smpt[5]=00000004
[ 1.662911] spi_nor_get_map_in_use:2880 smpt[6]=ff0201fe
[ 1.668215] spi_nor_get_map_in_use:2880 smpt[7]=00007ff1
[ 1.673520] spi_nor_get_map_in_use:2880 smpt[8]=00037ff4
[ 1.678825] spi_nor_get_map_in_use:2880 smpt[9]=03fbfff4
[ 1.684128] spi_nor_get_map_in_use:2880 smpt[10]=ff0203fe
[ 1.689519] spi_nor_get_map_in_use:2880 smpt[11]=03fbfff4
[ 1.694911] spi_nor_get_map_in_use:2880 smpt[12]=00037ff4
[ 1.700303] spi_nor_get_map_in_use:2880 smpt[13]=00007ff1
[ 1.705693] spi_nor_get_map_in_use:2880 smpt[14]=ff0005ff
[ 1.711081] spi_nor_get_map_in_use:2880 smpt[15]=03fffff4
[ 1.716470] spi_nor_get_map_in_use:2910 NO map_id
[ 1.721165] spi_nor_parse_smpt:3055
[ 1.724645] m25p80 spi0.0: failed to parse SMPT (err = -22)

I have checked that all these information are matching with the fields specified in this flash data sheet [1], page 131-133.

Below is the full debug patch.

--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2863,22 +2863,28 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
* @nor: pointer to a 'struct spi_nor'
* @smpt: pointer to the sector map parameter table
*/
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
{
const u32 *ret = NULL;
- u32 i, addr;
+ u32 i, addr, nmaps;
int err;
u8 addr_width, read_opcode, read_dummy;
u8 read_data_mask, data_byte, map_id;
+ bool map_id_is_valid = false;

addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode;

+ for (i = 0; i<smpt_len; i++)
+ pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
+
map_id = 0;
- i = 0;
/* Determine if there are any optional Detection Command Descriptors */
- while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ for (i = 0; i< smpt_len; i++) {
+ if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
+ break;
+
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
@@ -2894,18 +2900,35 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
* Configuration that is currently in use.
*/
map_id = map_id << 1 | !!(data_byte & read_data_mask);
+ map_id_is_valid = true;
i = i + 2;
}

- /* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
- if (smpt[i] & SMPT_DESC_END)
- goto out;
+ if (map_id_is_valid)
+ pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
+ else
+ pr_info("%s:%i NO map_id\n", __func__, __LINE__);
+
+ for (nmaps = 0; nmaps< smpt_len; nmaps++) {
+ if(!(smpt[nmaps] & SMPT_DESC_TYPE_MAP))
+ continue;
+
+ if(!map_id_is_valid) {
+ if (nmaps) {
+ ret = NULL;
+ break;
+ }
+
+ ret = smpt+nmaps;
+ } else if (map_id == SMPT_MAP_ID(smpt[nmaps])) {
+ ret = smpt+nmaps;
+ break;
+ }
+
/* increment the table index to the next map */
- i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
+ nmaps += SMPT_MAP_REGION_COUNT(smpt[nmaps]) + 1;
}

- ret = smpt + i;
/* fall through */
out:
nor->addr_width = addr_width;
@@ -3027,8 +3050,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
for (i = 0; i < smpt_header->length; i++)
smpt[i] = le32_to_cpu(smpt[i]);

- sector_map = spi_nor_get_map_in_use(nor, smpt);
+ sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
if (!sector_map) {
+ pr_info("%s:%i\n", __func__, __LINE__);
ret = -EINVAL;
goto out;
}
@@ -3139,6 +3167,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
switch (SFDP_PARAM_HEADER_ID(param_header)) {
case SFDP_SECTOR_MAP_ID:
err = spi_nor_parse_smpt(nor, param_header);
+ if (err) {
+ dev_warn(dev,
+ "failed to parse SMPT (err = %d)\n",
+ err);
+ /*
+ * SMPT parsing is optional, let's not drop
+ * all information we extracted so far just
+ * because it failed.
+ */
+ err = 0;
+ }
break;

default:
[1] http://www.cypress.com/documentation/datasheets/s25fs512s-512-mbit-18-v-serial-peripheral-interface-multi-io-non-volatile

--
Regards
Yogesh Gaur
[...]

2018-10-22 07:36:24

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 06:04:13 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi Boris, Tudor,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Wednesday, October 17, 2018 3:23 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; linux-arm-
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Wed, 17 Oct 2018 07:46:30 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > Hi Boris,
> > >
> > > > -----Original Message-----
> > > > From: Boris Brezillon [mailto:[email protected]]
> > > > Sent: Wednesday, October 17, 2018 1:00 PM
> > > > To: Yogesh Narayan Gaur <[email protected]>
> > > > Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > > > <[email protected]>; [email protected];
> > > > [email protected]; [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > linux-arm- [email protected]; [email protected]
> > > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > > SFDP SPI NOR flash memories
> > > >
> > > > On Wed, 17 Oct 2018 09:10:45 +0200
> > > > Boris Brezillon <[email protected]> wrote:
> > > >
> > > > > On Wed, 17 Oct 2018 09:07:24 +0200 Boris Brezillon
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > On Wed, 17 Oct 2018 02:07:43 +0000 Yogesh Narayan Gaur
> > > > > > <[email protected]> wrote:
> > > > > >
> > > > > > > >
> > > > > > > Actually there is no entry of s25fs512s in current spi-nor.c file.
> > > > > > > For my connected flash part, jedec ID read points to
> > > > > > > s25fl512s. I have asked my board team to confirm the name of
> > > > > > > exact connected flash part. When I check the data sheet of
> > > > > > > s25fs512s, it also points to the same Jedec ID information. {
> > > > > > > "s25fl512s", INFO(0x010220, 0x4d00, 256
> > > > > > > * 1024, 256, ....}
> > > > > > >
> > > > > > > But as stated earlier, if I skip reading SFDP or read using
> > > > > > > 1-1-1 protocol then read are always correct. For 1-4-4
> > > > > > > protocol read are wrong and on further debugging found that
> > > > > > > Read code of 0x6C is being send as opcode instead of 0xEC.
> > > > > > >
> > > > > > > If I revert this patch, reads are working fine.
> > > > > >
> > > > > > Can you try with the following patch?
> > > > > >
> > > > >
> > > > > Hm, nevermind. The problem is actually not related to 4B vs non-4B
> > > > > mode but 1-1-4 vs 1-4-4 modes.
> > > Yes, that's only I have stated in my first mail that instead of 1-4-4 mode read
> > opcode is being sent for 1-1-4 mode.
> > > > >
> > > >
> > > > Can you try with this patch applied?
> > > >
> > > With suggested patch, read for protocol 1-4-4 working correctly.
> > >
> > > [ 1.625360] m25p80 spi0.0: found s25fl512s, expected m25p80
> > > [ 1.631094] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > > [ 1.636661] 261 8c4c780 opcode(read:eb, pp:2, erase:d8)
> > > [ 1.641878] 266 8c4c780 opcode(read:ec, pp:12, erase:dc)
> > > [ 1.647200] m25p80 spi0.0: s25fl512s (65536 Kbytes)
> > >
> >
> > Okay, let's try this one to see why the SMPT parsing fails.
> >
>
> Please find the below info for the SMPT table read from the flash
> [ 1.631085] spi_nor_get_map_in_use:2880 smpt[0]=08ff65fc
> [ 1.636393] spi_nor_get_map_in_use:2880 smpt[1]=00000004
> [ 1.641696] spi_nor_get_map_in_use:2880 smpt[2]=04ff65fc
> [ 1.646998] spi_nor_get_map_in_use:2880 smpt[3]=00000002
> [ 1.652302] spi_nor_get_map_in_use:2880 smpt[4]=02ff65fd
> [ 1.657606] spi_nor_get_map_in_use:2880 smpt[5]=00000004
> [ 1.662911] spi_nor_get_map_in_use:2880 smpt[6]=ff0201fe
> [ 1.668215] spi_nor_get_map_in_use:2880 smpt[7]=00007ff1
> [ 1.673520] spi_nor_get_map_in_use:2880 smpt[8]=00037ff4
> [ 1.678825] spi_nor_get_map_in_use:2880 smpt[9]=03fbfff4
> [ 1.684128] spi_nor_get_map_in_use:2880 smpt[10]=ff0203fe
> [ 1.689519] spi_nor_get_map_in_use:2880 smpt[11]=03fbfff4
> [ 1.694911] spi_nor_get_map_in_use:2880 smpt[12]=00037ff4
> [ 1.700303] spi_nor_get_map_in_use:2880 smpt[13]=00007ff1
> [ 1.705693] spi_nor_get_map_in_use:2880 smpt[14]=ff0005ff
> [ 1.711081] spi_nor_get_map_in_use:2880 smpt[15]=03fffff4
> [ 1.716470] spi_nor_get_map_in_use:2910 NO map_id
> [ 1.721165] spi_nor_parse_smpt:3055
> [ 1.724645] m25p80 spi0.0: failed to parse SMPT (err = -22)
>
> I have checked that all these information are matching with the fields specified in this flash data sheet [1], page 131-133.
>
> Below is the full debug patch.
>
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -2863,22 +2863,28 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
> * @nor: pointer to a 'struct spi_nor'
> * @smpt: pointer to the sector map parameter table
> */
> -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
> {
> const u32 *ret = NULL;
> - u32 i, addr;
> + u32 i, addr, nmaps;
> int err;
> u8 addr_width, read_opcode, read_dummy;
> u8 read_data_mask, data_byte, map_id;
> + bool map_id_is_valid = false;
>
> addr_width = nor->addr_width;
> read_dummy = nor->read_dummy;
> read_opcode = nor->read_opcode;
>
> + for (i = 0; i<smpt_len; i++)
> + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
> +
> map_id = 0;
> - i = 0;
> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {
> + if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> @@ -2894,18 +2900,35 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> * Configuration that is currently in use.
> */
> map_id = map_id << 1 | !!(data_byte & read_data_mask);
> + map_id_is_valid = true;
> i = i + 2;
> }
>
> - /* Find the matching configuration map */
> - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> - if (smpt[i] & SMPT_DESC_END)
> - goto out;
> + if (map_id_is_valid)
> + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> + else
> + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> +
> + for (nmaps = 0; nmaps< smpt_len; nmaps++) {
> + if(!(smpt[nmaps] & SMPT_DESC_TYPE_MAP))

My bad, this should be

if(smpt[nmaps] & SMPT_DESC_TYPE_MAP)

Can you try again with this change?

> + continue;
> +
> + if(!map_id_is_valid) {
> + if (nmaps) {
> + ret = NULL;
> + break;
> + }
> +
> + ret = smpt+nmaps;
> + } else if (map_id == SMPT_MAP_ID(smpt[nmaps])) {
> + ret = smpt+nmaps;
> + break;
> + }
> +
> /* increment the table index to the next map */
> - i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> + nmaps += SMPT_MAP_REGION_COUNT(smpt[nmaps]) + 1;
> }
>
> - ret = smpt + i;
> /* fall through */
> out:
> nor->addr_width = addr_width;
> @@ -3027,8 +3050,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
> for (i = 0; i < smpt_header->length; i++)
> smpt[i] = le32_to_cpu(smpt[i]);
>
> - sector_map = spi_nor_get_map_in_use(nor, smpt);
> + sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
> if (!sector_map) {
> + pr_info("%s:%i\n", __func__, __LINE__);
> ret = -EINVAL;
> goto out;
> }
> @@ -3139,6 +3167,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
> switch (SFDP_PARAM_HEADER_ID(param_header)) {
> case SFDP_SECTOR_MAP_ID:
> err = spi_nor_parse_smpt(nor, param_header);
> + if (err) {
> + dev_warn(dev,
> + "failed to parse SMPT (err = %d)\n",
> + err);
> + /*
> + * SMPT parsing is optional, let's not drop
> + * all information we extracted so far just
> + * because it failed.
> + */
> + err = 0;
> + }
> break;
>
> default:
> [1] http://www.cypress.com/documentation/datasheets/s25fs512s-512-mbit-18-v-serial-peripheral-interface-multi-io-non-volatile
>
> --
> Regards
> Yogesh Gaur
> [...]


2018-10-22 08:05:33

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 06:04:13 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
> {
> const u32 *ret = NULL;
> - u32 i, addr;
> + u32 i, addr, nmaps;
> int err;
> u8 addr_width, read_opcode, read_dummy;
> u8 read_data_mask, data_byte, map_id;
> + bool map_id_is_valid = false;
>
> addr_width = nor->addr_width;
> read_dummy = nor->read_dummy;
> read_opcode = nor->read_opcode;
>
> + for (i = 0; i<smpt_len; i++)
> + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
> +
> map_id = 0;
> - i = 0;
> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {
> + if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);

Could you also print the ->addr_width, ->read_dummy and ->read_opcode
here?

> @@ -2894,18 +2900,35 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> * Configuration that is currently in use.
> */
> map_id = map_id << 1 | !!(data_byte & read_data_mask);
> + map_id_is_valid = true;
> i = i + 2;
> }


2018-10-22 08:56:28

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

HI,


> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 1:32 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Mark Brown
> <[email protected]>
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 06:04:13 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > u32 *smpt)
> > +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > +u32 *smpt, u32 smpt_len)
> > {
> > const u32 *ret = NULL;
> > - u32 i, addr;
> > + u32 i, addr, nmaps;
> > int err;
> > u8 addr_width, read_opcode, read_dummy;
> > u8 read_data_mask, data_byte, map_id;
> > + bool map_id_is_valid = false;
> >
> > addr_width = nor->addr_width;
> > read_dummy = nor->read_dummy;
> > read_opcode = nor->read_opcode;
> >
> > + for (i = 0; i<smpt_len; i++)
> > + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__,
> > + i, smpt[i]);
> > +
> > map_id = 0;
> > - i = 0;
> > /* Determine if there are any optional Detection Command Descriptors */
> > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > + for (i = 0; i< smpt_len; i++) {
> > + if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
> > + break;
> > +
> > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > nor->read_dummy = spi_nor_smpt_read_dummy(nor,
> > smpt[i]);
>
> Could you also print the ->addr_width, ->read_dummy and ->read_opcode here?
>
It didn't showing any print messages here, did above line " if (!(smpt[i] & SMPT_DESC_TYPE_MAP))" also needs to be changes to " if ((smpt[i] & SMPT_DESC_TYPE_MAP))"?

Below is the log, with the suggested change of modifying as
> + for (nmaps = 0; nmaps< smpt_len; nmaps++) {
> + if((smpt[nmaps] & SMPT_DESC_TYPE_MAP))

[ 1.625992] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.631681] spi_nor_get_map_in_use:2880 smpt[0]=08ff65fc
[ 1.636988] spi_nor_get_map_in_use:2880 smpt[1]=00000004
[ 1.642292] spi_nor_get_map_in_use:2880 smpt[2]=04ff65fc
[ 1.647596] spi_nor_get_map_in_use:2880 smpt[3]=00000002
[ 1.652898] spi_nor_get_map_in_use:2880 smpt[4]=02ff65fd
[ 1.658200] spi_nor_get_map_in_use:2880 smpt[5]=00000004
[ 1.663503] spi_nor_get_map_in_use:2880 smpt[6]=ff0201fe
[ 1.668806] spi_nor_get_map_in_use:2880 smpt[7]=00007ff1
[ 1.674108] spi_nor_get_map_in_use:2880 smpt[8]=00037ff4
[ 1.679412] spi_nor_get_map_in_use:2880 smpt[9]=03fbfff4
[ 1.684715] spi_nor_get_map_in_use:2880 smpt[10]=ff0203fe
[ 1.690105] spi_nor_get_map_in_use:2880 smpt[11]=03fbfff4
[ 1.695495] spi_nor_get_map_in_use:2880 smpt[12]=00037ff4
[ 1.700886] spi_nor_get_map_in_use:2880 smpt[13]=00007ff1
[ 1.706275] spi_nor_get_map_in_use:2880 smpt[14]=ff0005ff
[ 1.711664] spi_nor_get_map_in_use:2880 smpt[15]=03fffff4
[ 1.717053] spi_nor_get_map_in_use:2910 NO map_id
[ 1.721777] m25p80 spi0.0: s25fl512s (65536 Kbytes)

Didn't throw any return error.

--
Regards,
Yogesh Gaur


> > @@ -2894,18 +2900,35 @@ static const u32 *spi_nor_get_map_in_use(struct
> spi_nor *nor, const u32 *smpt)
> > * Configuration that is currently in use.
> > */
> > map_id = map_id << 1 | !!(data_byte & read_data_mask);
> > + map_id_is_valid = true;
> > i = i + 2;
> > }


2018-10-22 09:10:29

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 08:32:21 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> HI,
>
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Monday, October 22, 2018 1:32 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: Cyrille Pitchen <[email protected]>; Tudor Ambarus
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; linux-arm-
> > [email protected]; [email protected]; Mark Brown
> > <[email protected]>
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Mon, 22 Oct 2018 06:04:13 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > > u32 *smpt)
> > > +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > > +u32 *smpt, u32 smpt_len)
> > > {
> > > const u32 *ret = NULL;
> > > - u32 i, addr;
> > > + u32 i, addr, nmaps;
> > > int err;
> > > u8 addr_width, read_opcode, read_dummy;
> > > u8 read_data_mask, data_byte, map_id;
> > > + bool map_id_is_valid = false;
> > >
> > > addr_width = nor->addr_width;
> > > read_dummy = nor->read_dummy;
> > > read_opcode = nor->read_opcode;
> > >
> > > + for (i = 0; i<smpt_len; i++)
> > > + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__,
> > > + i, smpt[i]);
> > > +
> > > map_id = 0;
> > > - i = 0;
> > > /* Determine if there are any optional Detection Command Descriptors */
> > > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > > + for (i = 0; i< smpt_len; i++) {
> > > + if (!(smpt[i] & SMPT_DESC_TYPE_MAP))
> > > + break;
> > > +
> > > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > > nor->read_dummy = spi_nor_smpt_read_dummy(nor,
> > > smpt[i]);
> >
> > Could you also print the ->addr_width, ->read_dummy and ->read_opcode here?
> >
> It didn't showing any print messages here, did above line " if (!(smpt[i] & SMPT_DESC_TYPE_MAP))" also needs to be changes to " if ((smpt[i] & SMPT_DESC_TYPE_MAP))"?

Yes.

>
> Below is the log, with the suggested change of modifying as
> > + for (nmaps = 0; nmaps< smpt_len; nmaps++) {
> > + if((smpt[nmaps] & SMPT_DESC_TYPE_MAP))
>
> [ 1.625992] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.631681] spi_nor_get_map_in_use:2880 smpt[0]=08ff65fc
> [ 1.636988] spi_nor_get_map_in_use:2880 smpt[1]=00000004
> [ 1.642292] spi_nor_get_map_in_use:2880 smpt[2]=04ff65fc
> [ 1.647596] spi_nor_get_map_in_use:2880 smpt[3]=00000002
> [ 1.652898] spi_nor_get_map_in_use:2880 smpt[4]=02ff65fd
> [ 1.658200] spi_nor_get_map_in_use:2880 smpt[5]=00000004
> [ 1.663503] spi_nor_get_map_in_use:2880 smpt[6]=ff0201fe
> [ 1.668806] spi_nor_get_map_in_use:2880 smpt[7]=00007ff1
> [ 1.674108] spi_nor_get_map_in_use:2880 smpt[8]=00037ff4
> [ 1.679412] spi_nor_get_map_in_use:2880 smpt[9]=03fbfff4
> [ 1.684715] spi_nor_get_map_in_use:2880 smpt[10]=ff0203fe
> [ 1.690105] spi_nor_get_map_in_use:2880 smpt[11]=03fbfff4
> [ 1.695495] spi_nor_get_map_in_use:2880 smpt[12]=00037ff4
> [ 1.700886] spi_nor_get_map_in_use:2880 smpt[13]=00007ff1
> [ 1.706275] spi_nor_get_map_in_use:2880 smpt[14]=ff0005ff
> [ 1.711664] spi_nor_get_map_in_use:2880 smpt[15]=03fffff4
> [ 1.717053] spi_nor_get_map_in_use:2910 NO map_id
> [ 1.721777] m25p80 spi0.0: s25fl512s (65536 Kbytes)
>
> Didn't throw any return error.
>
> --
> Regards,
> Yogesh Gaur
>
>
> > > @@ -2894,18 +2900,35 @@ static const u32 *spi_nor_get_map_in_use(struct
> > spi_nor *nor, const u32 *smpt)
> > > * Configuration that is currently in use.
> > > */
> > > map_id = map_id << 1 | !!(data_byte & read_data_mask);
> > > + map_id_is_valid = true;
> > > i = i + 2;
> > > }
>


2018-10-22 09:15:19

by Tudor Ambarus

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

Please amend this as well. Thanks!

---
drivers/mtd/spi-nor/spi-nor.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 3a9b69e9ba6d..3019708696cd 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2887,10 +2887,15 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
{
const u32 *ret = NULL;
+ u8 *dma_safe;
u32 i, addr;
int err;
u8 addr_width, read_opcode, read_dummy;
- u8 read_data_mask, data_byte, map_id;
+ u8 read_data_mask, map_id;
+
+ dma_safe = kmalloc(sizeof(*dma_safe), GFP_KERNEL | GFP_DMA);
+ if (!dma_safe)
+ return ERR_PTR(-ENOMEM);

addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
@@ -2906,7 +2911,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
addr = smpt[i + 1];

- err = spi_nor_read_raw(nor, addr, 1, &data_byte);
+ err = spi_nor_read_raw(nor, addr, 1, dma_safe);
if (err)
goto out;

@@ -2914,7 +2919,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
* Build an index value that is used to select the Sector Map
* Configuration that is currently in use.
*/
- map_id = map_id << 1 | !!(data_byte & read_data_mask);
+ map_id = map_id << 1 | !!(*dma_safe & read_data_mask);
i = i + 2;
}

@@ -2929,6 +2934,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
ret = smpt + i;
/* fall through */
out:
+ kfree(dma_safe);
nor->addr_width = addr_width;
nor->read_dummy = read_dummy;
nor->read_opcode = read_opcode;
--
2.9.4


2018-10-22 09:31:56

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 06:04:13 +0000
Yogesh Narayan Gaur <[email protected]> wrote:


> - /* Find the matching configuration map */
> - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> - if (smpt[i] & SMPT_DESC_END)
> - goto out;
> + if (map_id_is_valid)
> + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> + else
> + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> +
> + for (nmaps = 0; nmaps< smpt_len; nmaps++) {

Why did you change this for loop?

> + if(!(smpt[nmaps] & SMPT_DESC_TYPE_MAP))
> + continue;
> +
> + if(!map_id_is_valid) {
> + if (nmaps) {

With your change in the for () block, this test is no longer valid...

Please keep the original patch and patch the if () condition as
suggested.

> + ret = NULL;
> + break;
> + }
> +
> + ret = smpt+nmaps;
> + } else if (map_id == SMPT_MAP_ID(smpt[nmaps])) {
> + ret = smpt+nmaps;
> + break;
> + }
> +
> /* increment the table index to the next map */
> - i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> + nmaps += SMPT_MAP_REGION_COUNT(smpt[nmaps]) + 1;
> }
>
> - ret = smpt + i;
> /* fall through */
> out:
> nor->addr_width = addr_width;

2018-10-22 10:21:38

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 3:41 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> Brown <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Cyrille Pitchen <[email protected]>;
> [email protected]; [email protected]; linux-arm-
> [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 10:03:55 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi,
> >
> >
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:[email protected]]
> > > Sent: Monday, October 22, 2018 2:46 PM
> > > To: Yogesh Narayan Gaur <[email protected]>
> > > Cc: Tudor Ambarus <[email protected]>; [email protected];
> > > Mark Brown <[email protected]>; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; Cyrille Pitchen
> > > <[email protected]>; [email protected];
> > > [email protected]; linux-arm- [email protected]
> > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > SFDP SPI NOR flash memories
> > >
> > > On Mon, 22 Oct 2018 06:04:13 +0000
> > > Yogesh Narayan Gaur <[email protected]> wrote:
> > >
> > >
> > With below patch, it gets stuck in for loop of "+ for (nmaps = 0; i< smpt_len;
> nmaps++) {".
> >
> > [ 1.624684] m25p80 spi0.0: found s25fl512s, expected m25p80
> > [ 1.630377] Start [addr_width:00000000, read_dumy:08,
> read_opcode:00000000]
> > [ 1.637335] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> > [ 1.642641] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> > [ 1.647945] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> > [ 1.653248] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> > [ 1.658551] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> > [ 1.663855] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> > [ 1.669158] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> > [ 1.674461] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> > [ 1.679766] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> > [ 1.685070] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> > [ 1.690375] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> > [ 1.695768] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> > [ 1.701158] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> > [ 1.706550] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> > [ 1.711940] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> > [ 1.717330] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> > [ 1.722720] smpt[0]=[addr_width:00000000, read_dumy:08,
> read_opcode:00000065]
> > [ 1.729861] spi_nor_get_map_in_use:2912 map_id=1
> >
> >
> > --- a/drivers/mtd/spi-nor/spi-nor.c
> > +++ b/drivers/mtd/spi-nor/spi-nor.c
> > @@ -2863,26 +2863,36 @@ static u8 spi_nor_smpt_read_dummy(const struct
> spi_nor *nor, const u32 settings)
> > * @nor: pointer to a 'struct spi_nor'
> > * @smpt: pointer to the sector map parameter table
> > */
> > -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > u32 *smpt)
> > +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > +u32 *smpt, u32 smpt_len)
> > {
> > const u32 *ret = NULL;
> > - u32 i, addr;
> > + u32 i, addr, nmaps;
> > int err;
> > u8 addr_width, read_opcode, read_dummy;
> > u8 read_data_mask, data_byte, map_id;
> > + bool map_id_is_valid = false;
> >
> > addr_width = nor->addr_width;
> > read_dummy = nor->read_dummy;
> > read_opcode = nor->read_opcode;
> >
> > + pr_info("Start [addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > +
> > + for (i = 0; i<smpt_len; i++)
> > + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__,
> > + i, smpt[i]);
> > +
> > map_id = 0;
> > - i = 0;
> > /* Determine if there are any optional Detection Command Descriptors */
> > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > + for (i = 0; i< smpt_len; i++) {
> > + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> > + break;
> > +
> > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> > nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> > + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > +
> > addr = smpt[i + 1];
> >
> > err = spi_nor_read_raw(nor, addr, 1, &data_byte); @@
> > -2894,18 +2904,36 @@ static const u32 *spi_nor_get_map_in_use(struct
> spi_nor *nor, const u32 *smpt)
> > * Configuration that is currently in use.
> > */
> > map_id = map_id << 1 | !!(data_byte & read_data_mask);
> > + map_id_is_valid = true;
> > i = i + 2;
> > }
> >
> > - /* Find the matching configuration map */
> > - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> > - if (smpt[i] & SMPT_DESC_END)
> > - goto out;
> > + if (map_id_is_valid)
> > + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> > + else
> > + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> > +
> > + for (nmaps = 0; i< smpt_len; nmaps++) {
> > + if((smpt[i] & SMPT_DESC_TYPE_MAP))
> > + continue;
>
> Try to Replace the above if block by:
>
> if (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> i += 2;
> continue;
> }
>

It works,
[ 1.628162] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.633854] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
[ 1.640811] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
[ 1.646117] spi_nor_get_map_in_use:2882 smpt[1]=00000004
[ 1.651421] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
[ 1.656724] spi_nor_get_map_in_use:2882 smpt[3]=00000002
[ 1.662028] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
[ 1.667331] spi_nor_get_map_in_use:2882 smpt[5]=00000004
[ 1.672635] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
[ 1.677937] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
[ 1.683240] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
[ 1.688542] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
[ 1.693845] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
[ 1.699234] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
[ 1.704625] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
[ 1.710014] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
[ 1.715403] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
[ 1.720791] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
[ 1.726180] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
[ 1.733320] spi_nor_get_map_in_use:2912 map_id=0 smpt_len:16 i=:3
[ 1.739406] End [addr_width:00000000, read_dumy:08, read_opcode:00000065]
[ 1.746204] m25p80 spi0.0: s25fl512s (65536 Kbytes)

- /* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
- if (smpt[i] & SMPT_DESC_END)
- goto out;
+ if (map_id_is_valid)
+ pr_info("%s:%i map_id=%d smpt_len:%d i=:%d\n", __func__, __LINE__, map_id, smpt_len, i);
+ else
+ pr_info("%s:%i NO map_id\n", __func__, __LINE__);
+
+ for (nmaps = 0; i< smpt_len; nmaps++) {
+ if(!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ i += 2;
+ continue;
+ }
+
+ if(!map_id_is_valid) {
+ if (nmaps) {
+ ret = NULL;
+ break;
+ }
+
+ ret = smpt+i;
+ } else if (map_id == SMPT_MAP_ID(smpt[i])) {
+ ret = smpt+i;
+ break;
+ }
+
/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}

- ret = smpt + i;
+ pr_info("End [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);

> > +
> > + if(!map_id_is_valid) {
> > + if (nmaps) {
> > + ret = NULL;
> > + break;
> > + }
> > +
> > + ret = smpt+i;
> > + } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> > + ret = smpt+i;
> > + break;
> > + }
> > +
> > /* increment the table index to the next map */
> > i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> > }
> >
> > - ret = smpt + i;
> > + pr_info("End [addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > /* fall through */
> > out:
> > nor->addr_width = addr_width;


2018-10-22 10:22:22

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 10:03:55 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> [ 1.624684] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.630377] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
> [ 1.637335] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.642641] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.647945] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.653248] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.658551] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.663855] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.669158] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.674461] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.679766] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.685070] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.690375] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.695768] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.701158] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.706550] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.711940] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.717330] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.722720] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]

Okay, so addr_width is wrong here (I guess read_dummy is wrong too).
That's probably because we fall in the SMPT_CMD_ADDRESS_LEN_USE_CURRENT
case and nor->addr_width has not yet been initialize when we reach this
function.

> [ 1.729861] spi_nor_get_map_in_use:2912 map_id=1
>
>

[...]

> + for (i = 0; i<smpt_len; i++)
> + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
> +
> map_id = 0;
> - i = 0;
> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {
> + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);

^ %08x

2018-10-22 10:26:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 10:17:58 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> It works,

Not really.

> [ 1.628162] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.633854] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
> [ 1.640811] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.646117] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.651421] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.656724] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.662028] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.667331] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.672635] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.677937] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.683240] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.688542] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.693845] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.699234] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.704625] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.710014] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.715403] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.720791] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.726180] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]

There's still a problem here: you should see this trace 3 times since
we have 3 command descriptors. And of course, the addr_width and
read_dummy are wrong.

> [ 1.733320] spi_nor_get_map_in_use:2912 map_id=0 smpt_len:16 i=:3
> [ 1.739406] End [addr_width:00000000, read_dumy:08, read_opcode:00000065]
> [ 1.746204] m25p80 spi0.0: s25fl512s (65536 Kbytes)


2018-10-22 10:29:30

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 10:03:55 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -2863,26 +2863,36 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
> * @nor: pointer to a 'struct spi_nor'
> * @smpt: pointer to the sector map parameter table
> */
> -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
> {
> const u32 *ret = NULL;
> - u32 i, addr;
> + u32 i, addr, nmaps;
> int err;
> u8 addr_width, read_opcode, read_dummy;
> u8 read_data_mask, data_byte, map_id;
> + bool map_id_is_valid = false;
>
> addr_width = nor->addr_width;
> read_dummy = nor->read_dummy;
> read_opcode = nor->read_opcode;
>
> + pr_info("Start [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
> +
> + for (i = 0; i<smpt_len; i++)
> + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
> +
> map_id = 0;
> - i = 0;
> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {

^ i += 2) {

> + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
> +
> addr = smpt[i + 1];
>
> err = spi_nor_read_raw(nor, addr, 1, &data_byte);
> @@ -2894,18 +2904,36 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> * Configuration that is currently in use.
> */
> map_id = map_id << 1 | !!(data_byte & read_data_mask);
> + map_id_is_valid = true;
> i = i + 2;

Drop the above line (i = i + 2).

> }
>
> - /* Find the matching configuration map */
> - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> - if (smpt[i] & SMPT_DESC_END)
> - goto out;
> + if (map_id_is_valid)
> + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> + else
> + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> +
> + for (nmaps = 0; i< smpt_len; nmaps++) {
> + if((smpt[i] & SMPT_DESC_TYPE_MAP))
> + continue;
> +
> + if(!map_id_is_valid) {
> + if (nmaps) {
> + ret = NULL;
> + break;
> + }
> +
> + ret = smpt+i;
> + } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> + ret = smpt+i;
> + break;
> + }
> +
> /* increment the table index to the next map */
> i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> }
>
> - ret = smpt + i;
> + pr_info("End [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
> /* fall through */
> out:
> nor->addr_width = addr_width;


2018-10-22 10:40:38

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 3:57 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> Brown <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Cyrille Pitchen <[email protected]>;
> [email protected]; [email protected]; linux-arm-
> [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 10:03:55 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > --- a/drivers/mtd/spi-nor/spi-nor.c
> > +++ b/drivers/mtd/spi-nor/spi-nor.c
> > @@ -2863,26 +2863,36 @@ static u8 spi_nor_smpt_read_dummy(const struct
> spi_nor *nor, const u32 settings)
> > * @nor: pointer to a 'struct spi_nor'
> > * @smpt: pointer to the sector map parameter table
> > */
> > -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > u32 *smpt)
> > +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const
> > +u32 *smpt, u32 smpt_len)
> > {
> > const u32 *ret = NULL;
> > - u32 i, addr;
> > + u32 i, addr, nmaps;
> > int err;
> > u8 addr_width, read_opcode, read_dummy;
> > u8 read_data_mask, data_byte, map_id;
> > + bool map_id_is_valid = false;
> >
> > addr_width = nor->addr_width;
> > read_dummy = nor->read_dummy;
> > read_opcode = nor->read_opcode;
> >
> > + pr_info("Start [addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > +
> > + for (i = 0; i<smpt_len; i++)
> > + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__,
> > + i, smpt[i]);
> > +
> > map_id = 0;
> > - i = 0;
> > /* Determine if there are any optional Detection Command Descriptors */
> > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > + for (i = 0; i< smpt_len; i++) {
>
> ^ i += 2) {
>
> > + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> > + break;
> > +
> > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> > nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> > + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > +
> > addr = smpt[i + 1];
> >
> > err = spi_nor_read_raw(nor, addr, 1, &data_byte); @@
> > -2894,18 +2904,36 @@ static const u32 *spi_nor_get_map_in_use(struct
> spi_nor *nor, const u32 *smpt)
> > * Configuration that is currently in use.
> > */
> > map_id = map_id << 1 | !!(data_byte & read_data_mask);
> > + map_id_is_valid = true;
> > i = i + 2;
>
> Drop the above line (i = i + 2).
>

[ 1.632190] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
[ 1.639148] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
[ 1.644451] spi_nor_get_map_in_use:2882 smpt[1]=00000004
[ 1.649755] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
[ 1.655057] spi_nor_get_map_in_use:2882 smpt[3]=00000002
[ 1.660360] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
[ 1.665662] spi_nor_get_map_in_use:2882 smpt[5]=00000004
[ 1.670965] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
[ 1.676267] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
[ 1.681571] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
[ 1.686874] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
[ 1.692176] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
[ 1.697566] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
[ 1.702954] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
[ 1.708343] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
[ 1.713732] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
[ 1.719120] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
[ 1.724509] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
[ 1.731650] smpt[1]=[addr_width:00000000, read_dumy:08, read_opcode:00000000]
[ 1.738791] smpt[2]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
[ 1.745932] spi_nor_get_map_in_use:2911 map_id=0 smpt_len:16 i=:3
[ 1.752018] End [addr_width:00000000, read_dumy:08, read_opcode:00000065]

Also, one more thing when we are returning from the function, we are over-writing the values of addr_widthm read_dummy and read_opcode.
Is this correct?

out:
nor->addr_width = addr_width;
nor->read_dummy = read_dummy;
nor->read_opcode = read_opcode;
return ret;
}

--
Regards
Yogesh Gaur
> > }
> >
> > - /* Find the matching configuration map */
> > - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> > - if (smpt[i] & SMPT_DESC_END)
> > - goto out;
> > + if (map_id_is_valid)
> > + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> > + else
> > + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> > +
> > + for (nmaps = 0; i< smpt_len; nmaps++) {
> > + if((smpt[i] & SMPT_DESC_TYPE_MAP))
> > + continue;
> > +
> > + if(!map_id_is_valid) {
> > + if (nmaps) {
> > + ret = NULL;
> > + break;
> > + }
> > +
> > + ret = smpt+i;
> > + } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> > + ret = smpt+i;
> > + break;
> > + }
> > +
> > /* increment the table index to the next map */
> > i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> > }
> >
> > - ret = smpt + i;
> > + pr_info("End [addr_width:%08x, read_dumy:%0x8,
> > + read_opcode:%08x]\n", nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > /* fall through */
> > out:
> > nor->addr_width = addr_width;


2018-10-22 10:48:39

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 10:39:48 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

>
> [ 1.632190] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
> [ 1.639148] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.644451] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.649755] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.655057] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.660360] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.665662] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.670965] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.676267] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.681571] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.686874] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.692176] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.697566] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.702954] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.708343] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.713732] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.719120] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.724509] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
> [ 1.731650] smpt[1]=[addr_width:00000000, read_dumy:08, read_opcode:00000000]
> [ 1.738791] smpt[2]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]

You still don't print read_dummy correctly: %0x8 -> %08x.

Can you add

if (!nor->addr_width)
nor->addr_width = 3;

After the

nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);

line.

> [ 1.745932] spi_nor_get_map_in_use:2911 map_id=0 smpt_len:16 i=:3
> [ 1.752018] End [addr_width:00000000, read_dumy:08, read_opcode:00000065]
>
> Also, one more thing when we are returning from the function, we are over-writing the values of addr_widthm read_dummy and read_opcode.
> Is this correct?

Yes, that's correct.

>
> out:
> nor->addr_width = addr_width;
> nor->read_dummy = read_dummy;
> nor->read_opcode = read_opcode;
> return ret;
> }
>



2018-10-22 10:53:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 12:46:27 +0200
Boris Brezillon <[email protected]> wrote:

> On Mon, 22 Oct 2018 10:39:48 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> >
> > [ 1.632190] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
> > [ 1.639148] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> > [ 1.644451] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> > [ 1.649755] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> > [ 1.655057] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> > [ 1.660360] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> > [ 1.665662] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> > [ 1.670965] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> > [ 1.676267] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> > [ 1.681571] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> > [ 1.686874] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> > [ 1.692176] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> > [ 1.697566] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> > [ 1.702954] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> > [ 1.708343] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> > [ 1.713732] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> > [ 1.719120] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> > [ 1.724509] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
> > [ 1.731650] smpt[1]=[addr_width:00000000, read_dumy:08, read_opcode:00000000]
> > [ 1.738791] smpt[2]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
>
> You still don't print read_dummy correctly: %0x8 -> %08x.
>
> Can you add
>
> if (!nor->addr_width)
> nor->addr_width = 3;
>
> After the
>
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
>
> line.

And you should also try to force ->read_dummy to 8, because according
to the spec, the default read_latency is 8 for this chip. With that in
place, you should get an map_id of 1, 3 or 5.

>
> > [ 1.745932] spi_nor_get_map_in_use:2911 map_id=0 smpt_len:16 i=:3
> > [ 1.752018] End [addr_width:00000000, read_dumy:08, read_opcode:00000065]
> >
> > Also, one more thing when we are returning from the function, we are over-writing the values of addr_widthm read_dummy and read_opcode.
> > Is this correct?
>
> Yes, that's correct.
>
> >
> > out:
> > nor->addr_width = addr_width;
> > nor->read_dummy = read_dummy;
> > nor->read_opcode = read_opcode;
> > return ret;
> > }
> >
>
>


2018-10-22 11:26:33

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,


> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 2:46 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> Brown <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Cyrille Pitchen <[email protected]>;
> [email protected]; [email protected]; linux-arm-
> [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 06:04:13 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
>
With below patch, it gets stuck in for loop of "+ for (nmaps = 0; i< smpt_len; nmaps++) {".

[ 1.624684] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.630377] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
[ 1.637335] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
[ 1.642641] spi_nor_get_map_in_use:2882 smpt[1]=00000004
[ 1.647945] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
[ 1.653248] spi_nor_get_map_in_use:2882 smpt[3]=00000002
[ 1.658551] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
[ 1.663855] spi_nor_get_map_in_use:2882 smpt[5]=00000004
[ 1.669158] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
[ 1.674461] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
[ 1.679766] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
[ 1.685070] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
[ 1.690375] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
[ 1.695768] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
[ 1.701158] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
[ 1.706550] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
[ 1.711940] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
[ 1.717330] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
[ 1.722720] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
[ 1.729861] spi_nor_get_map_in_use:2912 map_id=1


--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2863,26 +2863,36 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
* @nor: pointer to a 'struct spi_nor'
* @smpt: pointer to the sector map parameter table
*/
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
{
const u32 *ret = NULL;
- u32 i, addr;
+ u32 i, addr, nmaps;
int err;
u8 addr_width, read_opcode, read_dummy;
u8 read_data_mask, data_byte, map_id;
+ bool map_id_is_valid = false;

addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode;

+ pr_info("Start [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
+
+ for (i = 0; i<smpt_len; i++)
+ pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
+
map_id = 0;
- i = 0;
/* Determine if there are any optional Detection Command Descriptors */
- while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ for (i = 0; i< smpt_len; i++) {
+ if ((smpt[i] & SMPT_DESC_TYPE_MAP))
+ break;
+
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
+ pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
+
addr = smpt[i + 1];

err = spi_nor_read_raw(nor, addr, 1, &data_byte);
@@ -2894,18 +2904,36 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
* Configuration that is currently in use.
*/
map_id = map_id << 1 | !!(data_byte & read_data_mask);
+ map_id_is_valid = true;
i = i + 2;
}

- /* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
- if (smpt[i] & SMPT_DESC_END)
- goto out;
+ if (map_id_is_valid)
+ pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
+ else
+ pr_info("%s:%i NO map_id\n", __func__, __LINE__);
+
+ for (nmaps = 0; i< smpt_len; nmaps++) {
+ if((smpt[i] & SMPT_DESC_TYPE_MAP))
+ continue;
+
+ if(!map_id_is_valid) {
+ if (nmaps) {
+ ret = NULL;
+ break;
+ }
+
+ ret = smpt+i;
+ } else if (map_id == SMPT_MAP_ID(smpt[i])) {
+ ret = smpt+i;
+ break;
+ }
+
/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}

- ret = smpt + i;
+ pr_info("End [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
/* fall through */
out:
nor->addr_width = addr_width;

2018-10-22 11:34:57

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 10:03:55 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Monday, October 22, 2018 2:46 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> > Brown <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; Cyrille Pitchen <[email protected]>;
> > [email protected]; [email protected]; linux-arm-
> > [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Mon, 22 Oct 2018 06:04:13 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> >
> With below patch, it gets stuck in for loop of "+ for (nmaps = 0; i< smpt_len; nmaps++) {".
>
> [ 1.624684] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.630377] Start [addr_width:00000000, read_dumy:08, read_opcode:00000000]
> [ 1.637335] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.642641] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.647945] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.653248] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.658551] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.663855] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.669158] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.674461] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.679766] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.685070] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.690375] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.695768] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.701158] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.706550] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.711940] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.717330] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.722720] smpt[0]=[addr_width:00000000, read_dumy:08, read_opcode:00000065]
> [ 1.729861] spi_nor_get_map_in_use:2912 map_id=1
>
>
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -2863,26 +2863,36 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
> * @nor: pointer to a 'struct spi_nor'
> * @smpt: pointer to the sector map parameter table
> */
> -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
> {
> const u32 *ret = NULL;
> - u32 i, addr;
> + u32 i, addr, nmaps;
> int err;
> u8 addr_width, read_opcode, read_dummy;
> u8 read_data_mask, data_byte, map_id;
> + bool map_id_is_valid = false;
>
> addr_width = nor->addr_width;
> read_dummy = nor->read_dummy;
> read_opcode = nor->read_opcode;
>
> + pr_info("Start [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
> +
> + for (i = 0; i<smpt_len; i++)
> + pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
> +
> map_id = 0;
> - i = 0;
> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {
> + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
> +
> addr = smpt[i + 1];
>
> err = spi_nor_read_raw(nor, addr, 1, &data_byte);
> @@ -2894,18 +2904,36 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
> * Configuration that is currently in use.
> */
> map_id = map_id << 1 | !!(data_byte & read_data_mask);
> + map_id_is_valid = true;
> i = i + 2;
> }
>
> - /* Find the matching configuration map */
> - while (SMPT_MAP_ID(smpt[i]) != map_id) {
> - if (smpt[i] & SMPT_DESC_END)
> - goto out;
> + if (map_id_is_valid)
> + pr_info("%s:%i map_id=%d\n", __func__, __LINE__, map_id);
> + else
> + pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> +
> + for (nmaps = 0; i< smpt_len; nmaps++) {
> + if((smpt[i] & SMPT_DESC_TYPE_MAP))
> + continue;

Try to Replace the above if block by:

if (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
i += 2;
continue;
}

> +
> + if(!map_id_is_valid) {
> + if (nmaps) {
> + ret = NULL;
> + break;
> + }
> +
> + ret = smpt+i;
> + } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> + ret = smpt+i;
> + break;
> + }
> +
> /* increment the table index to the next map */
> i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> }
>
> - ret = smpt + i;
> + pr_info("End [addr_width:%08x, read_dumy:%0x8, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
> /* fall through */
> out:
> nor->addr_width = addr_width;


2018-10-22 12:07:35

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 4:23 PM
> To: Yogesh Narayan Gaur <[email protected]>;
> [email protected]
> Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> Brown <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 12:46:27 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Mon, 22 Oct 2018 10:39:48 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > >
> > > [ 1.632190] Start [addr_width:00000000, read_dumy:08,
> read_opcode:00000000]
> > > [ 1.639148] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> > > [ 1.644451] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> > > [ 1.649755] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> > > [ 1.655057] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> > > [ 1.660360] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> > > [ 1.665662] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> > > [ 1.670965] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> > > [ 1.676267] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> > > [ 1.681571] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> > > [ 1.686874] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> > > [ 1.692176] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> > > [ 1.697566] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> > > [ 1.702954] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> > > [ 1.708343] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> > > [ 1.713732] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> > > [ 1.719120] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> > > [ 1.724509] smpt[0]=[addr_width:00000000, read_dumy:08,
> read_opcode:00000065]
> > > [ 1.731650] smpt[1]=[addr_width:00000000, read_dumy:08,
> read_opcode:00000000]
> > > [ 1.738791] smpt[2]=[addr_width:00000000, read_dumy:08,
> read_opcode:00000065]
> >
> > You still don't print read_dummy correctly: %0x8 -> %08x.
> >
> > Can you add
> >
> > if (!nor->addr_width)
> > nor->addr_width = 3;
> >
> > After the
> >
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> >
> > line.
>
> And you should also try to force ->read_dummy to 8, because according to the
> spec, the default read_latency is 8 for this chip. With that in place, you should
> get an map_id of 1, 3 or 5.
>

Below is the log output.
I have forced the read_dummy as 8 and addr_width is programmed as 3.

[ 1.625176] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.630875] Start [addr_width:00000000, read_dumy:00000000, read_opcode:00000000]
[ 1.638352] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
[ 1.643658] spi_nor_get_map_in_use:2882 smpt[1]=00000004
[ 1.648963] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
[ 1.654266] spi_nor_get_map_in_use:2882 smpt[3]=00000002
[ 1.659569] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
[ 1.664872] spi_nor_get_map_in_use:2882 smpt[5]=00000004
[ 1.670175] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
[ 1.675477] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
[ 1.680782] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
[ 1.686084] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
[ 1.691391] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
[ 1.696782] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
[ 1.702171] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
[ 1.707561] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
[ 1.712951] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
[ 1.718340] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
[ 1.723731] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.731393] smpt[1]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000000]
[ 1.739056] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.746721] spi_nor_get_map_in_use:2914 map_id=0 smpt_len:16 i=:3
[ 1.752807] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.760125] m25p80 spi0.0: s25fl512s (65536 Kbytes)

read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
if (!nor->addr_width)
nor->addr_width = 3;

nor->read_dummy = 8;
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);

addr = smpt[i + 1];

err = spi_nor_read_raw(nor, addr, 1, &data_byte);
if (err)
goto out;

--
Regards
Yogesh Gaur.

> >
> > > [ 1.745932] spi_nor_get_map_in_use:2911 map_id=0 smpt_len:16 i=:3
> > > [ 1.752018] End [addr_width:00000000, read_dumy:08,
> read_opcode:00000065]
> > >
> > > Also, one more thing when we are returning from the function, we are over-
> writing the values of addr_widthm read_dummy and read_opcode.
> > > Is this correct?
> >
> > Yes, that's correct.
> >
> > >
> > > out:
> > > nor->addr_width = addr_width;
> > > nor->read_dummy = read_dummy;
> > > nor->read_opcode = read_opcode;
> > > return ret;
> > > }
> > >
> >
> >


2018-10-22 12:12:30

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 11:03:09 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Monday, October 22, 2018 4:23 PM
> > To: Yogesh Narayan Gaur <[email protected]>;
> > [email protected]
> > Cc: Tudor Ambarus <[email protected]>; [email protected]; Mark
> > Brown <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Cyrille Pitchen
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Mon, 22 Oct 2018 12:46:27 +0200
> > Boris Brezillon <[email protected]> wrote:
> >
> > > On Mon, 22 Oct 2018 10:39:48 +0000
> > > Yogesh Narayan Gaur <[email protected]> wrote:
> > >
> > > >
> > > > [ 1.632190] Start [addr_width:00000000, read_dumy:08,
> > read_opcode:00000000]
> > > > [ 1.639148] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> > > > [ 1.644451] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> > > > [ 1.649755] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> > > > [ 1.655057] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> > > > [ 1.660360] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> > > > [ 1.665662] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> > > > [ 1.670965] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> > > > [ 1.676267] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> > > > [ 1.681571] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> > > > [ 1.686874] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> > > > [ 1.692176] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> > > > [ 1.697566] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> > > > [ 1.702954] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> > > > [ 1.708343] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> > > > [ 1.713732] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> > > > [ 1.719120] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> > > > [ 1.724509] smpt[0]=[addr_width:00000000, read_dumy:08,
> > read_opcode:00000065]
> > > > [ 1.731650] smpt[1]=[addr_width:00000000, read_dumy:08,
> > read_opcode:00000000]
> > > > [ 1.738791] smpt[2]=[addr_width:00000000, read_dumy:08,
> > read_opcode:00000065]
> > >
> > > You still don't print read_dummy correctly: %0x8 -> %08x.
> > >
> > > Can you add
> > >
> > > if (!nor->addr_width)
> > > nor->addr_width = 3;
> > >
> > > After the
> > >
> > > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > >
> > > line.
> >
> > And you should also try to force ->read_dummy to 8, because according to the
> > spec, the default read_latency is 8 for this chip. With that in place, you should
> > get an map_id of 1, 3 or 5.
> >
>
> Below is the log output.
> I have forced the read_dummy as 8 and addr_width is programmed as 3.
>
> [ 1.625176] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.630875] Start [addr_width:00000000, read_dumy:00000000, read_opcode:00000000]
> [ 1.638352] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.643658] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.648963] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.654266] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.659569] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.664872] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.670175] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.675477] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.680782] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.686084] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.691391] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.696782] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.702171] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.707561] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.712951] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.718340] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.723731] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.731393] smpt[1]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000000]

You still have a problem with the for() loop increment. i should be 2
here, not 1. Did you change the i++ by i += 2?

> [ 1.739056] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.746721] spi_nor_get_map_in_use:2914 map_id=0 smpt_len:16 i=:3
> [ 1.752807] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.760125] m25p80 spi0.0: s25fl512s (65536 Kbytes)
>
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> if (!nor->addr_width)
> nor->addr_width = 3;
>
> nor->read_dummy = 8;
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
>
> addr = smpt[i + 1];
>
> err = spi_nor_read_raw(nor, addr, 1, &data_byte);

Can you print addr and data_byte here?

2018-10-22 12:18:47

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Mon, 22 Oct 2018 11:46:55 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Monday, October 22, 2018 5:13 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: [email protected]; Tudor Ambarus
> > <[email protected]>; [email protected]; Mark Brown
> > <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Cyrille Pitchen
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Mon, 22 Oct 2018 11:03:09 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > Hi,
> > >
> > > > -----Original Message-----
> > > > From: Boris Brezillon [mailto:[email protected]]
> > > > Sent: Monday, October 22, 2018 4:23 PM
> > > > To: Yogesh Narayan Gaur <[email protected]>;
> > > > [email protected]
> > > > Cc: Tudor Ambarus <[email protected]>; [email protected];
> > > > Mark Brown <[email protected]>; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > Cyrille Pitchen <[email protected]>;
> > > > [email protected]; [email protected];
> > > > [email protected]
> > > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > > SFDP SPI NOR flash memories
> > > >
> > > > On Mon, 22 Oct 2018 12:46:27 +0200
> > > > Boris Brezillon <[email protected]> wrote:
> > > >
> > > > > On Mon, 22 Oct 2018 10:39:48 +0000 Yogesh Narayan Gaur
> > > > > <[email protected]> wrote:
> > > > >
> Patch used
>
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -2863,26 +2863,39 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)

> /* Determine if there are any optional Detection Command Descriptors */
> - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> + for (i = 0; i< smpt_len; i++) {

See, you should have i += 2 here, not i++.

> + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> + break;
> +
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> - nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> + if (!nor->addr_width)
> + nor->addr_width = 3;
> +
> + nor->read_dummy = 8; //spi_nor_smpt_read_dummy(nor, smpt[i]);
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
> +
> addr = smpt[i + 1];
>
> err = spi_nor_read_raw(nor, addr, 1, &data_byte);

And add a trace here to print data_byte and addr.

Thanks,

Boris

2018-10-22 13:11:27

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 5:13 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 11:03:09 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi,
> >
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:[email protected]]
> > > Sent: Monday, October 22, 2018 4:23 PM
> > > To: Yogesh Narayan Gaur <[email protected]>;
> > > [email protected]
> > > Cc: Tudor Ambarus <[email protected]>; [email protected];
> > > Mark Brown <[email protected]>; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > Cyrille Pitchen <[email protected]>;
> > > [email protected]; [email protected];
> > > [email protected]
> > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > SFDP SPI NOR flash memories
> > >
> > > On Mon, 22 Oct 2018 12:46:27 +0200
> > > Boris Brezillon <[email protected]> wrote:
> > >
> > > > On Mon, 22 Oct 2018 10:39:48 +0000 Yogesh Narayan Gaur
> > > > <[email protected]> wrote:
> > > >
Patch used

--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2863,26 +2863,39 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
* @nor: pointer to a 'struct spi_nor'
* @smpt: pointer to the sector map parameter table
*/
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 smpt_len)
{
const u32 *ret = NULL;
- u32 i, addr;
+ u32 i, addr, nmaps;
int err;
u8 addr_width, read_opcode, read_dummy;
u8 read_data_mask, data_byte, map_id;
+ bool map_id_is_valid = false;

addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode;

+ pr_info("Start [addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
+
+ for (i = 0; i<smpt_len; i++)
+ pr_info("%s:%i smpt[%d]=%08x\n", __func__, __LINE__, i, smpt[i]);
+
map_id = 0;
- i = 0;
/* Determine if there are any optional Detection Command Descriptors */
- while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ for (i = 0; i< smpt_len; i++) {
+ if ((smpt[i] & SMPT_DESC_TYPE_MAP))
+ break;
+
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
- nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
+ if (!nor->addr_width)
+ nor->addr_width = 3;
+
+ nor->read_dummy = 8; //spi_nor_smpt_read_dummy(nor, smpt[i]);
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
+ pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
+
addr = smpt[i + 1];

err = spi_nor_read_raw(nor, addr, 1, &data_byte);
@@ -2894,18 +2907,37 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
* Configuration that is currently in use.
*/
map_id = map_id << 1 | !!(data_byte & read_data_mask);
- i = i + 2;
+ map_id_is_valid = true;
}

- /* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
- if (smpt[i] & SMPT_DESC_END)
- goto out;
+ if (map_id_is_valid)
+ pr_info("%s:%i map_id=%d smpt_len:%d i=:%d\n", __func__, __LINE__, map_id, smpt_len, i);
+ else
+ pr_info("%s:%i NO map_id\n", __func__, __LINE__);
+
+ for (nmaps = 0; i< smpt_len; nmaps++) {
+ if(!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ i += 2;
+ continue;
+ }
+
+ if(!map_id_is_valid) {
+ if (nmaps) {
+ ret = NULL;
+ break;
+ }
+
+ ret = smpt+i;
+ } else if (map_id == SMPT_MAP_ID(smpt[i])) {
+ ret = smpt+i;
+ break;
+ }
+
/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}

- ret = smpt + i;
+ pr_info("End [addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", nor->addr_width, nor->read_dummy, nor->read_opcode);
/* fall through */
out:
nor->addr_width = addr_width;
@@ -3027,8 +3059,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
for (i = 0; i < smpt_header->length; i++)
smpt[i] = le32_to_cpu(smpt[i]);

- sector_map = spi_nor_get_map_in_use(nor, smpt);
+ sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
if (!sector_map) {
+ pr_info("%s:%i\n", __func__, __LINE__);
ret = -EINVAL;
goto out;
}

2018-10-23 04:48:21

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Monday, October 22, 2018 5:22 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Mon, 22 Oct 2018 11:46:55 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi,
> >
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:[email protected]]
> > > Sent: Monday, October 22, 2018 5:13 PM
> > > To: Yogesh Narayan Gaur <[email protected]>
> > > Cc: [email protected]; Tudor Ambarus
> > > <[email protected]>; [email protected]; Mark Brown
> > > <[email protected]>; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > Cyrille Pitchen <[email protected]>;
> > > [email protected]; [email protected];
> > > [email protected]
> > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > SFDP SPI NOR flash memories
> > >
> > > On Mon, 22 Oct 2018 11:03:09 +0000
> > > Yogesh Narayan Gaur <[email protected]> wrote:
> > >
> > > > Hi,
> > > >
> > > > > -----Original Message-----
> > > > > From: Boris Brezillon [mailto:[email protected]]
> > > > > Sent: Monday, October 22, 2018 4:23 PM
> > > > > To: Yogesh Narayan Gaur <[email protected]>;
> > > > > [email protected]
> > > > > Cc: Tudor Ambarus <[email protected]>; [email protected];
> > > > > Mark Brown <[email protected]>; [email protected];
> > > > > [email protected]; [email protected];
> > > > > [email protected]; [email protected];
> > > > > Cyrille Pitchen <[email protected]>;
> > > > > [email protected]; [email protected];
> > > > > [email protected]
> > > > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to
> > > > > non-uniform SFDP SPI NOR flash memories
> > > > >
> > > > > On Mon, 22 Oct 2018 12:46:27 +0200 Boris Brezillon
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > On Mon, 22 Oct 2018 10:39:48 +0000 Yogesh Narayan Gaur
> > > > > > <[email protected]> wrote:
> > > > > >
> > Patch used
> >
> > --- a/drivers/mtd/spi-nor/spi-nor.c
> > +++ b/drivers/mtd/spi-nor/spi-nor.c
> > @@ -2863,26 +2863,39 @@ static u8 spi_nor_smpt_read_dummy(const struct
> > spi_nor *nor, const u32 settings)
>
> > /* Determine if there are any optional Detection Command Descriptors */
> > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > + for (i = 0; i< smpt_len; i++) {
>
> See, you should have i += 2 here, not i++.

Ok
>
> > + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> > + break;
> > +
> > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > - nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> > + if (!nor->addr_width)
> > + nor->addr_width = 3;
> > +
> > + nor->read_dummy = 8; //spi_nor_smpt_read_dummy(nor,
> > + smpt[i]);
> > nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> > + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x,
> > + read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy,
> > + nor->read_opcode);
> > +
> > addr = smpt[i + 1];
> >
> > err = spi_nor_read_raw(nor, addr, 1, &data_byte);
>
> And add a trace here to print data_byte and addr.
>
Logs:
[ 1.625840] m25p80 spi0.0: found s25fl512s, expected m25p80
[ 1.631536] Start [addr_width:00000000, read_dumy:00000000, read_opcode:00000000]
[ 1.639013] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
[ 1.644317] spi_nor_get_map_in_use:2882 smpt[1]=00000004
[ 1.649621] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
[ 1.654924] spi_nor_get_map_in_use:2882 smpt[3]=00000002
[ 1.660229] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
[ 1.665532] spi_nor_get_map_in_use:2882 smpt[5]=00000004
[ 1.670836] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
[ 1.676140] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
[ 1.681444] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
[ 1.686748] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
[ 1.692052] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
[ 1.697441] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
[ 1.702830] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
[ 1.708220] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
[ 1.713608] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
[ 1.718999] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
[ 1.724388] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.732050] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000
[ 1.738830] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.746491] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000
[ 1.753270] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
[ 1.760930] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000
[ 1.767710] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
[ 1.773794] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
[ 1.781092] spi_nor_parse_smpt:3065
[ 1.784573] m25p80 spi0.0: failed to parse SMPT (err = -22)
[ 1.790153] m25p80 spi0.0: s25fl512s (65536 Kbytes)


Code:
for (i = 0; i< smpt_len; i+=2) {
if ((smpt[i] & SMPT_DESC_TYPE_MAP))
break;

read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
if (!nor->addr_width)
nor->addr_width = 3;

nor->read_dummy = 8;
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);

addr = smpt[i + 1];

err = spi_nor_read_raw(nor, addr, 1, &data_byte);
pr_info("smpt_read[%d] addr[%08x], data_byte[%08x] err:%08x\n", (i+1), addr, data_byte, err);
if (err)
goto out;

/*
* Build an index value that is used to select the Sector Map
* Configuration that is currently in use.
*/
map_id = map_id << 1 | !!(data_byte & read_data_mask);
map_id_is_valid = true;
}

if (map_id_is_valid)
pr_info("%s:%i map_id=%d smpt_len:%d i=:%d\n", __func__, __LINE__, map_id, smpt_len, i);
else
pr_info("%s:%i NO map_id\n", __func__, __LINE__);

for (nmaps = 0; i< smpt_len; nmaps++) {
if(!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
i += 2;
continue;
}

if(!map_id_is_valid) {
if (nmaps) {
ret = NULL;
break;
}

ret = smpt+i;
} else if (map_id == SMPT_MAP_ID(smpt[i])) {
ret = smpt+i;
break;
}

/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}

pr_info("End [addr_width:%08x, read_dumy:%08x, read_opcode:%08x] ReturnVal:%08x\n", nor->addr_width, nor->read_dummy, nor->read_opcode, ret);

--
Thanks
Yogesh Gaur.

> Thanks,
>
> Boris

2018-10-23 05:42:27

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 23 Oct 2018 04:47:33 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Monday, October 22, 2018 5:22 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: [email protected]; Tudor Ambarus
> > <[email protected]>; [email protected]; Mark Brown
> > <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Cyrille Pitchen
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Mon, 22 Oct 2018 11:46:55 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > > Hi,
> > >
> > > > -----Original Message-----
> > > > From: Boris Brezillon [mailto:[email protected]]
> > > > Sent: Monday, October 22, 2018 5:13 PM
> > > > To: Yogesh Narayan Gaur <[email protected]>
> > > > Cc: [email protected]; Tudor Ambarus
> > > > <[email protected]>; [email protected]; Mark Brown
> > > > <[email protected]>; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > Cyrille Pitchen <[email protected]>;
> > > > [email protected]; [email protected];
> > > > [email protected]
> > > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > > SFDP SPI NOR flash memories
> > > >
> > > > On Mon, 22 Oct 2018 11:03:09 +0000
> > > > Yogesh Narayan Gaur <[email protected]> wrote:
> > > >
> > > > > Hi,
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Boris Brezillon [mailto:[email protected]]
> > > > > > Sent: Monday, October 22, 2018 4:23 PM
> > > > > > To: Yogesh Narayan Gaur <[email protected]>;
> > > > > > [email protected]
> > > > > > Cc: Tudor Ambarus <[email protected]>; [email protected];
> > > > > > Mark Brown <[email protected]>; [email protected];
> > > > > > [email protected]; [email protected];
> > > > > > [email protected]; [email protected];
> > > > > > Cyrille Pitchen <[email protected]>;
> > > > > > [email protected]; [email protected];
> > > > > > [email protected]
> > > > > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to
> > > > > > non-uniform SFDP SPI NOR flash memories
> > > > > >
> > > > > > On Mon, 22 Oct 2018 12:46:27 +0200 Boris Brezillon
> > > > > > <[email protected]> wrote:
> > > > > >
> > > > > > > On Mon, 22 Oct 2018 10:39:48 +0000 Yogesh Narayan Gaur
> > > > > > > <[email protected]> wrote:
> > > > > > >
> > > Patch used
> > >
> > > --- a/drivers/mtd/spi-nor/spi-nor.c
> > > +++ b/drivers/mtd/spi-nor/spi-nor.c
> > > @@ -2863,26 +2863,39 @@ static u8 spi_nor_smpt_read_dummy(const struct
> > > spi_nor *nor, const u32 settings)
> >
> > > /* Determine if there are any optional Detection Command Descriptors */
> > > - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > > + for (i = 0; i< smpt_len; i++) {
> >
> > See, you should have i += 2 here, not i++.
>
> Ok
> >
> > > + if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> > > + break;
> > > +
> > > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > > - nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
> > > + if (!nor->addr_width)
> > > + nor->addr_width = 3;
> > > +
> > > + nor->read_dummy = 8; //spi_nor_smpt_read_dummy(nor,
> > > + smpt[i]);
> > > nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> > > + pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x,
> > > + read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy,
> > > + nor->read_opcode);
> > > +
> > > addr = smpt[i + 1];
> > >
> > > err = spi_nor_read_raw(nor, addr, 1, &data_byte);
> >
> > And add a trace here to print data_byte and addr.
> >
> Logs:
> [ 1.625840] m25p80 spi0.0: found s25fl512s, expected m25p80
> [ 1.631536] Start [addr_width:00000000, read_dumy:00000000, read_opcode:00000000]
> [ 1.639013] spi_nor_get_map_in_use:2882 smpt[0]=08ff65fc
> [ 1.644317] spi_nor_get_map_in_use:2882 smpt[1]=00000004
> [ 1.649621] spi_nor_get_map_in_use:2882 smpt[2]=04ff65fc
> [ 1.654924] spi_nor_get_map_in_use:2882 smpt[3]=00000002
> [ 1.660229] spi_nor_get_map_in_use:2882 smpt[4]=02ff65fd
> [ 1.665532] spi_nor_get_map_in_use:2882 smpt[5]=00000004
> [ 1.670836] spi_nor_get_map_in_use:2882 smpt[6]=ff0201fe
> [ 1.676140] spi_nor_get_map_in_use:2882 smpt[7]=00007ff1
> [ 1.681444] spi_nor_get_map_in_use:2882 smpt[8]=00037ff4
> [ 1.686748] spi_nor_get_map_in_use:2882 smpt[9]=03fbfff4
> [ 1.692052] spi_nor_get_map_in_use:2882 smpt[10]=ff0203fe
> [ 1.697441] spi_nor_get_map_in_use:2882 smpt[11]=03fbfff4
> [ 1.702830] spi_nor_get_map_in_use:2882 smpt[12]=00037ff4
> [ 1.708220] spi_nor_get_map_in_use:2882 smpt[13]=00007ff1
> [ 1.713608] spi_nor_get_map_in_use:2882 smpt[14]=ff0005ff
> [ 1.718999] spi_nor_get_map_in_use:2882 smpt[15]=03fffff4
> [ 1.724388] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.732050] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000
> [ 1.738830] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.746491] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000
> [ 1.753270] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065]
> [ 1.760930] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000

Ok, so now the opcode and address are matching the values in the spec.
Can you check what's sent to the SPI controller side (in your
->exec_op() implementation), just to make sure the m25p80 propagates
the information correctly? When you do that, make sure you also print
the buswidth of each element (op->cmd.buswidth, op->addr.buswidth,
op->dummy.buswidth and op->data.buswidth).

Can you also print the read_data_mask value here.

Next thing you can do is read the CR2NV reg (using the RDAR command) and
check the RL (Read Latency) and AL (Address Length) values.

> [ 1.767710] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> [ 1.773794] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
> [ 1.781092] spi_nor_parse_smpt:3065
> [ 1.784573] m25p80 spi0.0: failed to parse SMPT (err = -22)
> [ 1.790153] m25p80 spi0.0: s25fl512s (65536 Kbytes)
>
>
> Code:
> for (i = 0; i< smpt_len; i+=2) {
> if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> break;
>
> read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> if (!nor->addr_width)
> nor->addr_width = 3;
>
> nor->read_dummy = 8;
> nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x, read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy, nor->read_opcode);
>
> addr = smpt[i + 1];
>
> err = spi_nor_read_raw(nor, addr, 1, &data_byte);
> pr_info("smpt_read[%d] addr[%08x], data_byte[%08x] err:%08x\n", (i+1), addr, data_byte, err);
> if (err)
> goto out;
>
> /*
> * Build an index value that is used to select the Sector Map
> * Configuration that is currently in use.
> */
> map_id = map_id << 1 | !!(data_byte & read_data_mask);
> map_id_is_valid = true;
> }
>
> if (map_id_is_valid)
> pr_info("%s:%i map_id=%d smpt_len:%d i=:%d\n", __func__, __LINE__, map_id, smpt_len, i);
> else
> pr_info("%s:%i NO map_id\n", __func__, __LINE__);
>
> for (nmaps = 0; i< smpt_len; nmaps++) {
> if(!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> i += 2;
> continue;
> }
>
> if(!map_id_is_valid) {
> if (nmaps) {
> ret = NULL;
> break;
> }
>
> ret = smpt+i;
> } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> ret = smpt+i;
> break;
> }
>
> /* increment the table index to the next map */
> i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> }
>
> pr_info("End [addr_width:%08x, read_dumy:%08x, read_opcode:%08x] ReturnVal:%08x\n", nor->addr_width, nor->read_dummy, nor->read_opcode, ret);
>
> --
> Thanks
> Yogesh Gaur.
>
> > Thanks,
> >
> > Boris


2018-10-23 08:19:24

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

HI,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Tuesday, October 23, 2018 11:10 AM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Tue, 23 Oct 2018 04:47:33 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi,
> >
> > > -----Original Message-----
[....]
>
> Ok, so now the opcode and address are matching the values in the spec.
> Can you check what's sent to the SPI controller side (in your
> ->exec_op() implementation), just to make sure the m25p80 propagates
> the information correctly? When you do that, make sure you also print the
> buswidth of each element (op->cmd.buswidth, op->addr.buswidth,
> op->dummy.buswidth and op->data.buswidth).
>
> Can you also print the read_data_mask value here.

I have added the prints in m25p80_read() and in flexspi controller prepare_lut and read_rxfifo() func.
In these have added prints for data variable of struct op and data which being read by the controller from the flash.

[ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000008]
[ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
[ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000


[ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000004]
[ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
[ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
[ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
[ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000


[ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000002]
[ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
[ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000


[ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
[ 2.296496] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
[ 2.305444] spi_nor_parse_smpt:3065
[ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)


>
> Next thing you can do is read the CR2NV reg (using the RDAR command) and
> check the RL (Read Latency) and AL (Address Length) values.

Please let me know how to read CR2NV register.

--
Regards,
Yogesh Gaur

>
> > [ 1.767710] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > [ 1.773794] End [addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065] ReturnVal:00000000
> > [ 1.781092] spi_nor_parse_smpt:3065
> > [ 1.784573] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > [ 1.790153] m25p80 spi0.0: s25fl512s (65536 Kbytes)
> >
> >
> > Code:
> > for (i = 0; i< smpt_len; i+=2) {
> > if ((smpt[i] & SMPT_DESC_TYPE_MAP))
> > break;
> >
> > read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
> > nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
> > if (!nor->addr_width)
> > nor->addr_width = 3;
> >
> > nor->read_dummy = 8;
> > nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
> > pr_info("smpt[%d]=[addr_width:%08x, read_dumy:%08x,
> > read_opcode:%08x]\n", i, nor->addr_width, nor->read_dummy,
> > nor->read_opcode);
> >
> > addr = smpt[i + 1];
> >
> > err = spi_nor_read_raw(nor, addr, 1, &data_byte);
> > pr_info("smpt_read[%d] addr[%08x], data_byte[%08x] err:%08x\n",
> (i+1), addr, data_byte, err);
> > if (err)
> > goto out;
> >
> > /*
> > * Build an index value that is used to select the Sector Map
> > * Configuration that is currently in use.
> > */
> > map_id = map_id << 1 | !!(data_byte & read_data_mask);
> > map_id_is_valid = true;
> > }
> >
> > if (map_id_is_valid)
> > pr_info("%s:%i map_id=%d smpt_len:%d i=:%d\n", __func__,
> __LINE__, map_id, smpt_len, i);
> > else
> > pr_info("%s:%i NO map_id\n", __func__, __LINE__);
> >
> > for (nmaps = 0; i< smpt_len; nmaps++) {
> > if(!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
> > i += 2;
> > continue;
> > }
> >
> > if(!map_id_is_valid) {
> > if (nmaps) {
> > ret = NULL;
> > break;
> > }
> >
> > ret = smpt+i;
> > } else if (map_id == SMPT_MAP_ID(smpt[i])) {
> > ret = smpt+i;
> > break;
> > }
> >
> > /* increment the table index to the next map */
> > i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
> > }
> >
> > pr_info("End [addr_width:%08x, read_dumy:%08x,
> > read_opcode:%08x] ReturnVal:%08x\n", nor->addr_width, nor->read_dummy,
> > nor->read_opcode, ret);
> >
> > --
> > Thanks
> > Yogesh Gaur.
> >
> > > Thanks,
> > >
> > > Boris


2018-10-23 08:49:19

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 23 Oct 2018 08:18:35 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

>
> I have added the prints in m25p80_read() and in flexspi controller prepare_lut and read_rxfifo() func.
> In these have added prints for data variable of struct op and data which being read by the controller from the flash.
>
> [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000008]
> [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000
>
>
> [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000004]
> [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
> [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
> [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000
>
>
> [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000002]
> [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000
>
>
> [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> [ 2.296496] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
> [ 2.305444] spi_nor_parse_smpt:3065
> [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
>
>
> >
> > Next thing you can do is read the CR2NV reg (using the RDAR command) and
> > check the RL (Read Latency) and AL (Address Length) values.
>
> Please let me know how to read CR2NV register.

Actually, RDAR is already what you use to read the map_id, and we need
to use it to read the register that contains the number of dummy cycles
and the number of address bytes to use for RDAR operations. Looks like
we have a chicken and egg situation here :-).

Let's try something else:

1/ create an u8 array of 16 entries named data_bytes

for each loop iteration (the first for loop):
2/ set ->addr_width to 3 and ->read_dummy to 0
3/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes)
4/ dump the data_bytes buf
5/ set ->addr_width to 4
6/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes)
7/ dump the data_bytes buf

If the SPI driver is working correctly, we should be able to figure out
the right value for ->addr_width and ->read_dummy.

Thanks,

Boris

2018-10-23 09:01:40

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Tuesday, October 23, 2018 2:18 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Tue, 23 Oct 2018 08:18:35 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> >
> > I have added the prints in m25p80_read() and in flexspi controller prepare_lut
> and read_rxfifo() func.
> > In these have added prints for data variable of struct op and data which being
> read by the controller from the flash.
> >
> > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000008]
> > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> bwidth:1]
> > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3, bwidth:1]
> > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000]
> err:00000000
> >
> >
> > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000004]
> > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3,
> bwidth:1]
> > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2,
> nbytes:3, bwidth:1]
> > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002]
> err:00000000
> >
> >
> > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000002]
> > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> bwidth:1]
> > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3, bwidth:1]
> > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000]
> err:00000000
> >
> >
> > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > [ 2.296496] End [addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065] ReturnVal:00000000
> > [ 2.305444] spi_nor_parse_smpt:3065
> > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> >
> >
> > >
> > > Next thing you can do is read the CR2NV reg (using the RDAR command)
> > > and check the RL (Read Latency) and AL (Address Length) values.
> >
> > Please let me know how to read CR2NV register.
>
> Actually, RDAR is already what you use to read the map_id, and we need to use
> it to read the register that contains the number of dummy cycles and the number
> of address bytes to use for RDAR operations. Looks like we have a chicken and
> egg situation here :-).
>
> Let's try something else:
>
> 1/ create an u8 array of 16 entries named data_bytes
>
> for each loop iteration (the first for loop):
> 2/ set ->addr_width to 3 and ->read_dummy to 0 3/ call spi_nor_read_raw(nor,
> addr, ARRAY_SIZE(data_bytes), data_bytes) 4/ dump the data_bytes buf 5/ set -
> >addr_width to 4 6/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes),
> data_bytes) 7/ dump the data_bytes buf
>
> If the SPI driver is working correctly, we should be able to figure out the right
> value for ->addr_width and ->read_dummy.
>

Ok, I would try with the suggested change.
But till the point we not able to figure out actual failure reason can we have below changes pushed-in

@@ -3139,6 +3177,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
switch (SFDP_PARAM_HEADER_ID(param_header)) {
case SFDP_SECTOR_MAP_ID:
err = spi_nor_parse_smpt(nor, param_header);
+ if (err) {
+ dev_warn(dev,
+ "failed to parse SMPT (err = %d)\n",
+ err);
+ /*
+ * SMPT parsing is optional, let's not drop
+ * all information we extracted so far just
+ * because it failed.
+ */
+ err = 0;
+ }

--
Regards
Yogesh Gaur.

> Thanks,
>
> Boris

2018-10-23 09:02:48

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 23 Oct 2018 10:48:27 +0200
Boris Brezillon <[email protected]> wrote:

> On Tue, 23 Oct 2018 08:18:35 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> >
> > I have added the prints in m25p80_read() and in flexspi controller prepare_lut and read_rxfifo() func.
> > In these have added prints for data variable of struct op and data which being read by the controller from the flash.
> >
> > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000008]
> > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000
> >
> >
> > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000004]
> > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
> > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
> > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000
> >
> >
> > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065, data_mask:00000002]
> > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
> > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
> > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
> > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
> > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000
> >
> >
> > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > [ 2.296496] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
> > [ 2.305444] spi_nor_parse_smpt:3065
> > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> >
> >
> > >
> > > Next thing you can do is read the CR2NV reg (using the RDAR command) and
> > > check the RL (Read Latency) and AL (Address Length) values.
> >
> > Please let me know how to read CR2NV register.
>
> Actually, RDAR is already what you use to read the map_id, and we need
> to use it to read the register that contains the number of dummy cycles
> and the number of address bytes to use for RDAR operations. Looks like
> we have a chicken and egg situation here :-).
>
> Let's try something else:
>
> 1/ create an u8 array of 16 entries named data_bytes
>
> for each loop iteration (the first for loop):
> 2/ set ->addr_width to 3 and ->read_dummy to 0
> 3/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes)
> 4/ dump the data_bytes buf
> 5/ set ->addr_width to 4
> 6/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes)
> 7/ dump the data_bytes buf
>
> If the SPI driver is working correctly, we should be able to figure out
> the right value for ->addr_width and ->read_dummy.

Also, can you add traces in nxp_fspi_read_rxfifo() to print the value
returned by f->read(base + FSPI_RFDR + j * 4) (there are 2 of them).

2018-10-23 10:20:49

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 23 Oct 2018 08:59:22 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Tuesday, October 23, 2018 2:18 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: [email protected]; Tudor Ambarus
> > <[email protected]>; [email protected]; Mark Brown
> > <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Cyrille Pitchen
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Tue, 23 Oct 2018 08:18:35 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > >
> > > I have added the prints in m25p80_read() and in flexspi controller prepare_lut
> > and read_rxfifo() func.
> > > In these have added prints for data variable of struct op and data which being
> > read by the controller from the flash.
> > >
> > > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000008]
> > > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> > bwidth:1]
> > > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > nbytes:3, bwidth:1]
> > > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000]
> > err:00000000
> > >
> > >
> > > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000004]
> > > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3,
> > bwidth:1]
> > > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2,
> > nbytes:3, bwidth:1]
> > > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002]
> > err:00000000
> > >
> > >
> > > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000002]
> > > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> > bwidth:1]
> > > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > nbytes:3, bwidth:1]
> > > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000]
> > err:00000000
> > >
> > >
> > > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > > [ 2.296496] End [addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065] ReturnVal:00000000
> > > [ 2.305444] spi_nor_parse_smpt:3065
> > > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > >
> > >
> > > >
> > > > Next thing you can do is read the CR2NV reg (using the RDAR command)
> > > > and check the RL (Read Latency) and AL (Address Length) values.
> > >
> > > Please let me know how to read CR2NV register.
> >
> > Actually, RDAR is already what you use to read the map_id, and we need to use
> > it to read the register that contains the number of dummy cycles and the number
> > of address bytes to use for RDAR operations. Looks like we have a chicken and
> > egg situation here :-).
> >
> > Let's try something else:
> >
> > 1/ create an u8 array of 16 entries named data_bytes
> >
> > for each loop iteration (the first for loop):
> > 2/ set ->addr_width to 3 and ->read_dummy to 0 3/ call spi_nor_read_raw(nor,
> > addr, ARRAY_SIZE(data_bytes), data_bytes) 4/ dump the data_bytes buf 5/ set -
> > >addr_width to 4 6/ call spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes),
> > data_bytes) 7/ dump the data_bytes buf
> >
> > If the SPI driver is working correctly, we should be able to figure out the right
> > value for ->addr_width and ->read_dummy.
> >
>
> Ok, I would try with the suggested change.
> But till the point we not able to figure out actual failure reason can we have below changes pushed-in

We're not there yet. The change just reached Linus tree, and we have
plenty of time to fix the issue before 4.20 is out. So no, I don't want
to rush this patch in until we figure out what's really going on.

>
> @@ -3139,6 +3177,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
> switch (SFDP_PARAM_HEADER_ID(param_header)) {
> case SFDP_SECTOR_MAP_ID:
> err = spi_nor_parse_smpt(nor, param_header);
> + if (err) {
> + dev_warn(dev,
> + "failed to parse SMPT (err = %d)\n",
> + err);
> + /*
> + * SMPT parsing is optional, let's not drop
> + * all information we extracted so far just
> + * because it failed.
> + */
> + err = 0;
> + }
>
> --
> Regards
> Yogesh Gaur.
>
> > Thanks,
> >
> > Boris


2018-10-23 10:20:59

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

On Tue, 23 Oct 2018 09:05:23 +0000
Yogesh Narayan Gaur <[email protected]> wrote:

> Hi,
>
> > -----Original Message-----
> > From: Boris Brezillon [mailto:[email protected]]
> > Sent: Tuesday, October 23, 2018 2:31 PM
> > To: Yogesh Narayan Gaur <[email protected]>
> > Cc: [email protected]; Tudor Ambarus
> > <[email protected]>; [email protected]; Mark Brown
> > <[email protected]>; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Cyrille Pitchen
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> > NOR flash memories
> >
> > On Tue, 23 Oct 2018 10:48:27 +0200
> > Boris Brezillon <[email protected]> wrote:
> >
> > > On Tue, 23 Oct 2018 08:18:35 +0000
> > > Yogesh Narayan Gaur <[email protected]> wrote:
> > >
> > > >
> > > > I have added the prints in m25p80_read() and in flexspi controller prepare_lut
> > and read_rxfifo() func.
> > > > In these have added prints for data variable of struct op and data which
> > being read by the controller from the flash.
> > > >
> > > > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000008]
> > > > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> > bwidth:1]
> > > > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > nbytes:3, bwidth:1]
> > > > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000]
> > err:00000000
> > > >
> > > >
> > > > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000004]
> > > > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3,
> > bwidth:1]
> > > > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2,
> > nbytes:3, bwidth:1]
> > > > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > > > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002]
> > err:00000000
> > > >
> > > >
> > > > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065, data_mask:00000002]
> > > > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003,
> > dummy:00000008, len:00000001
> > > > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> > bwidth:1]
> > > > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > nbytes:1]
> > > > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > nbytes:3, bwidth:1]
> > > > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > 1:24003008 2:0 3:0]
> > > > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000]
> > err:00000000
> > > >
> > > >
> > > > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > > > [ 2.296496] End [addr_width:00000003, read_dumy:00000008,
> > read_opcode:00000065] ReturnVal:00000000
> > > > [ 2.305444] spi_nor_parse_smpt:3065
> > > > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > > >
> > > >
> > > > >
> > > > > Next thing you can do is read the CR2NV reg (using the RDAR command)
> > and
> > > > > check the RL (Read Latency) and AL (Address Length) values.
> > > >
> > > > Please let me know how to read CR2NV register.
> > >
> > > Actually, RDAR is already what you use to read the map_id, and we need
> > > to use it to read the register that contains the number of dummy
> > > cycles and the number of address bytes to use for RDAR operations.
> > > Looks like we have a chicken and egg situation here :-).
> > >
> > > Let's try something else:
> > >
> > > 1/ create an u8 array of 16 entries named data_bytes
> > >
> > > for each loop iteration (the first for loop):
> > > 2/ set ->addr_width to 3 and ->read_dummy to 0 3/ call
> > > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 4/
> > > dump the data_bytes buf 5/ set ->addr_width to 4 6/ call
> > > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 7/
> > > dump the data_bytes buf
> > >
> > > If the SPI driver is working correctly, we should be able to figure
> > > out the right value for ->addr_width and ->read_dummy.
> >
> > Also, can you add traces in nxp_fspi_read_rxfifo() to print the value returned by
> > f->read(base + FSPI_RFDR + j * 4) (there are 2 of them).
>
> I have added this and printing the data content, all 4 bytes, after each f->read(base + FSPI_RFDR + j * 4);
> This I have shown in the logs, only 1 byte i.e. as we are reading only 1 byte so I have deleted content of rest 3 bytes when sending the logs in mail.

But I do care about the remaining 3 bytes, otherwise I wouldn't ask you
to print them. Please print the full reg content using %08x.

2018-10-23 10:21:45

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Tuesday, October 23, 2018 2:31 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Tue, 23 Oct 2018 10:48:27 +0200
> Boris Brezillon <[email protected]> wrote:
>
> > On Tue, 23 Oct 2018 08:18:35 +0000
> > Yogesh Narayan Gaur <[email protected]> wrote:
> >
> > >
> > > I have added the prints in m25p80_read() and in flexspi controller prepare_lut
> and read_rxfifo() func.
> > > In these have added prints for data variable of struct op and data which
> being read by the controller from the flash.
> > >
> > > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000008]
> > > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> bwidth:1]
> > > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3, bwidth:1]
> > > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000]
> err:00000000
> > >
> > >
> > > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000004]
> > > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3,
> bwidth:1]
> > > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2,
> nbytes:3, bwidth:1]
> > > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002]
> err:00000000
> > >
> > >
> > > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065, data_mask:00000002]
> > > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003,
> dummy:00000008, len:00000001
> > > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3,
> bwidth:1]
> > > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> nbytes:1]
> > > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3, bwidth:1]
> > > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> 1:24003008 2:0 3:0]
> > > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000]
> err:00000000
> > >
> > >
> > > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > > [ 2.296496] End [addr_width:00000003, read_dumy:00000008,
> read_opcode:00000065] ReturnVal:00000000
> > > [ 2.305444] spi_nor_parse_smpt:3065
> > > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > >
> > >
> > > >
> > > > Next thing you can do is read the CR2NV reg (using the RDAR command)
> and
> > > > check the RL (Read Latency) and AL (Address Length) values.
> > >
> > > Please let me know how to read CR2NV register.
> >
> > Actually, RDAR is already what you use to read the map_id, and we need
> > to use it to read the register that contains the number of dummy
> > cycles and the number of address bytes to use for RDAR operations.
> > Looks like we have a chicken and egg situation here :-).
> >
> > Let's try something else:
> >
> > 1/ create an u8 array of 16 entries named data_bytes
> >
> > for each loop iteration (the first for loop):
> > 2/ set ->addr_width to 3 and ->read_dummy to 0 3/ call
> > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 4/
> > dump the data_bytes buf 5/ set ->addr_width to 4 6/ call
> > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 7/
> > dump the data_bytes buf
> >
> > If the SPI driver is working correctly, we should be able to figure
> > out the right value for ->addr_width and ->read_dummy.
>
> Also, can you add traces in nxp_fspi_read_rxfifo() to print the value returned by
> f->read(base + FSPI_RFDR + j * 4) (there are 2 of them).

I have added this and printing the data content, all 4 bytes, after each f->read(base + FSPI_RFDR + j * 4);
This I have shown in the logs, only 1 byte i.e. as we are reading only 1 byte so I have deleted content of rest 3 bytes when sending the logs in mail.

--
Regards
Yogesh gaur

2018-10-23 10:21:55

by Yogesh Narayan Gaur

[permalink] [raw]
Subject: RE: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories

Hi,

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Tuesday, October 23, 2018 2:40 PM
> To: Yogesh Narayan Gaur <[email protected]>
> Cc: [email protected]; Tudor Ambarus
> <[email protected]>; [email protected]; Mark Brown
> <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Cyrille Pitchen
> <[email protected]>; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform SFDP SPI
> NOR flash memories
>
> On Tue, 23 Oct 2018 09:05:23 +0000
> Yogesh Narayan Gaur <[email protected]> wrote:
>
> > Hi,
> >
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:[email protected]]
> > > Sent: Tuesday, October 23, 2018 2:31 PM
> > > To: Yogesh Narayan Gaur <[email protected]>
> > > Cc: [email protected]; Tudor Ambarus
> > > <[email protected]>; [email protected]; Mark Brown
> > > <[email protected]>; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > Cyrille Pitchen <[email protected]>;
> > > [email protected]; [email protected];
> > > [email protected]
> > > Subject: Re: [PATCH v3 1/2] mtd: spi-nor: add support to non-uniform
> > > SFDP SPI NOR flash memories
> > >
> > > On Tue, 23 Oct 2018 10:48:27 +0200
> > > Boris Brezillon <[email protected]> wrote:
> > >
> > > > On Tue, 23 Oct 2018 08:18:35 +0000 Yogesh Narayan Gaur
> > > > <[email protected]> wrote:
> > > >
> > > > >
> > > > > I have added the prints in m25p80_read() and in flexspi
> > > > > controller prepare_lut
> > > and read_rxfifo() func.
> > > > > In these have added prints for data variable of struct op and
> > > > > data which
> > > being read by the controller from the flash.
> > > > >
> > > > > [ 2.091467] smpt[0]=[addr_width:00000003, read_dumy:00000008,
> > > read_opcode:00000065, data_mask:00000008]
> > > > > [ 2.099113] m25p80_read, nor[op:00000065 addr_width:00000003,
> > > dummy:00000008, len:00000001
> > > > > [ 2.107367] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3,
> > > bwidth:1]
> > > > > [ 2.114753] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > > nbytes:1]
> > > > > [ 2.121706] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > > nbytes:3, bwidth:1]
> > > > > [ 2.129786] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > > [ 2.136132] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > > 1:24003008 2:0 3:0]
> > > > > [ 2.144223] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > > > [ 2.151004] smpt_read[1] addr[00000004], data_byte[00000000]
> > > err:00000000
> > > > >
> > > > >
> > > > > [ 2.157782] smpt[2]=[addr_width:00000003, read_dumy:00000008,
> > > read_opcode:00000065, data_mask:00000004]
> > > > > [ 2.165429] m25p80_read, nor[op:00000065 addr_width:00000003,
> > > dummy:00000008, len:00000001
> > > > > [ 2.173683] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2,
> nbytes:3,
> > > bwidth:1]
> > > > > [ 2.181068] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > > nbytes:1]
> > > > > [ 2.188021] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2,
> > > nbytes:3, bwidth:1]
> > > > > [ 2.196101] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > > [ 2.202447] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > > 1:24003008 2:0 3:0]
> > > > > [ 2.210539] nxp_fspi_read_rxfifo, ReadData op.buf[0x02]
> > > > > [ 2.217319] smpt_read[3] addr[00000002], data_byte[00000002]
> > > err:00000000
> > > > >
> > > > >
> > > > > [ 2.224098] smpt[4]=[addr_width:00000003, read_dumy:00000008,
> > > read_opcode:00000065, data_mask:00000002]
> > > > > [ 2.231744] m25p80_read, nor[op:00000065 addr_width:00000003,
> > > dummy:00000008, len:00000001
> > > > > [ 2.239998] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4,
> nbytes:3,
> > > bwidth:1]
> > > > > [ 2.247383] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1,
> > > nbytes:1]
> > > > > [ 2.254336] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4,
> > > nbytes:3, bwidth:1]
> > > > > [ 2.262416] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
> > > > > [ 2.268762] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465
> > > 1:24003008 2:0 3:0]
> > > > > [ 2.276854] nxp_fspi_read_rxfifo, ReadData op.buf[0x00]
> > > > > [ 2.283634] smpt_read[5] addr[00000004], data_byte[00000000]
> > > err:00000000
> > > > >
> > > > >
> > > > > [ 2.290412] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
> > > > > [ 2.296496] End [addr_width:00000003, read_dumy:00000008,
> > > read_opcode:00000065] ReturnVal:00000000
> > > > > [ 2.305444] spi_nor_parse_smpt:3065
> > > > > [ 2.308924] m25p80 spi0.0: failed to parse SMPT (err = -22)
> > > > >
> > > > >
> > > > > >
> > > > > > Next thing you can do is read the CR2NV reg (using the RDAR
> > > > > > command)
> > > and
> > > > > > check the RL (Read Latency) and AL (Address Length) values.
> > > > >
> > > > > Please let me know how to read CR2NV register.
> > > >
> > > > Actually, RDAR is already what you use to read the map_id, and we
> > > > need to use it to read the register that contains the number of
> > > > dummy cycles and the number of address bytes to use for RDAR operations.
> > > > Looks like we have a chicken and egg situation here :-).
> > > >
> > > > Let's try something else:
> > > >
> > > > 1/ create an u8 array of 16 entries named data_bytes
> > > >
> > > > for each loop iteration (the first for loop):
> > > > 2/ set ->addr_width to 3 and ->read_dummy to 0 3/ call
> > > > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 4/
> > > > dump the data_bytes buf 5/ set ->addr_width to 4 6/ call
> > > > spi_nor_read_raw(nor, addr, ARRAY_SIZE(data_bytes), data_bytes) 7/
> > > > dump the data_bytes buf
> > > >
> > > > If the SPI driver is working correctly, we should be able to
> > > > figure out the right value for ->addr_width and ->read_dummy.
> > >
> > > Also, can you add traces in nxp_fspi_read_rxfifo() to print the
> > > value returned by
> > > f->read(base + FSPI_RFDR + j * 4) (there are 2 of them).
> >
> > I have added this and printing the data content, all 4 bytes, after
> > each f->read(base + FSPI_RFDR + j * 4); This I have shown in the logs, only 1
> byte i.e. as we are reading only 1 byte so I have deleted content of rest 3 bytes
> when sending the logs in mail.
>
> But I do care about the remaining 3 bytes, otherwise I wouldn't ask you to print
> them. Please print the full reg content using %08x.

Ok, logs with rxbuf content of 4 bytes, please check.
[ 2.091412] smpt[0]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065 data_mask:00000008]
[ 2.100708] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.108962] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.116349] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.123302] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.131382] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.137729] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.145821] nxp_fspi_read_rxfifo, ReadData op.buf[0x00, 0x53, 0x46, 0x44]
[ 2.152602] smpt_read[1] addr[00000004], data_byte[00000000] err:00000000


[ 2.159380] smpt[2]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065 data_mask:00000004]
[ 2.168677] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.176931] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
[ 2.184318] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.191271] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:2, nbytes:3, bwidth:1]
[ 2.199352] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.205698] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.213792] nxp_fspi_read_rxfifo, ReadData op.buf[0x02, 0x53, 0x46, 0x44]
[ 2.220573] smpt_read[3] addr[00000002], data_byte[00000002] err:00000000


[ 2.227352] smpt[4]=[addr_width:00000003, read_dumy:00000008, read_opcode:00000065 data_mask:00000002]
[ 2.236648] m25p80_read, nor[op:00000065 addr_width:00000003, dummy:00000008, len:00000001
[ 2.244902] m25p80_read, cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.252288] m25p80_read, dummy[nbytes:1 bwidth:1] data[bwidth:1, nbytes:1]
[ 2.259242] nxp_fspi_prepare_lut cmd[opcode:65 bwidth:1] aadr[val:4, nbytes:3, bwidth:1]
[ 2.267322] dummy[nbytes:1 bwidth:1] data[dir:0 bwidth:1, nbytes:1]
[ 2.273669] nxp-fspi 20c0000.flexspi: CMD[65] lutval[0:8180465 1:24003008 2:0 3:0]
[ 2.281761] nxp_fspi_read_rxfifo, ReadData op.buf[0x00, 0x53, 0x46, 0x44]
[ 2.288541] smpt_read[5] addr[00000004], data_byte[00000000] err:00000000


[ 2.295320] spi_nor_get_map_in_use:2915 map_id=0 smpt_len:16 i=:6
[ 2.301405] End [addr_width:00000003, read_dumy:00000008, read_opcode:00000065] ReturnVal:00000000
[ 2.310353] spi_nor_parse_smpt:3065
[ 2.313834] m25p80 spi0.0: failed to parse SMPT (err = -22)