[PATCH v4 4/5] mtd: spi-nor: macronix: add support for OTP

Erez Geva posted 5 patches 2 months, 1 week ago
There is a newer version of this series
[PATCH v4 4/5] mtd: spi-nor: macronix: add support for OTP
Posted by Erez Geva 2 months, 1 week ago
From: Erez Geva <ErezGeva2@gmail.com>

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 include all regions at once!

Signed-off-by: Erez Geva <ErezGeva2@gmail.com>
---
 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
+ * @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
+ * @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 = &macronix_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
Re: [PATCH v4 4/5] mtd: spi-nor: macronix: add support for OTP
Posted by Erez 2 months, 1 week ago
On Tue, 17 Sept 2024 at 11:50, Erez Geva <erezgeva@nwtime.org> wrote:
>
> From: Erez Geva <ErezGeva2@gmail.com>
>
> 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@gmail.com>
> ---
>  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 = &macronix_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
>