Subject: Re: [PATCH 2/3] nvmem: npcm-otp: Add Nuvoton NPCM OTP driver

From: Kuan-Wei Chiu <[email-masked]>
Date: Fri, 20 Mar 2026 00:17:37 +0800

Hi Tomer,

On Thu, Mar 19, 2026 at 12:57:04PM +0200, Tomer Maimon wrote:
> Hi Kuan-Wei,
> 
> Thanks for the upstream Nuvoton OTP driver.
> 
> On Wed, 18 Mar 2026 at 21:36, Kuan-Wei Chiu <[email-masked]> wrote:
> >
> > Add a new NVMEM driver for the OTP memory controllers found on Nuvoton
> > NPCM SoCs.
> >
> > This OTP is read-only and manages two independent arrays: Key Storage
> > and Fuse Array, which contain cryptographic keys, hardware strapping,
> > and calibration data.
> >
> > Each array provides 1024 bytes of storage.
> >
> > It can be accessed by writing the target address and a read command
> > to the control registers, and then polling a status register until
> > the data is ready. Concurrent accesses are protected by a mutex.
> >
> > Signed-off-by: Kuan-Wei Chiu <[email-masked]>
> > ---
> >  MAINTAINERS              |   7 +++
> >  drivers/nvmem/Kconfig    |  10 +++
> >  drivers/nvmem/Makefile   |   2 +
> >  drivers/nvmem/npcm-otp.c | 129 +++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 148 insertions(+)
> >  create mode 100644 drivers/nvmem/npcm-otp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 61bf550fd37c..e391e2bcb5f6 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -18894,6 +18894,13 @@ F:     drivers/nubus/
> >  F:     include/linux/nubus.h
> >  F:     include/uapi/linux/nubus.h
> >
> > +NUVOTON NPCM OTP NVMEM DRIVER
> > +M:     Kuan-Wei Chiu <[email-masked]>
> > +L:     [email-masked] (moderated for non-subscribers)
> > +S:     Maintained
> > +F:     Documentation/devicetree/bindings/nvmem/nuvoton,npcm750-otp.yaml
> > +F:     drivers/nvmem/npcm-otp.c
> > +
> >  NUVOTON NCT6694 MFD DRIVER
> >  M:     Ming Yu <[email-masked]>
> >  S:     Supported
> > diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> > index 74ddbd0f79b0..5d065b7448ff 100644
> > --- a/drivers/nvmem/Kconfig
> > +++ b/drivers/nvmem/Kconfig
> > @@ -483,4 +483,14 @@ config NVMEM_QORIQ_EFUSE
> >           This driver can also be built as a module. If so, the module
> >           will be called nvmem_qoriq_efuse.
> >
> > +config NVMEM_NPCM_OTP
> > +       tristate "Nuvoton NPCM7xx OTP Controller"
> > +       depends on ARCH_NPCM || COMPILE_TEST
> > +       help
> > +         This option enables support for the OTP (One-Time Programmable)
> > +         controller found on Nuvoton NPCM7xx BMCs.
> > +
> > +         This driver can also be built as a module. If so, the module
> > +         will be called npcm-otp.
> > +
> >  endif
> > diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> > index 7252b8ec88d4..63c23b304d64 100644
> > --- a/drivers/nvmem/Makefile
> > +++ b/drivers/nvmem/Makefile
> > @@ -95,3 +95,5 @@ obj-$(CONFIG_NVMEM_ZYNQMP)            += nvmem_zynqmp_nvmem.o
> >  nvmem_zynqmp_nvmem-y                   := zynqmp_nvmem.o
> >  obj-$(CONFIG_NVMEM_QORIQ_EFUSE)                += nvmem-qoriq-efuse.o
> >  nvmem-qoriq-efuse-y                    := qoriq-efuse.o
> > +obj-$(CONFIG_NVMEM_NPCM_OTP)           += nvmem-npcm-otp.o
> > +nvmem-npcm-otp-y                       := npcm-otp.o
> > diff --git a/drivers/nvmem/npcm-otp.c b/drivers/nvmem/npcm-otp.c
> > new file mode 100644
> > index 000000000000..abe4bab66c06
> > --- /dev/null
> > +++ b/drivers/nvmem/npcm-otp.c
> > @@ -0,0 +1,129 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Nuvoton NPCM7xx OTP (One-Time Programmable) NVMEM driver
> > + *
> > + * Copyright (C) 2026 Kuan-Wei Chiu <[email-masked]>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/nvmem-provider.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +
> > +/* Register offsets and bitmasks */
> > +#define NPCM_OTP_FST           0x00
> > +#define NPCM_OTP_FADDR         0x04
> > +#define NPCM_OTP_FDATA         0x08
> > +#define NPCM_OTP_FCTL          0x14
> > +
> > +#define FST_RDY                        BIT(0)
> > +#define FST_RDST               BIT(1)
> > +#define FCTL_READ_CMD          0x02
> > +
> > +/* OTP total capacity is 8192 bits (1024 Bytes) */
> > +#define NPCM_OTP_SIZE          1024
> > +
> > +struct npcm_otp {
> > +       void __iomem *base;
> > +       struct mutex lock; /* protects concurrent OTP accesses */
> > +};
> > +
> > +static int npcm_otp_read_byte(struct npcm_otp *otp, unsigned int offset, u8 *val)
> > +{
> > +       u32 fst;
> > +       int ret;
> > +
> > +       writel(offset, otp->base + NPCM_OTP_FADDR);
> > +       writel(FCTL_READ_CMD, otp->base + NPCM_OTP_FCTL);
> > +
> > +       ret = readl_poll_timeout(otp->base + NPCM_OTP_FST, fst,
> > +                                (fst & FST_RDY), 10, 10000);
> > +       if (ret)
> > +               return ret;
> > +
> > +       *val = (u8)(readl(otp->base + NPCM_OTP_FDATA) & 0xFF);
> > +
> > +       /* Clear the status bit to prepare for the next read */
> > +       writel(FST_RDST, otp->base + NPCM_OTP_FST);
> > +
> > +       return 0;
> > +}
> > +
> > +static int npcm_otp_read(void *context, unsigned int offset,
> > +                        void *val, size_t bytes)
> > +{
> > +       struct npcm_otp *otp = context;
> > +       u8 *buf = val;
> > +       int ret = 0;
> > +       size_t i;
> > +
> > +       mutex_lock(&otp->lock);
> > +
> > +       for (i = 0; i < bytes; i++) {
> > +               ret = npcm_otp_read_byte(otp, offset + i, &buf[i]);
> > +               if (ret)
> > +                       break;
> > +       }
> > +
> > +       mutex_unlock(&otp->lock);
> > +
> > +       return ret;
> > +}
> > +
> > +static int npcm_otp_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct npcm_otp *otp;
> > +       struct nvmem_config config = { 0 };
> > +       struct nvmem_device *nvmem;
> > +
> > +       otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
> > +       if (!otp)
> > +               return -ENOMEM;
> > +
> > +       otp->base = devm_platform_ioremap_resource(pdev, 0);
> > +       if (IS_ERR(otp->base))
> > +               return PTR_ERR(otp->base);
> Since the driver already opens and manages the OTP registers, it
> should also be responsible for handling the OTP index.
> The NPCM AES driver can make use of keys stored in OTP.
> We would be happy to help add support for OTP‑stored keys to the
> upstream driver.
> If you’d like, we can set up a meeting to discuss how this can be implemented.

I would be very happy to collaborate. :)

Regarding scheduling a meeting, I will be traveling abroad from 03/22
to 04/01 with very limited and unstable network access, so I would
prefer to avoid that window. We can coordinate the exact date and time
off-list.

Regards,
Kuan-Wei

> > +
> > +       mutex_init(&otp->lock);
> > +
> > +       config.dev = dev;
> > +       config.name = dev_name(dev);
> > +       config.read_only = true;
> > +       config.word_size = 1;
> > +       config.stride = 1;
> > +       config.reg_read = npcm_otp_read;
> > +       config.priv = otp;
> > +       config.size = NPCM_OTP_SIZE;
> Suggest setting the size according to the BMC SoC,
> The NPCM7xx has 2 8Kbyte OTP, and the NPCM8xx 64KB size.OPT
> > +
> > +       nvmem = devm_nvmem_register(dev, &config);
> > +       if (IS_ERR(nvmem))
> > +               return dev_err_probe(dev, PTR_ERR(nvmem), "Failed to register nvmem\n");
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct of_device_id npcm_otp_dt_ids[] = {
> > +       { .compatible = "nuvoton,npcm750-key-storage" },
> > +       { .compatible = "nuvoton,npcm750-fuse-array" },
> > +       { }
> > +};
> > +MODULE_DEVICE_TABLE(of, npcm_otp_dt_ids);
> > +
> > +static struct platform_driver npcm_otp_driver = {
> > +       .probe          = npcm_otp_probe,
> > +       .driver         = {
> > +               .name   = "npcm-otp",
> > +               .of_match_table = npcm_otp_dt_ids,
> > +       },
> > +};
> > +module_platform_driver(npcm_otp_driver);
> > +
> > +MODULE_AUTHOR("Kuan-Wei Chiu <[email-masked]>");
> > +MODULE_DESCRIPTION("Nuvoton NPCM7xx OTP NVMEM driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.53.0.851.ga537e3e6e9-goog
> >
> 
> Thanks,
> 
> Tomer