From: Mathieu Othacehe <othacehe@gnu.org>
To: Tom Rini <trini@konsulko.com>,
Marek Vasut <marek.vasut+renesas@mailbox.org>,
Paul Barker <paul@pbarker.dev>,
Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
Cc: Mathieu Othacehe <othacehe@gnu.org>, u-boot@lists.denx.de
Subject: [PATCH] misc: Add RZG2L OTP support
Date: Tue, 14 Apr 2026 11:14:21 +0200 [thread overview]
Message-ID: <20260414091422.16952-1-othacehe@gnu.org> (raw)
Add OTP support through the fuse command. Fusing is directly performed by
U-Boot, which means that the trusted-firmware must allow the non-secure
world to perform fusing operations.
Signed-off-by: Mathieu Othacehe <othacehe@gnu.org>
---
drivers/misc/Kconfig | 7 ++
drivers/misc/Makefile | 1 +
drivers/misc/rzg2l_otp.c | 222 +++++++++++++++++++++++++++++++++++++++
3 files changed, 230 insertions(+)
create mode 100644 drivers/misc/rzg2l_otp.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ea785793d18..16cb800a393 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -132,6 +132,13 @@ config SPL_ROCKCHIP_IODOMAIN
for the IO-domain setting of the SoC to match the voltage supplied
by the regulators.
+config RZG2L_OTP
+ bool "Renesas RZ/G2L OTP support"
+ depends on MISC
+ help
+ Enable support for the OTP controller on
+ Renesas RZ/G2L SoCs.
+
config SIFIVE_OTP
bool "SiFive eMemory OTP driver"
depends on MISC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e2170212e5a..64b2701b672 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_QCOM_GENI) += qcom_geni.o
obj-$(CONFIG_$(PHASE_)ROCKCHIP_EFUSE) += rockchip-efuse.o
obj-$(CONFIG_$(PHASE_)ROCKCHIP_OTP) += rockchip-otp.o
obj-$(CONFIG_$(PHASE_)ROCKCHIP_IODOMAIN) += rockchip-io-domain.o
+obj-$(CONFIG_RZG2L_OTP) += rzg2l_otp.o
obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
obj-$(CONFIG_SIFIVE_OTP) += sifive-otp.o
obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
diff --git a/drivers/misc/rzg2l_otp.c b/drivers/misc/rzg2l_otp.c
new file mode 100644
index 00000000000..3aa3880e679
--- /dev/null
+++ b/drivers/misc/rzg2l_otp.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2026 Mathieu Othacehe <m.othacehe@gmail.com>
+ */
+#include <fuse.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+/*
+ * XXX: To enable direct fusing through U-Boot, the trusted-firmware must
+ * allow the non-secure world to perform fusing operations. This is controlled
+ * by the SYS_SLVACCCTL7 register.
+ */
+#define RZG2L_OTP_BASE 0x11860000
+#define RZG2L_SYSC_BASE 0x11020000
+
+#define RZ_SYS_BASE_DEVID (RZG2L_SYSC_BASE + 0x0A04)
+#define RZ_OTP_BASE_DEVID (RZG2L_OTP_BASE + 0x1178)
+#define RZ_OTP_BASE_CHIPID (RZG2L_OTP_BASE + 0x1140)
+
+#define RZ_OTP_PWR (RZG2L_OTP_BASE + 0x0000)
+#define RZ_OTP_STR (RZG2L_OTP_BASE + 0x0004)
+#define RZ_OTP_STAWR (RZG2L_OTP_BASE + 0x0008)
+#define RZ_OTP_ADRWR (RZG2L_OTP_BASE + 0x000c)
+#define RZ_OTP_DATAWR (RZG2L_OTP_BASE + 0x0010)
+
+#define RZ_OTP_ADRRD (RZG2L_OTP_BASE + 0x0014)
+#define RZ_OTP_DATARD (RZG2L_OTP_BASE + 0x0018)
+
+#define RZ_OTP_FLAG (RZG2L_OTP_BASE + 0x001c)
+
+#define OTP_PWR BIT(0)
+#define ERR_WR_1 BIT(1)
+#define ERR_WR_2 BIT(2)
+#define ERR_WP BIT(3)
+#define OTP_ACCL BIT(4)
+#define ERR_RDY_WR BIT(8)
+#define OTP_DUMMY_READ (0x400 >> 2)
+
+static int rzg2l_otp_open(void)
+{
+ int i = 0;
+
+ if (readl(RZ_OTP_PWR) & 1) {
+ debug("OTP already powered up\n");
+ return 0;
+ }
+
+ while ((readl(RZ_OTP_STR) & 1) ||
+ ((readl(RZ_OTP_FLAG) & 1) == 0)) {
+ if (i++ > 1000) {
+ printf("OTP power-up timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ mdelay(1);
+ }
+
+ writel(readl(RZ_OTP_PWR) | OTP_PWR | OTP_ACCL, RZ_OTP_PWR);
+
+ return 0;
+}
+
+static int rzg2l_otp_dummy_read(void)
+{
+ int i = 0;
+
+ while ((readl(RZ_OTP_STR) & 1) == 0) {
+ if (i++ > 1000) {
+ printf("Timeout polling ready for OTP read\n");
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ }
+
+ writel(RZG2L_OTP_BASE + OTP_DUMMY_READ, RZ_OTP_ADRRD);
+ readl(RZ_OTP_DATARD);
+
+ return 0;
+}
+
+static int rzg2l_otp_close(void)
+{
+ int i = 0;
+ u32 val;
+
+ rzg2l_otp_dummy_read();
+
+ val = readl(RZ_OTP_PWR);
+ val = val & OTP_PWR & OTP_ACCL;
+ writel(val, RZ_OTP_PWR);
+
+ while (readl(RZ_OTP_STR) & 1) {
+ if (i++ > 1000) {
+ printf("Timeout leaving OTP ready state\n");
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+static int rzg2l_otp_read_word(u32 addr, u32 *val)
+{
+ int i = 0;
+ int ret;
+
+ ret = rzg2l_otp_open();
+ if (ret < 0)
+ return ret;
+
+ while ((readl(RZ_OTP_STR) & 1) == 0) {
+ if (i++ > 1000) {
+ printf("Timeout polling ready for OTP read\n");
+ rzg2l_otp_close();
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ }
+
+ writel(addr, RZ_OTP_ADRRD);
+ *val = readl(RZ_OTP_DATARD);
+
+ ret = rzg2l_otp_close();
+
+ return ret;
+}
+
+static int rzg2l_otp_program_word(u32 addr, u32 val)
+{
+ int i = 0;
+ int ret;
+ u32 otpval;
+
+ ret = rzg2l_otp_open();
+ if (ret < 0)
+ return ret;
+
+ while ((readl(RZ_OTP_STR) & 1) == 0) {
+ if (i++ > 1000) {
+ printf("Timeout polling ready for OTP write\n");
+ goto close;
+ }
+ mdelay(1);
+ }
+
+ writel(addr, RZ_OTP_ADRWR);
+ writel(val, RZ_OTP_DATAWR);
+
+ writel(1, RZ_OTP_STAWR);
+
+ if (readl(RZ_OTP_STR) & ERR_RDY_WR) {
+ printf("OTP not ready for write\n");
+ writel(readl(RZ_OTP_STR) & ERR_RDY_WR, RZ_OTP_STR);
+ goto close;
+ }
+
+ i = 0;
+ while ((readl(RZ_OTP_STR) & 1) == 0 ||
+ (readl(RZ_OTP_STAWR) & 1)) {
+ if (i++ > 1000) {
+ printf("Timeout polling OTP write finished\n");
+ goto close;
+ }
+
+ mdelay(1);
+ }
+
+ if ((readl(RZ_OTP_STR) & ERR_WP) ||
+ (readl(RZ_OTP_STR) & ERR_WR_1) ||
+ (readl(RZ_OTP_STR) & ERR_WR_2)) {
+ printf("OTP write error (protected or invalid)\n");
+ goto close;
+ }
+
+ writel(addr, RZ_OTP_ADRRD);
+ otpval = readl(RZ_OTP_DATARD);
+
+ if (otpval != val) {
+ printf("OTP verify failed: wrote 0x%08x read 0x%08x\n",
+ val, otpval);
+ goto close;
+ }
+
+close:
+ ret = rzg2l_otp_close();
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/* U-Boot fuse API */
+
+int fuse_read(u32 bank, u32 word, u32 *val)
+{
+ u32 addr;
+
+ addr = RZG2L_OTP_BASE + (word >> 2);
+
+ return rzg2l_otp_read_word(addr, val);
+}
+
+int fuse_prog(u32 bank, u32 word, u32 val)
+{
+ u32 addr;
+
+ addr = RZG2L_OTP_BASE + (word >> 2);
+
+ return rzg2l_otp_program_word(addr, val);
+}
+
+int fuse_sense(u32 bank, u32 word, u32 *val)
+{
+ /* not supported */
+ return -ENOSYS;
+}
+
+int fuse_override(u32 bank, u32 word, u32 val)
+{
+ /* not supported */
+ return -ENOSYS;
+}
--
2.52.0
next reply other threads:[~2026-04-14 9:15 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-14 9:14 Mathieu Othacehe [this message]
2026-04-14 14:53 ` [PATCH] misc: Add RZG2L OTP support Tom Rini
2026-04-14 22:18 ` Marek Vasut
2026-04-17 8:00 ` Mathieu Othacehe
2026-04-17 13:59 ` Marek Vasut
2026-04-22 9:37 ` Mathieu Othacehe
2026-04-22 19:05 ` Marek Vasut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260414091422.16952-1-othacehe@gnu.org \
--to=othacehe@gnu.org \
--cc=iwamatsu@nigauri.org \
--cc=marek.vasut+renesas@mailbox.org \
--cc=paul@pbarker.dev \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.