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 v2] misc: Add RZG2L OTP support
Date: Fri, 17 Apr 2026 10:06:15 +0200	[thread overview]
Message-ID: <20260417080616.5050-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>
---
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


                 reply	other threads:[~2026-04-17  8:06 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260417080616.5050-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.