[PATCH] iommu/riscv: Add DebugFS support for register dump

Sean Chang posted 1 patch 2 hours ago
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
[PATCH] iommu/riscv: Add DebugFS support for register dump
Posted by Sean Chang 2 hours ago
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