[PATCH v5 7/8] iommu/riscv: Add vendor event support for RISC-V IOMMU HPM

Lv Zheng lv.zheng at linux.spacemit.com
Sat Feb 28 06:45:22 PST 2026


Add a mechanism to allow vendor events to be registered via userspace
jevents. The PMU exposes an "identifier" sysfs attribute derived from the
device tree compatible string (e.g. "spacemit,t100" or "riscv,iommu"),
which perf's jevents uses to match JSON event definitions to the PMU.

Signed-off-by: Lv Zheng <lv.zheng at linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li at spacemit.com>
---
 drivers/iommu/riscv/iommu.c    | 29 +++++++++++++++++++++++++++++
 drivers/iommu/riscv/iommu.h    |  2 ++
 drivers/perf/riscv_iommu_hpm.c | 22 +++++++++++++++++++++-
 include/linux/riscv_iommu.h    |  2 ++
 4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index e10011493228..b8a0542ad5d4 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1664,6 +1664,7 @@ static int riscv_iommu_subdev_add(struct riscv_iommu_device *iommu,
 	subdev->base = params->base;
 	subdev->iommu = iommu;
 	subdev->info = params->info;
+	subdev->identifier = params->identifier;
 
 	auxdev = &subdev->auxdev;
 	auxdev->name = params->name;
@@ -1701,6 +1702,32 @@ static int riscv_iommu_subdev_add(struct riscv_iommu_device *iommu,
 	return ret;
 }
 
+/* Compatible strings that serve as PMU identifier for userspace jevents */
+static const char *const riscv_iommu_hpm_identifiers[] = {
+	"spacemit,t100",
+	"riscv,iommu",
+};
+
+static const char *riscv_iommu_get_hpm_identifier(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	int i, ret;
+
+	if (!np)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(riscv_iommu_hpm_identifiers); i++) {
+		ret = of_property_match_string(np, "compatible",
+					       riscv_iommu_hpm_identifiers[i]);
+		if (ret >= 0)
+			return devm_kstrdup(dev,
+					    riscv_iommu_hpm_identifiers[i],
+					    GFP_KERNEL);
+	}
+
+	return NULL;
+}
+
 static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
 {
 	struct riscv_iommu_hpm_info *hpm_info;
@@ -1728,6 +1755,7 @@ static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
 		.name = "riscv_iommu_hpm",
 		.info = hpm_info,
 		.base = iommu->reg + RISCV_IOMMU_REG_IOCOUNTOVF,
+		.identifier = riscv_iommu_get_hpm_identifier(iommu->dev),
 	};
 
 	if (of_device_is_compatible(iommu->dev->of_node, "spacemit,t100")) {
@@ -1822,6 +1850,7 @@ static void riscv_iommu_enumerate_ioatc(struct riscv_iommu_device *iommu)
 			.name = "spacemit_ioatc_hpm",
 			.info = ioatc_info,
 			.base = base + RISCV_IOMMU_REG_IOCOUNTOVF,
+			.identifier = riscv_iommu_get_hpm_identifier(iommu->dev),
 		};
 
 		ret = riscv_iommu_subdev_add(iommu, &params);
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index f1bb682dd478..7f34c1bbfe25 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -74,11 +74,13 @@ struct riscv_iommu_device {
  * @name: auxiliary device name
  * @info: device-specific info, freed in release
  * @base: PMU register base
+ * @identifier: perf PMU identifier for JSON Compat matching
  */
 struct riscv_iommu_subdev_params {
 	const char *name;
 	void *info;
 	void __iomem *base;
+	const char *identifier;
 };
 
 int riscv_iommu_init(struct riscv_iommu_device *iommu);
diff --git a/drivers/perf/riscv_iommu_hpm.c b/drivers/perf/riscv_iommu_hpm.c
index b166b3cb6d4f..fdc18affbe02 100644
--- a/drivers/perf/riscv_iommu_hpm.c
+++ b/drivers/perf/riscv_iommu_hpm.c
@@ -88,6 +88,7 @@ struct riscv_iommu_hpm {
 	unsigned int irq;
 	unsigned int on_cpu;
 	bool global_filter;
+	const char *identifier;
 	struct hlist_node node;
 	/*
 	 * Layout of events:
@@ -664,8 +665,24 @@ static ssize_t riscv_iommu_hpm_global_filter_show(struct device *dev,
 static struct device_attribute riscv_iommu_hpm_global_filter_attr =
 	__ATTR(global_filter, 0444, riscv_iommu_hpm_global_filter_show, NULL);
 
+static ssize_t riscv_iommu_hpm_identifier_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	struct riscv_iommu_hpm *hpm = dev_get_drvdata(dev);
+
+	if (!hpm->identifier)
+		return 0;
+
+	return sysfs_emit(buf, "%s\n", hpm->identifier);
+}
+
+static struct device_attribute riscv_iommu_hpm_identifier_attr =
+	__ATTR(identifier, 0444, riscv_iommu_hpm_identifier_show, NULL);
+
 static struct attribute *riscv_iommu_hpm_vendor_attrs[] = {
 	&riscv_iommu_hpm_global_filter_attr.attr,
+	&riscv_iommu_hpm_identifier_attr.attr,
 	NULL
 };
 
@@ -891,6 +908,7 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
 
 	hpm->subdev = subdev;
 	hpm->base = subdev->base;
+	hpm->identifier = subdev->identifier;
 	hpm->on_cpu = raw_smp_processor_id();
 	hpm->irq = info->irq;
 	hpm->global_filter = info->global_filter;
@@ -914,7 +932,9 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
 	attr_grps = is_ioatc ? riscv_iommu_hpm_ioatc_attr_grps :
 			       riscv_iommu_hpm_attr_grps;
 
-	hpm_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+	/* jevents name: auxdev->name + "_" + auxdev->id */
+	hpm_name = devm_kasprintf(dev, GFP_KERNEL, "%s_%u", auxdev->name,
+				  auxdev->id);
 	if (!hpm_name)
 		return -ENOMEM;
 
diff --git a/include/linux/riscv_iommu.h b/include/linux/riscv_iommu.h
index 71a961731c22..d21d89251ed5 100644
--- a/include/linux/riscv_iommu.h
+++ b/include/linux/riscv_iommu.h
@@ -24,6 +24,7 @@ struct riscv_iommu_device;
  * @base: PMU register base
  * @iommu: parent IOMMU (opaque)
  * @info: subdevice-specific info, freed in release
+ * @identifier: Vendor identifier for userspace jevent
  */
 struct riscv_iommu_subdev {
 	struct list_head link;
@@ -31,6 +32,7 @@ struct riscv_iommu_subdev {
 	void __iomem *base;
 	struct riscv_iommu_device *iommu;
 	void *info;
+	const char *identifier;
 };
 
 /**
-- 
2.43.0




More information about the linux-riscv mailing list