From mboxrd@z Thu Jan 1 00:00:00 1970 From: Pratyush Yadav Date: Wed, 24 Feb 2021 17:35:53 +0530 Subject: [PATCH v5 07/10] mtd: spi-nor-core: Add non-uniform erase for Spansion/Cypress In-Reply-To: <25396fac5db76216aa495bd77cbe4ec2e264880c.1613622392.git.Takahiro.Kuwano@infineon.com> References: <25396fac5db76216aa495bd77cbe4ec2e264880c.1613622392.git.Takahiro.Kuwano@infineon.com> Message-ID: <20210224120551.54spe6niivvav7v6@ti.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 19/02/21 10:56AM, tkuw584924 at gmail.com wrote: > From: Takahiro Kuwano > > Some of Spansion/Cypress chips have overlaid 4KB sectors at top and/or > bottom, depending on the device configuration, while U-Boot supports > uniform sector layout only. > > The spansion_erase_non_uniform() erases overlaid 4KB sectors, > non-overlaid portion of normal sector, and remaining normal sectors, by > selecting correct erase command and size based on the address to erase > and size of overlaid portion in parameters. > > Signed-off-by: Takahiro Kuwano > --- > Changes in v5: > - New in v5, introduce spansion_erase_non_uniform() as a replacement > for spansion_overlaid_erase() in v4 > > drivers/mtd/spi/spi-nor-core.c | 72 ++++++++++++++++++++++++++++++++++ > 1 file changed, 72 insertions(+) > > diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c > index e5fc0e7965..46948ed41b 100644 > --- a/drivers/mtd/spi/spi-nor-core.c > +++ b/drivers/mtd/spi/spi-nor-core.c > @@ -793,6 +793,78 @@ erase_err: > return ret; > } > > +#ifdef CONFIG_SPI_FLASH_SPANSION > +/** > + * spansion_erase_non_uniform() - erase non-uniform sectors for Spansion/Cypress > + * chips > + * @mtd: pointer to a 'struct mtd_info' > + * @instr: pointer to a 'struct erase_info' > + * @ovlsz_top: size of overlaid portion at the top address > + * @ovlsz_btm: size of overlaid portion at the bottom address > + * > + * Erase an address range on the nor chip that can contain 4KB sectors overlaid > + * on top and/or bottom. The appropriate erase opcode and size are chosen by > + * address to erase and size of overlaid portion. > + * > + * Return: 0 on success, -errno otherwise. > + */ > +static int spansion_erase_non_uniform(struct mtd_info *mtd, > + struct erase_info *instr, u32 ovlsz_top, > + u32 ovlsz_btm) Is there any reason why you are not using the nor->erase() hook? As far as I can see it should also be able to perform the same erase while avoiding all the boilerplate needed for keeping track of address, remaining erase, write enable, error handling, etc. One problem I can see is that you don't always increment address by nor->erasesize. That can change depending on which sector got erased. spi_nor_erase() can't currently handle that. But IMO a reasonable fix for this would be to return the size actually erased in nor->erase(), like how it is done for the unix read() and write() system calls. A negative value would still mean an error but now a positive return value will tell the caller how much data was actually erased. I think it is a relatively easy refactor and worth doing. > +{ > + struct spi_nor *nor = mtd_to_spi_nor(mtd); > + struct spi_mem_op op = > + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), > + SPI_MEM_OP_ADDR(nor->addr_width, instr->addr, 1), > + SPI_MEM_OP_NO_DUMMY, > + SPI_MEM_OP_NO_DATA); > + u32 len = instr->len; > + u32 erasesize; > + int ret; spi_nor_erase() does some sanity checking for instr->len. Even more reason to use nor->erase() so we don't have to duplicate that code. > + > + while (len) { > + /* 4KB sectors */ > + if (op.addr.val < ovlsz_btm || > + op.addr.val >= mtd->size - ovlsz_top) { > + op.cmd.opcode = SPINOR_OP_BE_4K; > + erasesize = SZ_4K; Ok. > + > + /* Non-overlaid portion in the normal sector at the bottom */ > + } else if (op.addr.val == ovlsz_btm) { > + op.cmd.opcode = nor->erase_opcode; > + erasesize = mtd->erasesize - ovlsz_btm; > + > + /* Non-overlaid portion in the normal sector at the top */ > + } else if (op.addr.val == mtd->size - mtd->erasesize) { > + op.cmd.opcode = nor->erase_opcode; > + erasesize = mtd->erasesize - ovlsz_top; Patch 9/10 does not check for uniform sector size configuration. But if the check is to be added later, passing 0 to ovlsz_top and ovlsz_btm will do the right thing because erasesize will end up equal to mtd->erasesize in both cases. Neat! > + > + /* Normal sectors */ > + } else { > + op.cmd.opcode = nor->erase_opcode; > + erasesize = mtd->erasesize; > + } Ok. > + > + write_enable(nor); > + > + ret = spi_mem_exec_op(nor->spi, &op); > + if (ret) > + break; > + > + op.addr.val += erasesize; > + len -= erasesize; I recall a patch for Linux by Tudor recently that moved some code like this to after spi_nor_wait_till_ready(). Let's do so here as well. Of course, this will not matter if you are using nor->erase() instead. The problem will still be there since spi_nor_erase() also does this but that is a separate fix. > + > + ret = spi_nor_wait_till_ready(nor); > + if (ret) > + break; > + } > + > + write_disable(nor); > + > + return ret; > +} > +#endif > + > #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) > /* Write status register and ensure bits in mask match written values */ > static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) > -- > 2.25.1 > -- Regards, Pratyush Yadav Texas Instruments Inc.