Re: [PATCH v2 3/4] mtd: spi-nor: implement OTP support for Macronix and similar flashes

2020-09-30 Thread Heiko Thiery
Hi Michael,

Am Sa., 12. Sept. 2020 um 00:26 Uhr schrieb Michael Walle :
>
> Use the new OTP ops to implement OTP access on Macronix flashes. The
> Macronix flashes provides one OTP area which is either programmed with
> an electrical serial number and locked by the factory or is empty and can
> be locked by the user. To keep things simple and because most devices
> will have unprogrammed OTP areas, we treat both options as user regions.
> If there will actually be an ESN preprogrammed, it will appear as a
> locked user region.
>
> This was tested on a Macronix MX25L6405D as well as on a Adesto
> AT25SL321.
>
> Signed-off-by: Michael Walle 

Reviewed-by: Heiko Thiery 

> ---
>  drivers/mtd/spi-nor/atmel.c|  13 ++-
>  drivers/mtd/spi-nor/core.c | 170 +
>  drivers/mtd/spi-nor/core.h |   9 ++
>  drivers/mtd/spi-nor/macronix.c |  13 ++-
>  include/linux/mtd/spi-nor.h|   6 ++
>  5 files changed, 209 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c
> index 3f5f21a473a6..1688c9989c6b 100644
> --- a/drivers/mtd/spi-nor/atmel.c
> +++ b/drivers/mtd/spi-nor/atmel.c
> @@ -19,7 +19,8 @@ static const struct flash_info atmel_parts[] = {
> { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
>
> { "at25sl321",  INFO(0x1f4216, 0, 64 * 1024, 64,
> -SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 
> },
> +SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
> +OTP_INFO1(512, 0) },
>
> { "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
> { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
> @@ -29,9 +30,19 @@ static const struct flash_info atmel_parts[] = {
> { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
>  };
>
> +static const struct spi_nor_otp_ops atmel_otp_ops = {
> +   .read = spi_nor_otp_read_otp_mode,
> +   .write = spi_nor_otp_write_otp_mode,
> +   .lock = spi_nor_otp_lock_scur,
> +   .is_locked = spi_nor_otp_is_locked_scur,
> +};
> +
>  static void atmel_default_init(struct spi_nor *nor)
>  {
> nor->flags |= SNOR_F_HAS_LOCK;
> +
> +   if (nor->params->otp_info.n_otps)
> +   nor->params->otp_ops = _otp_ops;
>  }
>
>  static const struct spi_nor_fixups atmel_fixups = {
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 4244f98e4948..348db19958e9 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -2828,6 +2828,176 @@ static int spi_nor_unlock_all(struct spi_nor *nor)
> return 0;
>  }
>
> +/**
> + * spi_nor_set_secured_otp_mode() - Set secured OTP mode
> + * @nor:   pointer to 'struct spi_nor'.
> + * @enable:true to enter the secured OTP mode, false to exit the secured
> + * OTP mode.
> + *
> + * Enter and exit OTP mode by using the command SPINOR_OP_ENSO (B1h) and
> + * SPINOR_EP_EXSO (C1h) command.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_set_secured_otp_mode(struct spi_nor *nor, bool enable)
> +{
> +   u8 cmd = enable ? SPINOR_OP_ENSO : SPINOR_OP_EXSO;
> +   int ret;
> +
> +   ret = spi_nor_simple_cmd(nor, cmd);
> +   if (ret)
> +   dev_dbg(nor->dev, "error %d setting secured OTP mode\n", ret);
> +
> +   return ret;
> +}
> +
> +/**
> + * spi_nor_read_scur() - Read the Security Register using the 
> SPINOR_OP_RDSCUR (2Bh) command.
> + * @nor:   pointer to 'struct spi_nor'
> + * @scur:  pointer to a DMA-able buffer where the value of the
> + * Security Register will be written.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_read_scur(struct spi_nor *nor, u8 *scur)
> +{
> +   int ret;
> +
> +   ret = spi_nor_simple_cmd_din(nor, SPINOR_OP_RDSCUR, scur, 1);
> +   if (ret)
> +   dev_dbg(nor->dev, "error %d reading SCUR\n", ret);
> +
> +   return ret;
> +}
> +
> +/**
> + * spi_nor_write_scur() - Write the Security Register using the 
> SPINOR_OP_WRSCUR (2Fh) command.
> + * @nor:   pointer to 'struct spi_nor'
> + *
> + * This register contains only one OTP bit. The command doesn't take any
> + * arguments. In fact it _must not_ take any arugments. Otherwise the command
> + * is ignored.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_write_scur(struct spi_nor *nor)
> +{
> +   int ret;
> +
> +   ret = spi_nor_simple_cmd(nor, SPINOR_OP_WRSCUR);
> +   if (ret)
> +   dev_dbg(nor->dev, "error %d writing SCUR\n", ret);
> +
> +   return ret;
> +}
> +
> +/**
> + * spi_nor_otp_read_otp_mode() - read OTP data
> + * @nor:   pointer to 'struct spi_nor'
> + * @from:   offset to read from
> + * @len:number of bytes to read
> + * @buf:pointer to dst buffer
> + *
> + * Read OTP data by using the ENSO and EXSO 

[PATCH v2 3/4] mtd: spi-nor: implement OTP support for Macronix and similar flashes

2020-09-11 Thread Michael Walle
Use the new OTP ops to implement OTP access on Macronix flashes. The
Macronix flashes provides one OTP area which is either programmed with
an electrical serial number and locked by the factory or is empty and can
be locked by the user. To keep things simple and because most devices
will have unprogrammed OTP areas, we treat both options as user regions.
If there will actually be an ESN preprogrammed, it will appear as a
locked user region.

This was tested on a Macronix MX25L6405D as well as on a Adesto
AT25SL321.

Signed-off-by: Michael Walle 
---
 drivers/mtd/spi-nor/atmel.c|  13 ++-
 drivers/mtd/spi-nor/core.c | 170 +
 drivers/mtd/spi-nor/core.h |   9 ++
 drivers/mtd/spi-nor/macronix.c |  13 ++-
 include/linux/mtd/spi-nor.h|   6 ++
 5 files changed, 209 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c
index 3f5f21a473a6..1688c9989c6b 100644
--- a/drivers/mtd/spi-nor/atmel.c
+++ b/drivers/mtd/spi-nor/atmel.c
@@ -19,7 +19,8 @@ static const struct flash_info atmel_parts[] = {
{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
 
{ "at25sl321",  INFO(0x1f4216, 0, 64 * 1024, 64,
-SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+OTP_INFO1(512, 0) },
 
{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
@@ -29,9 +30,19 @@ static const struct flash_info atmel_parts[] = {
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
 };
 
+static const struct spi_nor_otp_ops atmel_otp_ops = {
+   .read = spi_nor_otp_read_otp_mode,
+   .write = spi_nor_otp_write_otp_mode,
+   .lock = spi_nor_otp_lock_scur,
+   .is_locked = spi_nor_otp_is_locked_scur,
+};
+
 static void atmel_default_init(struct spi_nor *nor)
 {
nor->flags |= SNOR_F_HAS_LOCK;
+
+   if (nor->params->otp_info.n_otps)
+   nor->params->otp_ops = _otp_ops;
 }
 
 static const struct spi_nor_fixups atmel_fixups = {
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 4244f98e4948..348db19958e9 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2828,6 +2828,176 @@ static int spi_nor_unlock_all(struct spi_nor *nor)
return 0;
 }
 
+/**
+ * spi_nor_set_secured_otp_mode() - Set secured OTP mode
+ * @nor:   pointer to 'struct spi_nor'.
+ * @enable:true to enter the secured OTP mode, false to exit the secured
+ * OTP mode.
+ *
+ * Enter and exit OTP mode by using the command SPINOR_OP_ENSO (B1h) and
+ * SPINOR_EP_EXSO (C1h) command.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_set_secured_otp_mode(struct spi_nor *nor, bool enable)
+{
+   u8 cmd = enable ? SPINOR_OP_ENSO : SPINOR_OP_EXSO;
+   int ret;
+
+   ret = spi_nor_simple_cmd(nor, cmd);
+   if (ret)
+   dev_dbg(nor->dev, "error %d setting secured OTP mode\n", ret);
+
+   return ret;
+}
+
+/**
+ * spi_nor_read_scur() - Read the Security Register using the SPINOR_OP_RDSCUR 
(2Bh) command.
+ * @nor:   pointer to 'struct spi_nor'
+ * @scur:  pointer to a DMA-able buffer where the value of the
+ * Security Register will be written.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_read_scur(struct spi_nor *nor, u8 *scur)
+{
+   int ret;
+
+   ret = spi_nor_simple_cmd_din(nor, SPINOR_OP_RDSCUR, scur, 1);
+   if (ret)
+   dev_dbg(nor->dev, "error %d reading SCUR\n", ret);
+
+   return ret;
+}
+
+/**
+ * spi_nor_write_scur() - Write the Security Register using the 
SPINOR_OP_WRSCUR (2Fh) command.
+ * @nor:   pointer to 'struct spi_nor'
+ *
+ * This register contains only one OTP bit. The command doesn't take any
+ * arguments. In fact it _must not_ take any arugments. Otherwise the command
+ * is ignored.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_write_scur(struct spi_nor *nor)
+{
+   int ret;
+
+   ret = spi_nor_simple_cmd(nor, SPINOR_OP_WRSCUR);
+   if (ret)
+   dev_dbg(nor->dev, "error %d writing SCUR\n", ret);
+
+   return ret;
+}
+
+/**
+ * spi_nor_otp_read_otp_mode() - read OTP data
+ * @nor:   pointer to 'struct spi_nor'
+ * @from:   offset to read from
+ * @len:number of bytes to read
+ * @buf:pointer to dst buffer
+ *
+ * Read OTP data by using the ENSO and EXSO commands. This method is used on
+ * Adesto, Atmel, Macronix and Micron SPI flashes.
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+int spi_nor_otp_read_otp_mode(struct spi_nor *nor, loff_t from, uint64_t len, 
u8 *buf)
+{
+   int ret;
+
+   ret = spi_nor_set_secured_otp_mode(nor, true);
+   if (ret)
+   return ret;