All of lore.kernel.org
 help / color / mirror / Atom feed
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


             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.