* [PATCH v2] misc: Add RZG2L OTP support
@ 2026-04-17 8:06 Mathieu Othacehe
0 siblings, 0 replies; only message in thread
From: Mathieu Othacehe @ 2026-04-17 8:06 UTC (permalink / raw)
To: Tom Rini, Marek Vasut, Paul Barker, Nobuhiro Iwamatsu
Cc: Mathieu Othacehe, u-boot
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>
---
Changelog
v2: Depend on CMD_FUSE. Use readl_poll_sleep_timeout and clrbits_le32 functions.
drivers/misc/Kconfig | 7 ++
drivers/misc/Makefile | 1 +
drivers/misc/rzg2l_otp.c | 221 +++++++++++++++++++++++++++++++++++++++
3 files changed, 229 insertions(+)
create mode 100644 drivers/misc/rzg2l_otp.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ea785793d18..324a88c9494 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 && CMD_FUSE
+ 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..fe971bcb6c3
--- /dev/null
+++ b/drivers/misc/rzg2l_otp.c
@@ -0,0 +1,221 @@
+// 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>
+#include <linux/iopoll.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 OTP_RDY 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 0x100
+
+#define RZ_OTP_POLL(reg, val, cond) \
+ readl_poll_sleep_timeout((reg), (val), (cond), 1000, 1000000)
+
+static int rzg2l_otp_open(void)
+{
+ u32 val;
+ int ret;
+
+ if (readl(RZ_OTP_PWR) & OTP_PWR) {
+ debug("OTP already powered up\n");
+ return 0;
+ }
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, val, !(val & OTP_RDY));
+ if (ret) {
+ printf("OTP power-up timeout\n");
+ return ret;
+ }
+
+ ret = RZ_OTP_POLL(RZ_OTP_FLAG, val, val & OTP_RDY);
+ if (ret) {
+ printf("OTP power-up timeout\n");
+ return ret;
+ }
+
+ writel(readl(RZ_OTP_PWR) | OTP_PWR | OTP_ACCL, RZ_OTP_PWR);
+
+ return 0;
+}
+
+static int rzg2l_otp_dummy_read(void)
+{
+ u32 val;
+ int ret;
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, val, val & OTP_RDY);
+ if (ret) {
+ printf("Timeout polling ready for OTP read\n");
+ return ret;
+ }
+
+ writel(RZG2L_OTP_BASE + OTP_DUMMY_READ, RZ_OTP_ADRRD);
+ readl(RZ_OTP_DATARD);
+
+ return 0;
+}
+
+static int rzg2l_otp_close(void)
+{
+ u32 val;
+ int ret;
+
+ rzg2l_otp_dummy_read();
+
+ clrbits_le32((void *)RZ_OTP_PWR, OTP_PWR | OTP_ACCL);
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, val, !(val & OTP_RDY));
+ if (ret) {
+ printf("Timeout leaving OTP ready state\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rzg2l_otp_read_word(u32 addr, u32 *val)
+{
+ u32 str;
+ int ret;
+
+ ret = rzg2l_otp_open();
+ if (ret < 0)
+ return ret;
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, str, str & OTP_RDY);
+ if (ret) {
+ printf("Timeout polling ready for OTP read\n");
+ rzg2l_otp_close();
+ return ret;
+ }
+
+ 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)
+{
+ u32 str, otpval;
+ int ret;
+
+ ret = rzg2l_otp_open();
+ if (ret < 0)
+ return ret;
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, str, str & OTP_RDY);
+ if (ret) {
+ printf("Timeout polling ready for OTP write\n");
+ goto close;
+ }
+
+ 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;
+ }
+
+ ret = RZ_OTP_POLL(RZ_OTP_STR, str, str & OTP_RDY);
+ if (ret) {
+ printf("Timeout polling OTP write finished\n");
+ goto close;
+ }
+
+ ret = RZ_OTP_POLL(RZ_OTP_STAWR, str, !(str & OTP_RDY));
+ if (ret) {
+ printf("Timeout polling OTP write finished\n");
+ goto close;
+ }
+
+ 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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-04-17 8:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 8:06 [PATCH v2] misc: Add RZG2L OTP support Mathieu Othacehe
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.