Commit 5390a8df authored by Tudor Ambarus's avatar Tudor Ambarus Committed by Boris Brezillon

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: default avatarCristian Birsan <cristian.birsan@microchip.com>
Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
Reviewed-by: default avatarMarek Vasut <marek.vasut@gmail.com>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
parent 5b394b2d
This diff is collapsed.
...@@ -238,6 +238,94 @@ enum spi_nor_option_flags { ...@@ -238,6 +238,94 @@ enum spi_nor_option_flags {
SNOR_F_BROKEN_RESET = BIT(6), SNOR_F_BROKEN_RESET = BIT(6),
}; };
/**
* 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 * struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan() * spi_nor_scan()
...@@ -262,6 +350,7 @@ struct flash_info; ...@@ -262,6 +350,7 @@ struct flash_info;
* @write_proto: the SPI protocol for write operations * @write_proto: the SPI protocol for write operations
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations * @reg_proto the SPI protocol for read_reg/write_reg/erase operations
* @cmd_buf: used by the write_reg * @cmd_buf: used by the write_reg
* @erase_map: the erase map of the SPI NOR
* @prepare: [OPTIONAL] do some preparations for the * @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations * read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the * @unprepare: [OPTIONAL] do some post work after the
...@@ -297,6 +386,7 @@ struct spi_nor { ...@@ -297,6 +386,7 @@ struct spi_nor {
bool sst_write_second; bool sst_write_second;
u32 flags; u32 flags;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; 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); int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
...@@ -317,6 +407,23 @@ struct spi_nor { ...@@ -317,6 +407,23 @@ struct spi_nor {
void *priv; 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, static inline void spi_nor_set_flash_node(struct spi_nor *nor,
struct device_node *np) struct device_node *np)
{ {
......
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