Commit e8828ec1 authored by Tudor.Ambarus@microchip.com's avatar Tudor.Ambarus@microchip.com Committed by Boris Brezillon

mtd: spi-nor: fix selection of uniform erase type in flexible conf

There are uniform, non-uniform and flexible erase flash configurations.

The non-uniform erase types, are the erase types that can _not_ erase
the entire flash by their own.

As the code was, in case flashes had flexible erase capabilities
(support both uniform and non-uniform erase types in the same flash
configuration) and supported multiple uniform erase type sizes, the
code did not sort the uniform erase types, and could select a wrong
erase type size.

Sort the uniform erase mask in case of flexible erase flash
configurations, in order to select the best uniform erase type size.

Uniform, non-uniform, and flexible configurations with just a valid
uniform erase type, are not affected by this change.

Uniform erase tested on mx25l3273fm2i-08g and sst26vf064B-104i/sn.
Non uniform erase tested on sst26vf064B-104i/sn.

Fixes: 5390a8df ("mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories")
Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
parent a6a66f80
...@@ -2521,6 +2521,34 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r) ...@@ -2521,6 +2521,34 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
return left->size - right->size; return left->size - right->size;
} }
/**
* spi_nor_sort_erase_mask() - sort erase mask
* @map: the erase map of the SPI NOR
* @erase_mask: the erase type mask to be sorted
*
* Replicate the sort done for the map's erase types in BFPT: sort the erase
* mask in ascending order with the smallest erase type size starting from
* BIT(0) in the sorted erase mask.
*
* Return: sorted erase mask.
*/
static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask)
{
struct spi_nor_erase_type *erase_type = map->erase_type;
int i;
u8 sorted_erase_mask = 0;
if (!erase_mask)
return 0;
/* Replicate the sort done for the map's erase types. */
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx))
sorted_erase_mask |= BIT(i);
return sorted_erase_mask;
}
/** /**
* spi_nor_regions_sort_erase_types() - sort erase types in each region * spi_nor_regions_sort_erase_types() - sort erase types in each region
* @map: the erase map of the SPI NOR * @map: the erase map of the SPI NOR
...@@ -2536,19 +2564,13 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r) ...@@ -2536,19 +2564,13 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) 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_region *region = map->regions;
struct spi_nor_erase_type *erase_type = map->erase_type;
int i;
u8 region_erase_mask, sorted_erase_mask; u8 region_erase_mask, sorted_erase_mask;
while (region) { while (region) {
region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK; region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
/* Replicate the sort done for the map's erase types. */ sorted_erase_mask = spi_nor_sort_erase_mask(map,
sorted_erase_mask = 0; region_erase_mask);
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. */ /* Overwrite erase mask. */
region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) | region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
...@@ -2978,7 +3000,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, ...@@ -2978,7 +3000,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
u64 offset; u64 offset;
u32 region_count; u32 region_count;
int i, j; int i, j;
u8 erase_type; u8 erase_type, uniform_erase_type;
region_count = SMPT_MAP_REGION_COUNT(*smpt); region_count = SMPT_MAP_REGION_COUNT(*smpt);
/* /*
...@@ -2991,7 +3013,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, ...@@ -2991,7 +3013,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
return -ENOMEM; return -ENOMEM;
map->regions = region; map->regions = region;
map->uniform_erase_type = 0xff; uniform_erase_type = 0xff;
offset = 0; offset = 0;
/* Populate regions. */ /* Populate regions. */
for (i = 0; i < region_count; i++) { for (i = 0; i < region_count; i++) {
...@@ -3006,12 +3028,15 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, ...@@ -3006,12 +3028,15 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
* Save the erase types that are supported in all regions and * Save the erase types that are supported in all regions and
* can erase the entire flash memory. * can erase the entire flash memory.
*/ */
map->uniform_erase_type &= erase_type; uniform_erase_type &= erase_type;
offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
region[i].size; region[i].size;
} }
map->uniform_erase_type = spi_nor_sort_erase_mask(map,
uniform_erase_type);
spi_nor_region_mark_end(&region[i - 1]); spi_nor_region_mark_end(&region[i - 1]);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment