On Tue, 17 Sept 2024 at 11:50, Erez Geva <erezgeva@xxxxxxxxxx> wrote: > > From: Erez Geva <ErezGeva2@xxxxxxxxx> > > Macronix SPI-NOR support OTP. > Add callbacks to read, write and lock the OTP. > > Notice Macronix OTP do not support erase. > Every bit written with '0', can not be changed further. > > Notice Macronix OTP do not support single region lock! > The locking includes all regions at once! > > Signed-off-by: Erez Geva <ErezGeva2@xxxxxxxxx> > --- > drivers/mtd/spi-nor/macronix.c | 167 +++++++++++++++++++++++++++++++++ > include/linux/mtd/spi-nor.h | 9 ++ > 2 files changed, 176 insertions(+) > > diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c > index ea6be95e75a5..02aa844d641b 100644 > --- a/drivers/mtd/spi-nor/macronix.c > +++ b/drivers/mtd/spi-nor/macronix.c > @@ -8,6 +8,162 @@ > > #include "core.h" > > +/** > + * macronix_nor_otp_enter() - Send Enter Secured OTP instruction to the chip. > + * @nor: pointer to 'struct spi_nor'. > + * > + * Return: 0 on success, -errno otherwise. > + */ > +static int macronix_nor_otp_enter(struct spi_nor *nor) > +{ > + int error; > + > + error = spi_nor_send_cmd(nor, SPINOR_OP_ENSO); > + if (error) > + dev_dbg(nor->dev, "error %d on Macronix Enter Secured OTP\n", error); > + > + return error; > +} > + > +/** > + * macronix_nor_otp_exit() - Send Exit Secured OTP instruction to the chip. > + * @nor: pointer to 'struct spi_nor'. > + * > + * Return: 0 on success, -errno otherwise. > + */ > +static int macronix_nor_otp_exit(struct spi_nor *nor) > +{ > + int error; > + > + error = spi_nor_send_cmd(nor, SPINOR_OP_EXSO); > + if (error) > + dev_dbg(nor->dev, "error %d on Macronix Exit Secured OTP\n", error); > + > + return error; > +} > + > +/** > + * macronix_nor_otp_read() - read security register Sorry, it should be "read OTP data". > + * @nor: pointer to 'struct spi_nor' > + * @addr: offset to read from > + * @len: number of bytes to read > + * @buf: pointer to dst buffer > + * > + * Return: number of bytes read successfully, -errno otherwise > + */ > +static int macronix_nor_otp_read(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) > +{ > + int ret, error; > + > + error = macronix_nor_otp_enter(nor); > + if (error) > + return error; > + > + ret = spi_nor_read_data(nor, addr, len, buf); > + > + error = macronix_nor_otp_exit(nor); > + > + if (ret < 0) > + dev_dbg(nor->dev, "error %d on Macronix read OTP data\n", ret); > + else if (error) > + return error; > + > + return ret; > +} > + > +/** > + * macronix_nor_otp_write() - write security register Sorry, it should be "write data to OTP". > + * @nor: pointer to 'struct spi_nor' > + * @addr: offset to write to > + * @len: number of bytes to write > + * @buf: pointer to src buffer > + * > + * Return: number of bytes written successfully, -errno otherwise > + */ > +static int macronix_nor_otp_write(struct spi_nor *nor, loff_t addr, size_t len, const u8 *buf) > +{ > + int error, ret = 0; > + > + error = macronix_nor_otp_enter(nor); > + if (error) > + return error; > + > + error = spi_nor_write_enable(nor); > + if (error) > + goto otp_write_err; > + > + ret = spi_nor_write_data(nor, addr, len, buf); > + if (ret < 0) { > + dev_dbg(nor->dev, "error %d on Macronix write OTP data\n", ret); > + goto otp_write_err; > + } > + > + error = spi_nor_wait_till_ready(nor); > + if (error) > + dev_dbg(nor->dev, "error %d on Macronix waiting write OTP finish\n", error); > + > +otp_write_err: > + > + error = macronix_nor_otp_exit(nor); > + > + return ret; > +} > + > +/** > + * macronix_nor_otp_lock() - lock the OTP region > + * @nor: pointer to 'struct spi_nor' > + * @region: OTP region > + * > + * Return: 0 on success, -errno otherwise. > + */ > +static int macronix_nor_otp_lock(struct spi_nor *nor, unsigned int region) > +{ > + int error; > + u8 *rdscur = nor->bouncebuf; > + > + error = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1); > + if (error) { > + dev_dbg(nor->dev, "error %d on read security register\n", error); > + return error; > + } > + > + if (rdscur[0] & SEC_REG_LDSO) > + return 0; > + > + error = spi_nor_write_enable(nor); > + if (error) { > + dev_dbg(nor->dev, "error %d on enable write before update security register\n", > + error); > + return error; > + } > + > + error = spi_nor_send_cmd(nor, SPINOR_OP_WRSCUR); > + if (error) > + dev_dbg(nor->dev, "error %d on update security register\n", error); > + > + return error; > +} > + > +/** > + * macronix_nor_otp_is_locked() - get the OTP region lock status > + * @nor: pointer to 'struct spi_nor' > + * @region: OTP region > + * > + * Return: 1 on lock, 0 on not locked, -errno otherwise. > + */ > +static int macronix_nor_otp_is_locked(struct spi_nor *nor, unsigned int region) > +{ > + int error; > + u8 *rdscur = nor->bouncebuf; > + > + error = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1); > + if (error) { > + dev_dbg(nor->dev, "error %d on read security register\n", error); > + return error; > + } > + return rdscur[0] & SEC_REG_LDSO ? 1 : 0; > +} > + > static int > mx25l25635_post_bfpt_fixups(struct spi_nor *nor, > const struct sfdp_parameter_header *bfpt_header, > @@ -190,8 +346,19 @@ static void macronix_nor_default_init(struct spi_nor *nor) > nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; > } > > +static const struct spi_nor_otp_ops macronix_nor_otp_ops = { > + .read = macronix_nor_otp_read, > + .write = macronix_nor_otp_write, > + /* .erase = Macronix OTP do not support erase, */ > + .lock = macronix_nor_otp_lock, > + .is_locked = macronix_nor_otp_is_locked, > +}; > + > static int macronix_nor_late_init(struct spi_nor *nor) > { > + if (nor->params->otp.org.n_regions) > + nor->params->otp.ops = ¯onix_nor_otp_ops; > + > if (!nor->params->set_4byte_addr_mode) > nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; > > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index cdcfe0fd2e7d..ef834e7fc0ac 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -81,6 +81,15 @@ > #define SPINOR_OP_BP 0x02 /* Byte program */ > #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ > > +/* Macronix OTP registers. */ > +#define SPINOR_OP_RDSCUR 0x2b /* read security register */ > +#define SPINOR_OP_WRSCUR 0x2f /* write security register */ > +#define SPINOR_OP_ENSO 0xb1 /* enter secured OTP */ > +#define SPINOR_OP_EXSO 0xc1 /* exit secured OTP */ > + > +/* Macronix security register values */ > +#define SEC_REG_LDSO BIT(1) /* Lock-down Secured OTP */ > + > /* Used for Macronix and Winbond flashes. */ > #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ > #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ > -- > 2.39.5 >