[PATCH] iommu: Add RISC-V IOMMU driver

Anirudh Srinivasan via U-Boot <[email protected]> Mon, 03 Aug 2026 15:06:12 -0500
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>
Adds a basic driver for IOMMUs that conform to the RISC-V IOMMU
specification. This driver sets up the IOMMU in bare mode so that
peripherals requiring DMA in U-Boot can function.

Signed-off-by: Anirudh Srinivasan <[email protected]>
---
Hi,

This is a pre-requisite for U-Boot on Tenstorrent Atlantis. On this
platform, the IOMMU comes up in Off mode at boot, so this is needed for
SD and NVME boot to work in U-Boot.
---
 MAINTAINERS                 |  1 +
 drivers/iommu/Kconfig       |  8 +++++
 drivers/iommu/Makefile      |  1 +
 drivers/iommu/riscv_iommu.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 92 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 289a45f47bb..a7b9e78cd79 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1583,6 +1583,7 @@ F:	arch/riscv/
 F:	cmd/riscv/
 F:	doc/arch/riscv.rst
 F:	doc/usage/sbi.rst
+F:	drivers/iommu/riscv_iommu.c
 F:	drivers/sysreset/sysreset_sbi.c
 F:	drivers/timer/andes_plmt_timer.c
 F:	drivers/timer/riscv_aclint_timer.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 2ba6d9c1362..24cfd823c0a 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -40,4 +40,12 @@ config QCOM_HYP_SMMU
 	  Say Y here to enable support for non-boot peripherals like USB by
 	  configuring identity mapped streams for them.
 
+config RISCV_IOMMU
+	bool "RISC-V IOMMU support"
+	depends on IOMMU && RISCV && 64BIT
+	help
+	  Enable support for the RISC-V IOMMU. This driver configures the IOMMU
+	  in Bare mode so that devices requiring DMA in U-Boot can work
+	  without additional configuration.
+
 endmenu
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 438cab8a7c4..caad566694a 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_IOMMU) += iommu-uclass.o
 obj-$(CONFIG_APPLE_DART) += apple_dart.o
 obj-$(CONFIG_SANDBOX) += sandbox_iommu.o
 obj-$(CONFIG_QCOM_HYP_SMMU) += qcom-hyp-smmu.o
+obj-$(CONFIG_RISCV_IOMMU) += riscv_iommu.o
diff --git a/drivers/iommu/riscv_iommu.c b/drivers/iommu/riscv_iommu.c
new file mode 100644
index 00000000000..9e41a397250
--- /dev/null
+++ b/drivers/iommu/riscv_iommu.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Tenstorrent
+ */
+
+#include <dm.h>
+#include <iommu.h>
+
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+
+#define RISCV_IOMMU_REG_DDTP              0x10
+#define RISCV_IOMMU_DDTP_BUSY             BIT_ULL(4)
+#define RISCV_IOMMU_DDTP_TIMEOUT          10000000
+#define RISCV_IOMMU_DDTP_IOMMU_MODE_MASK  GENMASK_ULL(3, 0)
+#define RISCV_IOMMU_DDTP_IOMMU_MODE_OFF   0
+#define RISCV_IOMMU_DDTP_IOMMU_MODE_BARE  1
+
+static int riscv_iommu_probe(struct udevice *dev)
+{
+	void *base;
+	int ret;
+	u64 val;
+
+	base = dev_read_addr_ptr(dev);
+	if (!base)
+		return -EINVAL;
+
+	/* Read DDTP and check if busy bit is cleared */
+	ret = readq_poll_timeout(base + RISCV_IOMMU_REG_DDTP, val,
+				 !(val & RISCV_IOMMU_DDTP_BUSY),
+				 RISCV_IOMMU_DDTP_TIMEOUT);
+	if (ret)
+		return ret;
+
+	/*
+	 * Transitions to Bare mode from any mode other
+	 * than Off are undefined. Skip writing the register
+	 * when already in Bare mode.
+	 */
+	switch (val & RISCV_IOMMU_DDTP_IOMMU_MODE_MASK) {
+	case RISCV_IOMMU_DDTP_IOMMU_MODE_OFF:
+		break;
+	case RISCV_IOMMU_DDTP_IOMMU_MODE_BARE:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+	/* Enable Bare Mode */
+	val &= ~RISCV_IOMMU_DDTP_IOMMU_MODE_MASK;
+	val |= RISCV_IOMMU_DDTP_IOMMU_MODE_BARE;
+	writeq(val, base + RISCV_IOMMU_REG_DDTP);
+
+	/* Read DDTP to check if write was accepted */
+	ret = readq_poll_timeout(base + RISCV_IOMMU_REG_DDTP, val,
+				 !(val & RISCV_IOMMU_DDTP_BUSY),
+				 RISCV_IOMMU_DDTP_TIMEOUT);
+	if (ret)
+		return ret;
+
+	/* Check if DDTP value is Bare */
+	if ((val & RISCV_IOMMU_DDTP_IOMMU_MODE_MASK) !=
+	    RISCV_IOMMU_DDTP_IOMMU_MODE_BARE)
+		return -EIO;
+
+	return 0;
+}
+
+static const struct udevice_id riscv_iommu_ids[] = {
+	{ .compatible = "riscv,iommu" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(riscv_iommu) = {
+	.name = "riscv_iommu",
+	.id = UCLASS_IOMMU,
+	.of_match = riscv_iommu_ids,
+	.probe = riscv_iommu_probe,
+	.flags	= DM_FLAG_VITAL,
+};

---
base-commit: 5c215cb75c3723cbf77c36cbac3e60b001721c79
change-id: 20260803-riscv_iommu-697ed4fefd31

Best regards,
--  
Anirudh Srinivasan <[email protected]>