[PATCH 2/3] nvmem: npcm-otp: Add Nuvoton NPCM OTP driver
Kuan-Wei Chiu
visitorckw at gmail.com
Fri Mar 20 03:17:37 AEDT 2026
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 <visitorckw at gmail.com> 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 <visitorckw at gmail.com>
> > ---
> > 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 <visitorckw at gmail.com>
> > +L: linux-arm-kernel at lists.infradead.org (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 <tmyu0 at nuvoton.com>
> > 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 <visitorckw at gmail.com>
> > + */
> > +
> > +#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 <visitorckw at gmail.com>");
> > +MODULE_DESCRIPTION("Nuvoton NPCM7xx OTP NVMEM driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.53.0.851.ga537e3e6e9-goog
> >
>
> Thanks,
>
> Tomer
More information about the openbmc
mailing list