[PATCH 2/3] nvmem: npcm-otp: Add Nuvoton NPCM OTP driver
Tomer Maimon
tmaimon77 at gmail.com
Thu Mar 19 21:57:04 AEDT 2026
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.
> +
> + 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