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]>

Reply via email to