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

Erez erezgeva2 at gmail.com
Tue Sep 17 03:32:15 PDT 2024


On Tue, 17 Sept 2024 at 11:50, Erez Geva <erezgeva at nwtime.org> wrote:
>
> From: Erez Geva <ErezGeva2 at 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 at 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
>



More information about the linux-mtd mailing list