On 03/23/2017 12:33 AM, Cyrille Pitchen wrote: Hrmmmm, sigh, took me almost month to review this one, sorry :(
> This patch is a first step in introducing the support of SPI memories > with non-uniform erase sizes like Spansion s25fs512s. > > It introduces the memory erase map which splits the memory array into one > or many erase regions. Each erase region supports up to 4 erase commands, > as defined by the JEDEC JESD216B (SFDP) specification. > In turn, an erase command is defined by an op code and a sector size. > > 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. > > This is a transitional patch: non-uniform erase maps will be used later > when initialized based on the SFDP data. > > Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> [...] Before I dive into the code, I have two questions: 1) On ie. 128 MiB part, how many struct spi_nor_erase_region {} instances would be allocated in total (consider you support 4k, 64k and 32M erase opcodes) ? Three ? 2) Would it make sense to represent the erase regions as a tree instead? For example [ region with 32MiB die erase opcode , start=0 , count=4 ] | V [ region with 64k erase opcode ][ region with 64k erase opcode ] [ start=0, count=1 ][ start=0, count=511 ] | V [ region with 4k erase opcode ] [ start=0, count=16 ] I think it'd make the lookup for the best-fitting opcode combination faster if the user decides to erase some arbitrarily-aligned block of the flash. What do you think ? Note this tree-based approach does not handle the cases where erase regions would overlap, although I doubt that could be a problem . > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index d270788f5ab6..c12cafe99bee 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -216,6 +216,55 @@ enum spi_nor_option_flags { > }; > > /** > + * struct spi_nor_erase_command - Structure to describe a SPI NOR erase > command > + * @size: the size of the sector/block erased by the command. > + * @size_shift: the size shift: if @size is a power of 2 then > the shift > + * is stored in @size_shift, otherwise @size_shift is zero. > + * @size_mask: the size mask based on @size_shift. > + * @opcode: the SPI command op code to erase the sector/block. > + */ > +struct spi_nor_erase_command { > + u32 size; > + u32 size_shift; > + u32 size_mask; > + 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 the erase > + * commands supported inside this erase region. > + * @size: the size of the region in bytes. > + */ > +struct spi_nor_erase_region { > + u64 offset; > + u64 size; > +}; > + > +#define SNOR_CMD_ERASE_MAX 4 > +#define SNOR_CMD_ERASE_MASK GENMASK_ULL(SNOR_CMD_ERASE_MAX - 1, 0) > +#define SNOR_CMD_ERASE_OFFSET(_cmd_mask, _offset) \ > + ((((u64)(_offset)) & ~SNOR_CMD_ERASE_MASK) | \ > + (((u64)(_cmd_mask)) & SNOR_CMD_ERASE_MASK)) > + > +/** > + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map > + * @commands: an array of erase commands shared by all the > regions. > + * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform > + * sector size (legacy implementation). > + * @regions: point to an array describing the boundaries of the erase > + * regions. > + * @num_regions: the number of elements in the @regions array. > + */ > +struct spi_nor_erase_map { > + struct spi_nor_erase_command commands[SNOR_CMD_ERASE_MAX]; > + struct spi_nor_erase_region uniform_region; > + struct spi_nor_erase_region *regions; > + u32 num_regions; > +}; > + > +/** > * struct flash_info - Forward declaration of a structure used > internally by > * spi_nor_scan() and spi_nor_init(). > */ > @@ -238,6 +287,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 > @@ -273,6 +323,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); > @@ -293,6 +344,11 @@ struct spi_nor { > void *priv; > }; > > +static inline bool spi_nor_has_uniform_erase(const struct spi_nor *nor) > +{ > + return (nor->erase_map.regions == &nor->erase_map.uniform_region); > +} > + > static inline void spi_nor_set_flash_node(struct spi_nor *nor, > struct device_node *np) > { > -- Best regards, Marek Vasut