Messages in this thread Patch in this message | | | From | <> | Subject | Re: [PATCH v4 4/4] mtd: spi-nor: otp: implement erase for Winbond and similar flashes | Date | Thu, 3 Jun 2021 05:30:37 +0000 |
| |
On 5/21/21 10:40 PM, Michael Walle wrote: > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe > > Winbond flashes with OTP support provide a command to erase the OTP > data. This might come in handy during development. > > This was tested with a Winbond W25Q32JW on a LS1028A SoC with the > NXP FSPI controller. > > Signed-off-by: Michael Walle <michael@walle.cc> > --- > drivers/mtd/spi-nor/core.c | 2 +- > drivers/mtd/spi-nor/core.h | 4 ++ > drivers/mtd/spi-nor/otp.c | 83 +++++++++++++++++++++++++++++++++++ > drivers/mtd/spi-nor/winbond.c | 1 + > 4 files changed, 89 insertions(+), 1 deletion(-) > > diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c > index bd2c7717eb10..9551effb6a44 100644 > --- a/drivers/mtd/spi-nor/core.c > +++ b/drivers/mtd/spi-nor/core.c > @@ -1318,7 +1318,7 @@ static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr) > /* > * Initiate the erasure of a single sector > */ > -static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) > +int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) > { > int i; > > diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h > index 28a2e0be97a3..9398a8738857 100644 > --- a/drivers/mtd/spi-nor/core.h > +++ b/drivers/mtd/spi-nor/core.h > @@ -207,6 +207,7 @@ struct spi_nor_otp_organization { > * @read: read from the SPI NOR OTP area. > * @write: write to the SPI NOR OTP area. > * @lock: lock an OTP region. > + * @erase: erase an OTP region. > * @is_locked: check if an OTP region of the SPI NOR is locked. > */ > struct spi_nor_otp_ops { > @@ -214,6 +215,7 @@ struct spi_nor_otp_ops { > int (*write)(struct spi_nor *nor, loff_t addr, size_t len, > const u8 *buf); > int (*lock)(struct spi_nor *nor, unsigned int region); > + int (*erase)(struct spi_nor *nor, loff_t addr); > int (*is_locked)(struct spi_nor *nor, unsigned int region); > }; > > @@ -503,10 +505,12 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, > u8 *buf); > ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, > const u8 *buf); > +int spi_nor_erase_sector(struct spi_nor *nor, u32 addr); > > int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); > int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, > const u8 *buf); > +int spi_nor_otp_erase_secr(struct spi_nor *nor, loff_t addr); > int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region); > int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region); > > diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c > index b87f96593c13..afa348a3af70 100644 > --- a/drivers/mtd/spi-nor/otp.c > +++ b/drivers/mtd/spi-nor/otp.c > @@ -120,6 +120,38 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, > return ret ?: written; > } > > +/** > + * spi_nor_otp_erase_secr() - erase a security register > + * @nor: pointer to 'struct spi_nor' > + * @addr: offset of the security register to be erased > + * > + * Erase a security register by using the SPINOR_OP_ESECR command. > + * > + * For more information on the term "security register", see the documentation > + * of spi_nor_otp_read_secr(). > + * > + * This method is used on GigaDevice and Winbond flashes. > + * > + * Return: 0 on success, -errno otherwise > + */ > +int spi_nor_otp_erase_secr(struct spi_nor *nor, loff_t addr) > +{ > + u8 erase_opcode = nor->erase_opcode; > + int ret; > + > + ret = spi_nor_write_enable(nor); > + if (ret) > + return ret; > + > + nor->erase_opcode = SPINOR_OP_ESECR; > + ret = spi_nor_erase_sector(nor, addr); > + nor->erase_opcode = erase_opcode; > + if (ret) > + return ret; > + > + return spi_nor_wait_till_ready(nor); > +} > + > static int spi_nor_otp_lock_bit_cr(unsigned int region) > { > static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 }; > @@ -360,6 +392,56 @@ static int spi_nor_mtd_otp_write(struct mtd_info *mtd, loff_t to, size_t len, > return spi_nor_mtd_otp_read_write(mtd, to, len, retlen, buf, true); > } > > +static int spi_nor_mtd_otp_erase(struct mtd_info *mtd, loff_t from, size_t len) > +{ > + struct spi_nor *nor = mtd_to_spi_nor(mtd); > + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; > + const size_t rlen = spi_nor_otp_region_len(nor); > + unsigned int region; > + loff_t rstart; > + int ret; > + > + /* OTP erase is optional */ > + if (!ops->erase) > + return -EOPNOTSUPP;
if (!len) return 0;
> + > + if (from < 0 || (from + len) > spi_nor_otp_size(nor)) > + return -EINVAL; > + > + /* the user has to explicitly ask for whole regions */ > + if (!IS_ALIGNED(len, rlen) || !IS_ALIGNED(from, rlen)) > + return -EINVAL; > + > + ret = spi_nor_lock_and_prep(nor); > + if (ret) > + return ret; > + > + ret = spi_nor_mtd_otp_range_is_locked(nor, from, len); > + if (ret < 0) { > + goto out; > + } else if (ret) { > + ret = -EROFS; > + goto out; > + } > + > + while (len) { > + region = spi_nor_otp_offset_to_region(nor, from); > + rstart = spi_nor_otp_region_start(nor, region); > + > + ret = ops->erase(nor, rstart); > + if (ret) > + goto out; > + > + len -= rlen; > + from += rlen; > + } > + > +out: > + spi_nor_unlock_and_unprep(nor); > + > + return ret; > +} > + > static int spi_nor_mtd_otp_lock(struct mtd_info *mtd, loff_t from, size_t len) > { > struct spi_nor *nor = mtd_to_spi_nor(mtd); > @@ -418,4 +500,5 @@ void spi_nor_otp_init(struct spi_nor *nor) > mtd->_read_user_prot_reg = spi_nor_mtd_otp_read; > mtd->_write_user_prot_reg = spi_nor_mtd_otp_write; > mtd->_lock_user_prot_reg = spi_nor_mtd_otp_lock; > + mtd->_erase_user_prot_reg = spi_nor_mtd_otp_erase; > } > diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c > index 9a81c67a60c6..96573f61caf5 100644 > --- a/drivers/mtd/spi-nor/winbond.c > +++ b/drivers/mtd/spi-nor/winbond.c > @@ -139,6 +139,7 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) > static const struct spi_nor_otp_ops winbond_otp_ops = { > .read = spi_nor_otp_read_secr, > .write = spi_nor_otp_write_secr, > + .erase = spi_nor_otp_erase_secr, > .lock = spi_nor_otp_lock_sr2, > .is_locked = spi_nor_otp_is_locked_sr2, > }; > -- > 2.20.1 >
| |