drivers/iommu/riscv/Kconfig | 11 ++++++++ drivers/iommu/riscv/Makefile | 1 + drivers/iommu/riscv/debugfs.c | 53 +++++++++++++++++++++++++++++++++++ drivers/iommu/riscv/iommu.c | 3 ++ drivers/iommu/riscv/iommu.h | 12 ++++++++ 5 files changed, 80 insertions(+) create mode 100644 drivers/iommu/riscv/debugfs.c
Add a DebugFS interface to allow users to dump the internal state of
the RISC-V IOMMU hardware. This is essential for diagnosing hardware
initialization issues and debugging driver-hardware communication
such as Command or Fault queue stalls.
The implementation exposes a "registers" file under the device's
debugfs directory. It uses the standard debugfs_regset32 structure
to dump core registers:
* Global configuration: capabilities, fctl, ddtp
* Command Queue: cqb, cqh, cqt, cqcsr
* Fault Queue: fqb, fqh, fqt, fqcsr
* Interrupts: ipsr, icvec
Signed-off-by: Sean Chang <seanwascoding@gmail.com>
---
drivers/iommu/riscv/Kconfig | 11 ++++++++
drivers/iommu/riscv/Makefile | 1 +
drivers/iommu/riscv/debugfs.c | 53 +++++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 3 ++
drivers/iommu/riscv/iommu.h | 12 ++++++++
5 files changed, 80 insertions(+)
create mode 100644 drivers/iommu/riscv/debugfs.c
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index b86e5ab94183..c2ba12d97e98 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
def_bool y if RISCV_IOMMU && PCI_MSI
help
Support for the PCIe implementation of RISC-V IOMMU architecture.
+
+config RISCV_IOMMU_DEBUGFS
+ bool "RISC-V IOMMU Debugfs Support"
+ depends on RISCV_IOMMU
+ depends on IOMMU_DEBUGFS
+ help
+ Expose RISC-V IOMMU internals via debugfs. This includes
+ register dumps, queue status, and other hardware-specific
+ information useful for debugging.
+
+ If unsure, say N.
diff --git a/drivers/iommu/riscv/Makefile b/drivers/iommu/riscv/Makefile
index b5929f9f23e6..9658a10d8b24 100644
--- a/drivers/iommu/riscv/Makefile
+++ b/drivers/iommu/riscv/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += iommu.o iommu-platform.o
obj-$(CONFIG_RISCV_IOMMU_PCI) += iommu-pci.o
+obj-$(CONFIG_RISCV_IOMMU_DEBUGFS) += debugfs.o
diff --git a/drivers/iommu/riscv/debugfs.c b/drivers/iommu/riscv/debugfs.c
new file mode 100644
index 000000000000..52dc50a7fa20
--- /dev/null
+++ b/drivers/iommu/riscv/debugfs.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include "iommu.h"
+
+static const struct debugfs_reg32 riscv_iommu_regs[] = {
+ /* --- Global Configuration --- */
+ { .name = "capabilities", .offset = RISCV_IOMMU_REG_CAPABILITIES },
+ { .name = "fctl", .offset = RISCV_IOMMU_REG_FCTL },
+ { .name = "ddtp", .offset = RISCV_IOMMU_REG_DDTP },
+ /* --- Command Queue --- */
+ { .name = "cqb", .offset = RISCV_IOMMU_REG_CQB },
+ { .name = "cqh", .offset = RISCV_IOMMU_REG_CQH },
+ { .name = "cqt", .offset = RISCV_IOMMU_REG_CQT },
+ { .name = "cqcsr", .offset = RISCV_IOMMU_REG_CQCSR },
+ /* --- Fault Queue --- */
+ { .name = "fqb", .offset = RISCV_IOMMU_REG_FQB },
+ { .name = "fqh", .offset = RISCV_IOMMU_REG_FQH },
+ { .name = "fqt", .offset = RISCV_IOMMU_REG_FQT },
+ { .name = "fqcsr", .offset = RISCV_IOMMU_REG_FQCSR },
+ /* --- Interrupts --- */
+ { .name = "ipsr", .offset = RISCV_IOMMU_REG_IPSR },
+ { .name = "icvec", .offset = RISCV_IOMMU_REG_ICVEC },
+};
+
+void riscv_iommu_debugfs_init(struct riscv_iommu_device *iommu)
+{
+ struct debugfs_regset32 *regset;
+
+ if (!iommu_debugfs_dir)
+ return;
+
+ iommu->debugfs_dir = debugfs_create_dir(dev_name(iommu->dev), iommu_debugfs_dir);
+ if (IS_ERR_OR_NULL(iommu->debugfs_dir))
+ return;
+
+ regset = devm_kzalloc(iommu->dev, sizeof(*regset), GFP_KERNEL);
+ if (!regset)
+ return;
+
+ regset->regs = riscv_iommu_regs;
+ regset->nregs = ARRAY_SIZE(riscv_iommu_regs);
+ regset->base = iommu->reg;
+
+ debugfs_create_regset32("registers", 0444, iommu->debugfs_dir, regset);
+}
+
+void riscv_iommu_debugfs_remove(struct riscv_iommu_device *iommu)
+{
+ debugfs_remove_recursive(iommu->debugfs_dir);
+}
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 368f3cbd2d0a..a4fa0307b32a 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1466,6 +1466,7 @@ void riscv_iommu_remove(struct riscv_iommu_device *iommu)
riscv_iommu_iodir_set_mode(iommu, RISCV_IOMMU_DDTP_IOMMU_MODE_OFF);
riscv_iommu_queue_disable(&iommu->cmdq);
riscv_iommu_queue_disable(&iommu->fltq);
+ riscv_iommu_debugfs_remove(iommu);
}
int riscv_iommu_init(struct riscv_iommu_device *iommu)
@@ -1526,6 +1527,8 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)
goto err_remove_sysfs;
}
+ riscv_iommu_debugfs_init(iommu);
+
return 0;
err_remove_sysfs:
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..1084e4e77455 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -60,6 +60,10 @@ struct riscv_iommu_device {
unsigned int ddt_mode;
dma_addr_t ddt_phys;
u64 *ddt_root;
+
+#ifdef CONFIG_RISCV_IOMMU_DEBUGFS
+ struct dentry *debugfs_dir;
+#endif
};
int riscv_iommu_init(struct riscv_iommu_device *iommu);
@@ -86,4 +90,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
delay_us, timeout_us)
+#ifdef CONFIG_RISCV_IOMMU_DEBUGFS
+void riscv_iommu_debugfs_init(struct riscv_iommu_device *iommu);
+void riscv_iommu_debugfs_remove(struct riscv_iommu_device *iommu);
+#else
+static inline void riscv_iommu_debugfs_init(struct riscv_iommu_device *iommu) { }
+static inline void riscv_iommu_debugfs_remove(struct riscv_iommu_device *iommu) { }
+#endif
+
#endif
--
2.34.1
© 2016 - 2026 Red Hat, Inc.