* [PATCH v5 1/8] iommu/riscv: Enable IOMMU DMA mapping support
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
@ 2026-02-28 14:44 ` Lv Zheng
2026-02-28 14:44 ` [PATCH v5 2/8] iommu/riscv: Add auxiliary bus framework and HPM device support Lv Zheng
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:44 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
From: Jingyu Li <joey.li@spacemit.com>
Enable IOMMU DMA mapping support for RISC-V, so that DMACs can be tested
with translation enabled.
Known Possible Issue:
1. When CONFIG_IOMMU_DMA is enabled, on the tested Linux, RISC-V IOMMU is
lack of PCIe support, causing riscv_iommu_fault:522 in dealing with
NVMe PCIe devices.
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
---
drivers/iommu/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f86262b11416..34d8a792339f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -151,7 +151,7 @@ config OF_IOMMU
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
- def_bool ARM64 || X86 || S390
+ def_bool ARM64 || X86 || S390 || RISCV
select DMA_OPS_HELPERS
select IOMMU_API
select IOMMU_IOVA
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 2/8] iommu/riscv: Add auxiliary bus framework and HPM device support
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
2026-02-28 14:44 ` [PATCH v5 1/8] iommu/riscv: Enable IOMMU DMA mapping support Lv Zheng
@ 2026-02-28 14:44 ` Lv Zheng
2026-02-28 14:44 ` [PATCH v5 3/8] iommu/riscv: Add HPM support for performance monitoring Lv Zheng
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:44 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
From: Jingyu Li <joey.li@spacemit.com>
Introduces auxiliary bus support for RISC-V IOMMU to enable modular
extension of IOMMU capabilities. The framework allows creating auxiliary
devices that can be bound to separate drivers.
The IOMMU HPM featured PMU device ("iommu.riscv_iommu_hpm.0") is created
and registered as RISC-V IOMMU auxiliary device.
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Link: https://github.com/riscv-non-isa/riscv-iommu
Cc: Zong Li <zong.li@sifive.com>
Cc: Yaxing Guo <guoyaxing@bosc.ac.cn>
---
drivers/iommu/riscv/Kconfig | 1 +
drivers/iommu/riscv/iommu-pci.c | 8 +-
drivers/iommu/riscv/iommu-platform.c | 3 +
drivers/iommu/riscv/iommu.c | 169 +++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.h | 21 ++++
include/linux/riscv_iommu.h | 75 ++++++++++++
6 files changed, 276 insertions(+), 1 deletion(-)
create mode 100644 include/linux/riscv_iommu.h
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index c071816f59a6..26122a3a73d2 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -6,6 +6,7 @@ config RISCV_IOMMU
depends on RISCV && 64BIT
default y
select IOMMU_API
+ select AUXILIARY_BUS
help
Support for implementations of the RISC-V IOMMU architecture that
complements the RISC-V MMU capabilities, providing similar address
diff --git a/drivers/iommu/riscv/iommu-pci.c b/drivers/iommu/riscv/iommu-pci.c
index d82d2b00904c..478e72e9a285 100644
--- a/drivers/iommu/riscv/iommu-pci.c
+++ b/drivers/iommu/riscv/iommu-pci.c
@@ -34,6 +34,8 @@ static int riscv_iommu_pci_probe(struct pci_dev *pdev, const struct pci_device_i
{
struct device *dev = &pdev->dev;
struct riscv_iommu_device *iommu;
+ phys_addr_t reg_phys;
+ resource_size_t reg_size;
int rc, vec;
rc = pcim_enable_device(pdev);
@@ -43,7 +45,9 @@ static int riscv_iommu_pci_probe(struct pci_dev *pdev, const struct pci_device_i
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM))
return -ENODEV;
- if (pci_resource_len(pdev, 0) < RISCV_IOMMU_REG_SIZE)
+ reg_phys = pci_resource_start(pdev, 0);
+ reg_size = pci_resource_len(pdev, 0);
+ if (reg_size < RISCV_IOMMU_REG_SIZE)
return -ENODEV;
rc = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
@@ -56,6 +60,8 @@ static int riscv_iommu_pci_probe(struct pci_dev *pdev, const struct pci_device_i
iommu->dev = dev;
iommu->reg = pcim_iomap_table(pdev)[0];
+ iommu->reg_phys = reg_phys;
+ iommu->reg_size = reg_size;
pci_set_master(pdev);
dev_set_drvdata(dev, iommu);
diff --git a/drivers/iommu/riscv/iommu-platform.c b/drivers/iommu/riscv/iommu-platform.c
index 83a28c83f991..e8e52bca8856 100644
--- a/drivers/iommu/riscv/iommu-platform.c
+++ b/drivers/iommu/riscv/iommu-platform.c
@@ -62,6 +62,9 @@ static int riscv_iommu_platform_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(iommu->reg),
"could not map register region\n");
+ iommu->reg_phys = res->start;
+ iommu->reg_size = resource_size(res);
+
dev_set_drvdata(dev, iommu);
/* Check device reported capabilities / features. */
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index d9429097a2b5..1aa942486e3a 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -16,6 +16,7 @@
#include <linux/acpi_rimt.h>
#include <linux/compiler.h>
#include <linux/crash_dump.h>
+#include <linux/idr.h>
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
@@ -47,11 +48,28 @@
static DEFINE_IDA(riscv_iommu_pscids);
#define RISCV_IOMMU_MAX_PSCID (BIT(20) - 1)
+static DEFINE_IDA(riscv_iommu_subdev_ida);
+
/* Device resource-managed allocations */
struct riscv_iommu_devres {
void *addr;
};
+bool riscv_iommu_pmip_status(struct riscv_iommu_subdev *subdev)
+{
+ u32 ipsr = riscv_iommu_readl(subdev->iommu, RISCV_IOMMU_REG_IPSR);
+
+ return !!(ipsr & RISCV_IOMMU_IPSR_PMIP);
+}
+EXPORT_SYMBOL_GPL(riscv_iommu_pmip_status);
+
+void riscv_iommu_clear_pmip(struct riscv_iommu_subdev *subdev)
+{
+ riscv_iommu_writel(subdev->iommu, RISCV_IOMMU_REG_IPSR,
+ RISCV_IOMMU_IPSR_PMIP);
+}
+EXPORT_SYMBOL_GPL(riscv_iommu_clear_pmip);
+
static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
{
struct riscv_iommu_devres *devres = res;
@@ -1602,10 +1620,154 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
return 0;
}
+static void riscv_iommu_subdev_release(struct device *dev)
+{
+ struct riscv_iommu_subdev *subdev = riscv_iommu_get_subdev(dev);
+
+ ida_free(&riscv_iommu_subdev_ida, subdev->auxdev.id);
+ kfree(subdev->info);
+ kfree(subdev);
+}
+
+static int riscv_iommu_subdev_add(struct riscv_iommu_device *iommu,
+ const struct riscv_iommu_subdev_params *params)
+{
+ struct riscv_iommu_subdev *subdev;
+ struct auxiliary_device *auxdev;
+ int id, ret;
+
+ if (!params->info)
+ return -EINVAL;
+
+ id = ida_alloc(&riscv_iommu_subdev_ida, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
+ if (!subdev) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ subdev->base = params->base;
+ subdev->iommu = iommu;
+ subdev->info = params->info;
+
+ auxdev = &subdev->auxdev;
+ auxdev->name = params->name;
+ auxdev->id = id;
+ auxdev->dev.parent = iommu->dev;
+ auxdev->dev.release = riscv_iommu_subdev_release;
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret) {
+ dev_err(iommu->dev, "Failed to init %s auxiliary device: %d\n",
+ params->name, ret);
+ goto err_free;
+ }
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret) {
+ dev_err(iommu->dev, "Failed to add %s auxiliary device: %d\n",
+ params->name, ret);
+ goto err_uninit;
+ }
+
+ spin_lock(&iommu->subdev_lock);
+ list_add_tail(&subdev->link, &iommu->subdev_list);
+ spin_unlock(&iommu->subdev_lock);
+ dev_info(iommu->dev, "%s auxiliary device created\n", params->name);
+ return 0;
+
+err_uninit:
+ auxiliary_device_uninit(auxdev);
+ return ret;
+
+err_free:
+ kfree(subdev);
+ ida_free(&riscv_iommu_subdev_ida, id);
+ return ret;
+}
+
+static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
+{
+ struct riscv_iommu_hpm_info *hpm_info;
+ struct riscv_iommu_subdev_params params;
+ int irq;
+ int ret;
+
+ if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_HPM))
+ return;
+
+ irq = iommu->irqs[riscv_iommu_queue_vec(iommu, RISCV_IOMMU_INTR_PM)];
+ if (irq <= 0) {
+ dev_err(iommu->dev, "HPM: No IRQ available\n");
+ return;
+ }
+
+ hpm_info = kzalloc(sizeof(*hpm_info), GFP_KERNEL);
+ if (!hpm_info)
+ return;
+
+ hpm_info->irq = irq;
+
+ params = (struct riscv_iommu_subdev_params) {
+ .name = "riscv_iommu_hpm",
+ .info = hpm_info,
+ .base = iommu->reg + RISCV_IOMMU_REG_IOCOUNTOVF,
+ };
+
+ ret = riscv_iommu_subdev_add(iommu, ¶ms);
+ if (ret) {
+ kfree(hpm_info);
+ dev_warn(iommu->dev,
+ "Failed to enumerate HPM auxiliary device: %d\n",
+ ret);
+ }
+}
+
+/**
+ * riscv_iommu_subdev_setup - Enumerate auxiliary bus subdevices
+ *
+ * @iommu: RISC-V IOMMU device
+ *
+ * Enumerates HPM, or other extended subdevices via the auxiliary bus. To
+ * add new extended device types, implement an enumerate function and call
+ * it from here.
+ */
+void riscv_iommu_subdev_setup(struct riscv_iommu_device *iommu)
+{
+ riscv_iommu_enumerate_hpm(iommu);
+}
+
+/**
+ * riscv_iommu_subdev_cleanup - Remove all auxiliary bus subdevices
+ *
+ * @iommu: RISC-V IOMMU device
+ *
+ * Iterates over the subdev_list in reverse order, deletes each auxiliary
+ * device from the bus and uninitializes it.
+ */
+void riscv_iommu_subdev_cleanup(struct riscv_iommu_device *iommu)
+{
+ struct riscv_iommu_subdev *subdev, *next;
+
+ spin_lock(&iommu->subdev_lock);
+ list_for_each_entry_safe_reverse(subdev, next, &iommu->subdev_list, link) {
+ list_del_init(&subdev->link);
+ spin_unlock(&iommu->subdev_lock);
+ auxiliary_device_delete(&subdev->auxdev);
+ auxiliary_device_uninit(&subdev->auxdev);
+ spin_lock(&iommu->subdev_lock);
+ }
+ spin_unlock(&iommu->subdev_lock);
+}
+
void riscv_iommu_remove(struct riscv_iommu_device *iommu)
{
iommu_device_unregister(&iommu->iommu);
iommu_device_sysfs_remove(&iommu->iommu);
+ riscv_iommu_subdev_cleanup(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);
@@ -1615,6 +1777,8 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)
{
int rc;
+ spin_lock_init(&iommu->subdev_lock);
+ INIT_LIST_HEAD(&iommu->subdev_list);
RISCV_IOMMU_QUEUE_INIT(&iommu->cmdq, CQ);
RISCV_IOMMU_QUEUE_INIT(&iommu->fltq, FQ);
@@ -1669,6 +1833,11 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)
goto err_remove_sysfs;
}
+ /* Initialize auxiliary devices for extended features. These are not
+ * critical to IOMMU operation, so failures are non-fatal.
+ */
+ riscv_iommu_subdev_setup(iommu);
+
return 0;
err_remove_sysfs:
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..1296625488ef 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,6 +14,7 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#include <linux/riscv_iommu.h>
#include "iommu-bits.h"
@@ -42,6 +43,8 @@ struct riscv_iommu_device {
/* hardware control register space */
void __iomem *reg;
+ phys_addr_t reg_phys;
+ resource_size_t reg_size;
/* supported and enabled hardware capabilities */
u64 caps;
@@ -60,11 +63,29 @@ struct riscv_iommu_device {
unsigned int ddt_mode;
dma_addr_t ddt_phys;
u64 *ddt_root;
+
+ /* auxiliary subdevices */
+ spinlock_t subdev_lock;
+ struct list_head subdev_list;
+};
+
+/**
+ * struct riscv_iommu_subdev_params - params for adding auxiliary subdevice
+ * @name: auxiliary device name
+ * @info: device-specific info, freed in release
+ * @base: PMU register base
+ */
+struct riscv_iommu_subdev_params {
+ const char *name;
+ void *info;
+ void __iomem *base;
};
int riscv_iommu_init(struct riscv_iommu_device *iommu);
void riscv_iommu_remove(struct riscv_iommu_device *iommu);
void riscv_iommu_disable(struct riscv_iommu_device *iommu);
+void riscv_iommu_subdev_setup(struct riscv_iommu_device *iommu);
+void riscv_iommu_subdev_cleanup(struct riscv_iommu_device *iommu);
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
diff --git a/include/linux/riscv_iommu.h b/include/linux/riscv_iommu.h
new file mode 100644
index 000000000000..0447bc4d1fab
--- /dev/null
+++ b/include/linux/riscv_iommu.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * RISC-V IOMMU Common Interface
+ *
+ * This header provides a common interface for sharing resources between
+ * the RISC-V IOMMU driver and its auxiliary bus child drivers.
+ *
+ * Copyright (C) 2026 SpacemiT Technologies Inc.
+ * Author: 2026 Jingyu Li <joey.li@spacemit.com>
+ * Lv Zheng <lv.zheng@spacemit.com>
+ */
+
+#ifndef _LINUX_RISCV_IOMMU_H_
+#define _LINUX_RISCV_IOMMU_H_
+
+#include <linux/auxiliary_bus.h>
+
+struct riscv_iommu_device;
+
+/**
+ * struct riscv_iommu_subdev - RISC-V IOMMU auxiliary bus subdevice
+ * @link: list node for iommu->subdev_list
+ * @auxdev: auxiliary bus device ((use auxdev.id for unique id)
+ * @base: PMU register base
+ * @iommu: parent IOMMU (opaque)
+ * @info: subdevice-specific info, freed in release
+ */
+struct riscv_iommu_subdev {
+ struct list_head link;
+ struct auxiliary_device auxdev;
+ void __iomem *base;
+ struct riscv_iommu_device *iommu;
+ void *info;
+};
+
+/**
+ * struct riscv_iommu_hpm_info - HPM info for IOATS (main IOMMU HPM)
+ * @irq: interrupt number
+ */
+struct riscv_iommu_hpm_info {
+ unsigned int irq;
+};
+
+/**
+ * riscv_iommu_get_subdev - get riscv_iommu_subdev from device
+ *
+ * @dev: &device of the auxiliary device (auxdev->dev)
+ *
+ * Returns the riscv_iommu_subdev pointer, or NULL if @dev is NULL.
+ */
+static inline struct riscv_iommu_subdev *riscv_iommu_get_subdev(struct device *dev)
+{
+ if (!dev)
+ return NULL;
+ return container_of(container_of(dev, struct auxiliary_device, dev),
+ struct riscv_iommu_subdev, auxdev);
+}
+
+/**
+ * riscv_iommu_pmip_status - test if PM interrupt is pending
+ *
+ * @subdev: subdevice with iommu
+ *
+ * Returns true if PM interrupt pending, false otherwise.
+ */
+bool riscv_iommu_pmip_status(struct riscv_iommu_subdev *subdev);
+
+/**
+ * riscv_iommu_clear_pmip - clear PMIP bit in IPSR to ack PMU interrupt
+ *
+ * @subdev: subdevice with iommu
+ */
+void riscv_iommu_clear_pmip(struct riscv_iommu_subdev *subdev);
+
+#endif /* _LINUX_RISCV_IOMMU_H_ */
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 3/8] iommu/riscv: Add HPM support for performance monitoring
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
2026-02-28 14:44 ` [PATCH v5 1/8] iommu/riscv: Enable IOMMU DMA mapping support Lv Zheng
2026-02-28 14:44 ` [PATCH v5 2/8] iommu/riscv: Add auxiliary bus framework and HPM device support Lv Zheng
@ 2026-02-28 14:44 ` Lv Zheng
2026-02-28 14:44 ` [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features Lv Zheng
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:44 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
From: Jingyu Li <joey.li@spacemit.com>
Introduces perf-based HPM driver for RISC-V IOMMU, enabling performance
monitoring capabilities.
Note that the RISC-V IOMMU HPM module uses COUNTER_MAX-1 as a static
counter index of HPMCYCLES, and 0~COUNTER_MAX-2 as the dynamic counter
indexes of other HPMEVENTS in order to correctly index into IOHPMEVT and
IOHPMCTR registers that have already been defined in the iommu-bits.h.
However the users treat 0 as the index of HPMCYCLES and 1~COUNTER_MAX-1 as
the indexes of other HPMEVENTS, thus care should be taken in dealing with
counter indexes between userspace and kernel space.
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Link: https://github.com/riscv-non-isa/riscv-iommu
Cc: Zong Li <zong.li@sifive.com>
Cc: Yaxing Guo <guoyaxing@bosc.ac.cn>
---
drivers/perf/Kconfig | 9 +
drivers/perf/Makefile | 1 +
drivers/perf/riscv_iommu_hpm.c | 864 +++++++++++++++++++++++++++++++++
3 files changed, 874 insertions(+)
create mode 100644 drivers/perf/riscv_iommu_hpm.c
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 638321fc9800..1edf66d8ae41 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -311,4 +311,13 @@ config MARVELL_PEM_PMU
Enable support for PCIe Interface performance monitoring
on Marvell platform.
+config RISCV_IOMMU_HPM
+ tristate "RISC-V IOMMU HPM support"
+ depends on RISCV_IOMMU && PERF_EVENTS
+ help
+ Support for the Hardware Performance Monitor (HPM) in RISC-V IOMMU,
+ which provides monitoring of transactions passing through the
+ IOMMU and allows the resulting information to be filtered based
+ on the device/process ID of the corresponding master.
+
endmenu
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index ea52711a87e3..89a347232c29 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -35,3 +35,4 @@ obj-$(CONFIG_DWC_PCIE_PMU) += dwc_pcie_pmu.o
obj-$(CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU) += arm_cspmu/
obj-$(CONFIG_MESON_DDR_PMU) += amlogic/
obj-$(CONFIG_CXL_PMU) += cxl_pmu.o
+obj-$(CONFIG_RISCV_IOMMU_HPM) += riscv_iommu_hpm.o
diff --git a/drivers/perf/riscv_iommu_hpm.c b/drivers/perf/riscv_iommu_hpm.c
new file mode 100644
index 000000000000..4d5ce0f380e7
--- /dev/null
+++ b/drivers/perf/riscv_iommu_hpm.c
@@ -0,0 +1,864 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RISC-V IOMMU Hardware Performance Monitor driver
+ *
+ * This driver provides perf-based performance monitoring for RISC-V IOMMU
+ * devices via the auxiliary bus framework.
+ *
+ * Copyright (C) 2026 SpacemiT Technologies Inc.
+ * Author: 2026 Jingyu Li <joey.li@spacemit.com>
+ * Lv Zheng <lv.zheng@spacemit.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/perf_event.h>
+#include <linux/riscv_iommu.h>
+
+/* Performance monitoring registers */
+#define RISCV_IOMMU_REG_IOCOUNTOVF 0x0000
+#define RISCV_IOMMU_IOCOUNTOVF_CY BIT(0)
+#define RISCV_IOMMU_IOCOUNTOVF_HPM GENMASK_ULL(31, 1)
+
+#define RISCV_IOMMU_REG_IOCOUNTINH 0x0004
+#define RISCV_IOMMU_IOCOUNTINH_CY BIT(0)
+#define RISCV_IOMMU_IOCOUNTINH_HPM GENMASK(31, 1)
+
+#define RISCV_IOMMU_REG_IOHPMCYCLES 0x0008
+#define RISCV_IOMMU_IOHPMCYCLES_COUNTER GENMASK_ULL(62, 0)
+#define RISCV_IOMMU_IOHPMCYCLES_OF BIT_ULL(63)
+
+#define RISCV_IOMMU_REG_IOHPMCTR_BASE 0x0010
+#define RISCV_IOMMU_REG_IOHPMCTR(_n) (RISCV_IOMMU_REG_IOHPMCTR_BASE + ((_n) * 0x8))
+#define RISCV_IOMMU_IOHPMEVENT_COUNTER GENMASK_ULL(63, 0)
+
+#define RISCV_IOMMU_REG_IOHPMEVT_BASE 0x0108
+#define RISCV_IOMMU_REG_IOHPMEVT(_n) (RISCV_IOMMU_REG_IOHPMEVT_BASE + ((_n) * 0x8))
+#define RISCV_IOMMU_IOHPMEVT_EVENTID GENMASK_ULL(14, 0)
+#define RISCV_IOMMU_IOHPMEVT_DMASK BIT_ULL(15)
+#define RISCV_IOMMU_IOHPMEVT_PID_PSCID GENMASK_ULL(35, 16)
+#define RISCV_IOMMU_IOHPMEVT_DID_GSCID GENMASK_ULL(59, 36)
+#define RISCV_IOMMU_IOHPMEVT_PV_PSCV BIT_ULL(60)
+#define RISCV_IOMMU_IOHPMEVT_DV_GSCV BIT_ULL(61)
+#define RISCV_IOMMU_IOHPMEVT_IDT BIT_ULL(62)
+#define RISCV_IOMMU_IOHPMEVT_OF BIT_ULL(63)
+
+/* HPM counter definitions */
+#define RISCV_IOMMU_IOHPMEVT_CNT 31
+#define RISCV_IOMMU_HPMCOUNTER_CYCLES RISCV_IOMMU_IOHPMEVT_CNT
+#define RISCV_IOMMU_HPMCOUNTER_MAX (RISCV_IOMMU_IOHPMEVT_CNT + 1)
+
+/* HPM event IDs */
+enum riscv_iommu_hpmevent_id {
+ RISCV_IOMMU_HPMEVENT_INVALID = 0,
+ RISCV_IOMMU_HPMEVENT_URQ = 1,
+ RISCV_IOMMU_HPMEVENT_TRQ = 2,
+ RISCV_IOMMU_HPMEVENT_ATS_RQ = 3,
+ RISCV_IOMMU_HPMEVENT_TLB_MISS = 4,
+ RISCV_IOMMU_HPMEVENT_DD_WALK = 5,
+ RISCV_IOMMU_HPMEVENT_PD_WALK = 6,
+ RISCV_IOMMU_HPMEVENT_S_VS_WALKS = 7,
+ RISCV_IOMMU_HPMEVENT_G_WALKS = 8,
+ RISCV_IOMMU_HPMEVENT_MAX = 128
+};
+
+#define RISCV_IOMMU_HPMEVENT_CYCLES RISCV_IOMMU_HPMEVENT_INVALID
+#define RISCV_IOMMU_HPMEVENT_MAX 128
+
+#define to_iommu_hpm(p) (container_of(p, struct riscv_iommu_hpm, pmu))
+
+#define RISCV_IOMMU_HPM_EVENT_EXTRACTOR(_n, _c, _s, _e) \
+ static inline u32 get_##_n(struct perf_event *event) \
+ { \
+ return FIELD_GET(GENMASK_ULL(_e, _s), \
+ event->attr._c); \
+ }
+
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(event, config, 0, 14);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_dmask, config1, 15, 15);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_pid_pscid, config1, 16, 35);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_did_gscid, config1, 36, 59);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_pv_pscv, config1, 60, 60);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_dv_gscv, config1, 61, 61);
+RISCV_IOMMU_HPM_EVENT_EXTRACTOR(filter_idt, config1, 62, 62);
+
+struct riscv_iommu_hpm {
+ struct pmu pmu;
+ struct riscv_iommu_subdev *subdev;
+ void __iomem *base;
+ unsigned int irq;
+ unsigned int on_cpu;
+ struct hlist_node node;
+ /*
+ * Layout of events:
+ * 0 -> HPMCYCLES
+ * 1...n-1 -> HPMEVENTS
+ */
+ struct perf_event *events[RISCV_IOMMU_HPMCOUNTER_MAX];
+ DECLARE_BITMAP(supported_events, RISCV_IOMMU_HPMEVENT_MAX);
+ DECLARE_BITMAP(used_counters, RISCV_IOMMU_HPMCOUNTER_MAX);
+ unsigned int num_counters;
+};
+
+static DEFINE_MUTEX(riscv_iommu_hpm_lock);
+static int cpuhp_state_num = -1;
+
+static inline void riscv_iommu_hpm_writel(struct riscv_iommu_hpm *hpm, u32 reg,
+ u32 val)
+{
+ writel_relaxed(val, hpm->base + reg);
+}
+
+static inline u32 riscv_iommu_hpm_readl(struct riscv_iommu_hpm *hpm, u32 reg)
+{
+ return readl_relaxed(hpm->base + reg);
+}
+
+static inline void riscv_iommu_hpm_writeq(struct riscv_iommu_hpm *hpm, u32 reg,
+ u64 val)
+{
+ writeq_relaxed(val, hpm->base + reg);
+}
+
+static inline u64 riscv_iommu_hpm_readq(struct riscv_iommu_hpm *hpm, u32 reg)
+{
+ return readq_relaxed(hpm->base + reg);
+}
+
+/* All iohpmcycles and iohpmctr registers are 64-bit wide and WARL. If a
+ * 32-bit counter width should be supported according to a newer revision
+ * of the standard or a vendor specific implementation, the following
+ * functions should be extended.
+ */
+static inline void riscv_iommu_hpm_cycles_set_value(struct riscv_iommu_hpm *hpm,
+ u64 value)
+{
+ riscv_iommu_hpm_writeq(hpm, RISCV_IOMMU_REG_IOHPMCYCLES,
+ value & RISCV_IOMMU_IOHPMCYCLES_COUNTER);
+}
+
+static inline u64 riscv_iommu_hpm_cycles_get_value(struct riscv_iommu_hpm *hpm)
+{
+ return riscv_iommu_hpm_readq(hpm, RISCV_IOMMU_REG_IOHPMCYCLES) &
+ RISCV_IOMMU_IOHPMCYCLES_COUNTER;
+}
+
+static inline void riscv_iommu_hpm_counter_set_value(struct riscv_iommu_hpm *hpm,
+ u32 idx, u64 value)
+{
+ riscv_iommu_hpm_writeq(hpm, RISCV_IOMMU_REG_IOHPMCTR(idx), value);
+}
+
+static inline u64 riscv_iommu_hpm_counter_get_value(struct riscv_iommu_hpm *hpm,
+ u32 idx)
+{
+ return riscv_iommu_hpm_readq(hpm, RISCV_IOMMU_REG_IOHPMCTR(idx));
+}
+
+static inline void riscv_iommu_hpm_cycles_enable(struct riscv_iommu_hpm *hpm)
+{
+ u32 val = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTINH);
+
+ val &= ~RISCV_IOMMU_IOCOUNTINH_CY;
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH, val);
+}
+
+static inline void riscv_iommu_hpm_cycles_disable(struct riscv_iommu_hpm *hpm)
+{
+ u32 val = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTINH);
+
+ val |= RISCV_IOMMU_IOCOUNTINH_CY;
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH, val);
+}
+
+static inline void riscv_iommu_hpm_counter_enable(struct riscv_iommu_hpm *hpm,
+ u32 idx)
+{
+ u32 val = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTINH);
+
+ val &= ~BIT(idx + 1);
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH, val);
+}
+
+static inline void riscv_iommu_hpm_counter_disable(struct riscv_iommu_hpm *hpm,
+ u32 idx)
+{
+ u32 val = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTINH);
+
+ val |= BIT(idx + 1);
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH, val);
+}
+
+static inline void riscv_iommu_hpm_cycles_clear_ovf(struct riscv_iommu_hpm *hpm)
+{
+ u64 val = riscv_iommu_hpm_readq(hpm, RISCV_IOMMU_REG_IOHPMCYCLES);
+
+ val &= ~RISCV_IOMMU_IOHPMCYCLES_OF;
+ riscv_iommu_hpm_writeq(hpm, RISCV_IOMMU_REG_IOHPMCYCLES, val);
+}
+
+static inline void riscv_iommu_hpm_counter_clear_ovf(struct riscv_iommu_hpm *hpm,
+ u32 idx)
+{
+ u64 val = riscv_iommu_hpm_readq(hpm, RISCV_IOMMU_REG_IOHPMEVT(idx));
+
+ val &= ~RISCV_IOMMU_IOHPMEVT_OF;
+ riscv_iommu_hpm_writeq(hpm, RISCV_IOMMU_REG_IOHPMEVT(idx), val);
+}
+
+static inline void riscv_iommu_hpm_interrupt_clear(struct riscv_iommu_hpm *hpm)
+{
+ riscv_iommu_clear_pmip(hpm->subdev);
+}
+
+/**
+ * riscv_iommu_hpm_event_update() - Update and return RISC-V IOMMU HPM
+ * event counters
+ *
+ * @event: IOMMU performance event
+ *
+ * This function can be used to implement the .read() interface of pmu.
+ */
+static void riscv_iommu_hpm_event_update(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ u64 delta, prev, now;
+ u32 idx = hwc->idx;
+
+ do {
+ prev = local64_read(&hwc->prev_count);
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ now = riscv_iommu_hpm_cycles_get_value(hpm);
+ else
+ now = riscv_iommu_hpm_counter_get_value(hpm, idx);
+ } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
+
+ delta = now - prev;
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ delta &= RISCV_IOMMU_IOHPMCYCLES_COUNTER;
+ else
+ delta &= RISCV_IOMMU_IOHPMEVENT_COUNTER;
+
+ local64_add(delta, &event->count);
+}
+
+static void riscv_iommu_hpm_set_period(struct riscv_iommu_hpm *hpm,
+ struct hw_perf_event *hwc)
+{
+ u32 idx = hwc->idx;
+ u64 new, max_period;
+
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ max_period = RISCV_IOMMU_IOHPMCYCLES_COUNTER;
+ else
+ max_period = RISCV_IOMMU_IOHPMEVENT_COUNTER;
+
+ /* Start at half the counter range */
+ new = max_period >> 1;
+
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ riscv_iommu_hpm_cycles_set_value(hpm, new);
+ else
+ riscv_iommu_hpm_counter_set_value(hpm, idx, new);
+
+ local64_set(&hwc->prev_count, new);
+}
+
+/**
+ * riscv_iommu_hpm_event_start() - Start RISC-V IOMMU HPM event
+ *
+ * @event: IOMMU performance event
+ * @flags: Performance event flags
+ *
+ * This function can be used to implement the .start() interface of pmu.
+ */
+static void riscv_iommu_hpm_event_start(struct perf_event *event, int flags)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ u32 idx = hwc->idx;
+
+ hwc->state = 0;
+ riscv_iommu_hpm_set_period(hpm, hwc);
+
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ riscv_iommu_hpm_cycles_enable(hpm);
+ else
+ riscv_iommu_hpm_counter_enable(hpm, idx);
+}
+
+/**
+ * riscv_iommu_hpm_event_stop() - Stop RISC-V IOMMU HPM event
+ *
+ * @event: IOMMU performance event
+ * @flags: Performance event flags
+ *
+ * This function can be used to implement the .stop() interface of pmu.
+ */
+static void riscv_iommu_hpm_event_stop(struct perf_event *event, int flags)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ u32 idx = hwc->idx;
+
+ if (hwc->state & PERF_HES_STOPPED)
+ return;
+
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ riscv_iommu_hpm_cycles_disable(hpm);
+ else
+ riscv_iommu_hpm_counter_disable(hpm, idx);
+
+ if (flags & PERF_EF_UPDATE)
+ riscv_iommu_hpm_event_update(event);
+ hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+}
+
+static void riscv_iommu_hpm_set_event_filter(struct perf_event *event, int idx,
+ u32 pid_pscid, u32 did_gscid,
+ u32 pv_pscv,
+ u32 dv_gscv, u32 idt, u32 dmask)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ u64 event_cfg;
+
+ /* Start with event ID */
+ event_cfg = get_event(event);
+ /* Set ID fields - values of 0 are valid */
+ event_cfg |= FIELD_PREP(RISCV_IOMMU_IOHPMEVT_PID_PSCID,
+ pid_pscid & 0xFFFFF);
+ event_cfg |= FIELD_PREP(RISCV_IOMMU_IOHPMEVT_DID_GSCID,
+ did_gscid & 0xFFFFFF);
+ /* Set control flags - 0 means disabled, 1 means enabled */
+ if (pv_pscv)
+ event_cfg |= RISCV_IOMMU_IOHPMEVT_PV_PSCV;
+ if (dv_gscv)
+ event_cfg |= RISCV_IOMMU_IOHPMEVT_DV_GSCV;
+ if (idt)
+ event_cfg |= RISCV_IOMMU_IOHPMEVT_IDT;
+ if (dmask)
+ event_cfg |= RISCV_IOMMU_IOHPMEVT_DMASK;
+
+ /* Write to the specific event register for this counter */
+ riscv_iommu_hpm_writeq(hpm,
+ RISCV_IOMMU_REG_IOHPMEVT(idx), event_cfg);
+}
+
+static void riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *hpm,
+ struct perf_event *event, int idx)
+{
+ u32 pid_pscid, did_gscid, pv_pscv, dv_gscv, idt, dmask;
+
+ pid_pscid = get_filter_pid_pscid(event);
+ did_gscid = get_filter_did_gscid(event);
+ pv_pscv = get_filter_pv_pscv(event);
+ dv_gscv = get_filter_dv_gscv(event);
+ idt = get_filter_idt(event);
+ dmask = get_filter_dmask(event);
+
+ riscv_iommu_hpm_set_event_filter(event, idx, pid_pscid, did_gscid,
+ pv_pscv, dv_gscv, idt, dmask);
+}
+
+static int riscv_iommu_hpm_get_event_idx(struct riscv_iommu_hpm *hpm,
+ struct perf_event *event)
+{
+ int idx;
+ unsigned int num_ctrs = hpm->num_counters;
+ u16 event_id = get_event(event);
+
+ /* Handle cycles event specially */
+ if (event_id == RISCV_IOMMU_HPMEVENT_CYCLES) {
+ /* Check if cycles counter is already in use */
+ if (test_and_set_bit(RISCV_IOMMU_HPMCOUNTER_CYCLES,
+ hpm->used_counters)) {
+ dev_dbg(hpm->pmu.dev,
+ "Cycles counter already in use\n");
+ return -EAGAIN;
+ }
+ return RISCV_IOMMU_HPMCOUNTER_CYCLES;
+ }
+
+ idx = find_first_zero_bit(hpm->used_counters, num_ctrs - 1);
+ if (idx == num_ctrs - 1) {
+ dev_dbg(hpm->pmu.dev, "All counters already in use\n");
+ return -EAGAIN;
+ }
+
+ riscv_iommu_hpm_apply_event_filter(hpm, event, idx);
+ set_bit(idx, hpm->used_counters);
+
+ return idx;
+}
+
+/**
+ * riscv_iommu_hpm_event_add() - Add a RISC-V IOMMU HPM event
+ *
+ * @event - IOMMU performance event
+ * @flags - Performance event flags
+ *
+ * This function can be used to implement the .add() interface of pmu.
+ */
+static int riscv_iommu_hpm_event_add(struct perf_event *event, int flags)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+
+ idx = riscv_iommu_hpm_get_event_idx(hpm, event);
+ if (idx < 0)
+ return idx;
+
+ hwc->idx = idx;
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES)
+ hpm->events[0] = event;
+ else
+ hpm->events[idx + 1] = event;
+
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+ if (flags & PERF_EF_START)
+ riscv_iommu_hpm_event_start(event, flags);
+ perf_event_update_userpage(event);
+
+ return 0;
+}
+
+/**
+ * riscv_iommu_hpm_event_del() - Delete a RISC-V IOMMU HPM event
+ *
+ * @event: IOMMU performance event
+ * @flags: Performance event flags
+ *
+ * This function can be used to implement the .del() interface of pmu.
+ */
+static void riscv_iommu_hpm_event_del(struct perf_event *event, int flags)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ u32 idx = hwc->idx;
+
+ riscv_iommu_hpm_event_stop(event, flags | PERF_EF_UPDATE);
+
+ /* Clear the used counter bit and event array entry */
+ if (idx == RISCV_IOMMU_HPMCOUNTER_CYCLES) {
+ clear_bit(RISCV_IOMMU_HPMCOUNTER_CYCLES,
+ hpm->used_counters);
+ hpm->events[0] = NULL;
+ } else {
+ clear_bit(idx, hpm->used_counters);
+ hpm->events[idx + 1] = NULL;
+ }
+
+ perf_event_update_userpage(event);
+}
+
+/**
+ * riscv_iommu_hpm_event_init() - Initialize HPM event attributes
+ *
+ * @event: IOMMU performance event
+ *
+ * This function can be used to implement the .event_init() interface of
+ * pmu.
+ */
+static int riscv_iommu_hpm_event_init(struct perf_event *event)
+{
+ struct riscv_iommu_hpm *hpm = to_iommu_hpm(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ struct perf_event *sibling;
+ int group_num_events = 1;
+ u16 event_id;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+ if (hwc->sample_period)
+ return -EINVAL;
+ if (event->cpu < 0)
+ return -EINVAL;
+
+ event_id = get_event(event);
+ if (event_id >= RISCV_IOMMU_HPMEVENT_MAX ||
+ !test_bit(event_id, hpm->supported_events)) {
+ dev_dbg(hpm->pmu.dev, "Invalid event %d for this HPM\n",
+ event_id);
+ return -EINVAL;
+ }
+
+ if (!is_software_event(event->group_leader)) {
+ if (++group_num_events > hpm->num_counters)
+ return -EINVAL;
+ }
+
+ for_each_sibling_event(sibling, event->group_leader) {
+ if (is_software_event(sibling))
+ continue;
+ if (++group_num_events > hpm->num_counters)
+ return -EINVAL;
+ }
+
+ event->cpu = hpm->on_cpu;
+ hwc->idx = -1;
+
+ return 0;
+}
+
+static ssize_t riscv_iommu_hpm_cpumask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct riscv_iommu_hpm *hpm = dev_get_drvdata(dev);
+
+ return cpumap_print_to_pagebuf(true, buf, cpumask_of(hpm->on_cpu));
+}
+
+static struct device_attribute riscv_iommu_hpm_cpumask_attr =
+ __ATTR(cpumask, 0444, riscv_iommu_hpm_cpumask_show, NULL);
+
+static struct attribute *riscv_iommu_hpm_cpumask_attrs[] = {
+ &riscv_iommu_hpm_cpumask_attr.attr,
+ NULL
+};
+
+static const struct attribute_group riscv_iommu_hpm_cpumask_group = {
+ .attrs = riscv_iommu_hpm_cpumask_attrs,
+};
+
+#define IOMMU_HPM_EVENT_ATTR(name, config) \
+ PMU_EVENT_ATTR_ID(name, riscv_iommu_hpm_event_show, config)
+
+static ssize_t riscv_iommu_hpm_event_show(struct device *dev,
+ struct device_attribute *attr,
+ char *page)
+{
+ struct perf_pmu_events_attr *hpm_attr;
+
+ hpm_attr = container_of(attr, struct perf_pmu_events_attr, attr);
+ return sysfs_emit(page, "event=0x%02llx\n", hpm_attr->id);
+}
+
+static struct attribute *riscv_iommu_hpm_events[] = {
+ IOMMU_HPM_EVENT_ATTR(cycles, RISCV_IOMMU_HPMEVENT_CYCLES),
+ IOMMU_HPM_EVENT_ATTR(untrans_rq, RISCV_IOMMU_HPMEVENT_URQ),
+ IOMMU_HPM_EVENT_ATTR(trans_rq, RISCV_IOMMU_HPMEVENT_TRQ),
+ IOMMU_HPM_EVENT_ATTR(ats_rq, RISCV_IOMMU_HPMEVENT_ATS_RQ),
+ IOMMU_HPM_EVENT_ATTR(tlb_mis, RISCV_IOMMU_HPMEVENT_TLB_MISS),
+ IOMMU_HPM_EVENT_ATTR(dd_walk, RISCV_IOMMU_HPMEVENT_DD_WALK),
+ IOMMU_HPM_EVENT_ATTR(pd_walk, RISCV_IOMMU_HPMEVENT_PD_WALK),
+ IOMMU_HPM_EVENT_ATTR(s_walk, RISCV_IOMMU_HPMEVENT_S_VS_WALKS),
+ IOMMU_HPM_EVENT_ATTR(g_walk, RISCV_IOMMU_HPMEVENT_G_WALKS),
+ NULL
+};
+
+static umode_t riscv_iommu_hpm_event_is_visible(struct kobject *kobj,
+ struct attribute *attr,
+ int unused)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct riscv_iommu_hpm *hpm = dev_get_drvdata(dev);
+ struct perf_pmu_events_attr *hpm_attr;
+
+ hpm_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr);
+ if (test_bit(hpm_attr->id, hpm->supported_events))
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group riscv_iommu_hpm_events_group = {
+ .name = "events",
+ .attrs = riscv_iommu_hpm_events,
+ .is_visible = riscv_iommu_hpm_event_is_visible,
+};
+
+PMU_FORMAT_ATTR(event, "config:0-14");
+PMU_FORMAT_ATTR(filter_pid_pscid, "config1:16-35");
+PMU_FORMAT_ATTR(filter_did_gscid, "config1:36-59");
+PMU_FORMAT_ATTR(filter_pv_pscv, "config1:60");
+PMU_FORMAT_ATTR(filter_dv_gscv, "config1:61");
+PMU_FORMAT_ATTR(filter_idt, "config1:62");
+PMU_FORMAT_ATTR(filter_dmask, "config1:15");
+
+static struct attribute *riscv_iommu_hpm_formats[] = {
+ &format_attr_event.attr,
+ &format_attr_filter_pid_pscid.attr,
+ &format_attr_filter_did_gscid.attr,
+ &format_attr_filter_pv_pscv.attr,
+ &format_attr_filter_dv_gscv.attr,
+ &format_attr_filter_idt.attr,
+ &format_attr_filter_dmask.attr,
+ NULL
+};
+
+static const struct attribute_group riscv_iommu_hpm_format_group = {
+ .name = "format",
+ .attrs = riscv_iommu_hpm_formats,
+};
+
+static const struct attribute_group *riscv_iommu_hpm_attr_grps[] = {
+ &riscv_iommu_hpm_cpumask_group,
+ &riscv_iommu_hpm_events_group,
+ &riscv_iommu_hpm_format_group,
+ NULL
+};
+
+static irqreturn_t riscv_iommu_hpm_handle_irq(int irq_num, void *data)
+{
+ struct riscv_iommu_hpm *hpm = data;
+ struct perf_event *event;
+ int idx;
+ u32 ovf;
+ DECLARE_BITMAP(ovs, 32);
+
+ if (!riscv_iommu_pmip_status(hpm->subdev))
+ return IRQ_NONE;
+
+ ovf = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTOVF);
+ if (!ovf)
+ return IRQ_HANDLED;
+
+ /* Handle cycles counter overflow (always stored at index 0) */
+ if (ovf & RISCV_IOMMU_IOCOUNTOVF_CY) {
+ event = hpm->events[0];
+ if (event && event->hw.idx == RISCV_IOMMU_HPMCOUNTER_CYCLES) {
+ riscv_iommu_hpm_cycles_clear_ovf(hpm);
+ riscv_iommu_hpm_event_update(event);
+ riscv_iommu_hpm_set_period(hpm, &event->hw);
+ }
+ }
+
+ /*
+ * Handle regular HPM counter overflows.
+ * IOCOUNTOVF bit mapping:
+ * bit 0: cycles (already handled above)
+ * bit 1: counter 0 -> events[1]
+ * bit 2: counter 1 -> events[2]
+ * ...
+ * bit N: counter N-1 -> events[N]
+ * We need to check bits [1..num_counters] and skip bit 0.
+ */
+ bitmap_from_u64(ovs, ovf);
+ for_each_set_bit(idx, ovs, hpm->num_counters) {
+ /* Skip bit 0 (cycles counter, already handled) */
+ if (idx == 0)
+ continue;
+
+ /* IOCOUNTOVF bit N corresponds to counter N-1, stored in
+ * events[N]
+ */
+ event = hpm->events[idx];
+ if (WARN_ON_ONCE(!event))
+ continue;
+
+ dev_dbg(&hpm->subdev->auxdev.dev, "counter overflow: hw_idx=%d, counter=%d\n",
+ idx, idx - 1);
+ riscv_iommu_hpm_counter_clear_ovf(hpm, idx - 1);
+ riscv_iommu_hpm_event_update(event);
+ riscv_iommu_hpm_set_period(hpm, &event->hw);
+ }
+
+ riscv_iommu_hpm_interrupt_clear(hpm);
+
+ return IRQ_HANDLED;
+}
+
+static int riscv_iommu_hpm_offline_cpu(unsigned int cpu,
+ struct hlist_node *node)
+{
+ struct riscv_iommu_hpm *hpm;
+ unsigned int target;
+
+ hpm = hlist_entry_safe(node, struct riscv_iommu_hpm, node);
+ if (cpu != hpm->on_cpu)
+ return 0;
+
+ if (!hpm->irq)
+ return 0;
+
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+
+ perf_pmu_migrate_context(&hpm->pmu, cpu, target);
+ hpm->on_cpu = target;
+ if (hpm->irq > 0)
+ WARN_ON(irq_set_affinity(hpm->irq, cpumask_of(target)));
+
+ return 0;
+}
+
+/* Protected by riscv_iommu_hpm_lock. This block is self-contained; any
+ * modifications to cpuhp handling must preserve mutex serialization around
+ * cpuhp_state_num and the cpuhp_state_*() calls.
+ */
+static int riscv_iommu_hpm_cpuhp_add(struct hlist_node *node)
+{
+ int err = 0;
+
+ mutex_lock(&riscv_iommu_hpm_lock);
+ if (cpuhp_state_num < 0) {
+ cpuhp_state_num = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
+ "perf/riscv/iommu:online",
+ NULL,
+ riscv_iommu_hpm_offline_cpu);
+ if (cpuhp_state_num < 0) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ }
+ err = cpuhp_state_add_instance_nocalls(cpuhp_state_num, node);
+unlock:
+ mutex_unlock(&riscv_iommu_hpm_lock);
+ return err;
+}
+
+static void riscv_iommu_hpm_cpuhp_remove(struct hlist_node *node)
+{
+ mutex_lock(&riscv_iommu_hpm_lock);
+ if (cpuhp_state_num >= 0)
+ cpuhp_state_remove_instance_nocalls(cpuhp_state_num, node);
+ mutex_unlock(&riscv_iommu_hpm_lock);
+}
+
+static void riscv_iommu_hpm_reset(struct riscv_iommu_hpm *hpm)
+{
+ u64 counter_present_mask = (1ULL << hpm->num_counters) - 1;
+
+ /* Disable all counters */
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH,
+ counter_present_mask);
+ /* Clear interrupt pending status */
+ riscv_iommu_hpm_interrupt_clear(hpm);
+}
+
+static void riscv_iommu_hpm_set_standard_events(struct riscv_iommu_hpm *hpm)
+{
+ /* Cycles counter is always supported */
+ set_bit(RISCV_IOMMU_HPMEVENT_CYCLES, hpm->supported_events);
+
+ /* Standard RISC-V IOMMU HPM events */
+ set_bit(RISCV_IOMMU_HPMEVENT_URQ, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_TRQ, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_ATS_RQ, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_TLB_MISS, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_DD_WALK, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_PD_WALK, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_S_VS_WALKS, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_G_WALKS, hpm->supported_events);
+}
+
+static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct riscv_iommu_subdev *subdev;
+ struct riscv_iommu_hpm *hpm;
+ struct device *dev = &auxdev->dev;
+ struct riscv_iommu_hpm_info *info;
+ const char *hpm_name;
+ u32 val;
+ int err;
+
+ subdev = riscv_iommu_get_subdev(dev);
+ if (!subdev) {
+ dev_err(dev, "No auxiliary device data\n");
+ return -ENODEV;
+ }
+
+ info = subdev->info;
+ if (!info || info->irq <= 0) {
+ dev_err(dev, "HPM: No IRQ available\n");
+ return -EINVAL;
+ }
+
+ hpm = devm_kzalloc(dev, sizeof(*hpm), GFP_KERNEL);
+ if (!hpm)
+ return -ENOMEM;
+
+ hpm->subdev = subdev;
+ hpm->base = subdev->base;
+ hpm->on_cpu = raw_smp_processor_id();
+ hpm->irq = info->irq;
+
+ bitmap_zero(hpm->used_counters, RISCV_IOMMU_HPMCOUNTER_MAX);
+ bitmap_zero(hpm->supported_events, RISCV_IOMMU_HPMEVENT_MAX);
+
+ riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IOCOUNTINH, 0xFFFFFFFF);
+ val = riscv_iommu_hpm_readl(hpm, RISCV_IOMMU_REG_IOCOUNTINH);
+ hpm->num_counters = hweight32(val & RISCV_IOMMU_IOCOUNTINH_HPM);
+ if (!hpm->num_counters)
+ return -ENODEV;
+
+ riscv_iommu_hpm_reset(hpm);
+ riscv_iommu_hpm_set_standard_events(hpm);
+
+ hpm_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+ if (!hpm_name)
+ return -ENOMEM;
+
+ err = devm_request_threaded_irq(dev, hpm->irq, NULL,
+ riscv_iommu_hpm_handle_irq,
+ IRQF_SHARED | IRQF_ONESHOT,
+ hpm_name, hpm);
+ if (err)
+ return err;
+ WARN_ON(irq_set_affinity(hpm->irq, cpumask_of(hpm->on_cpu)));
+
+ hpm->pmu = (struct pmu) {
+ .name = hpm_name,
+ .module = THIS_MODULE,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = riscv_iommu_hpm_event_init,
+ .add = riscv_iommu_hpm_event_add,
+ .del = riscv_iommu_hpm_event_del,
+ .start = riscv_iommu_hpm_event_start,
+ .stop = riscv_iommu_hpm_event_stop,
+ .read = riscv_iommu_hpm_event_update,
+ .attr_groups = riscv_iommu_hpm_attr_grps,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
+ };
+
+ err = perf_pmu_register(&hpm->pmu, hpm_name, -1);
+ if (err) {
+ dev_err(dev, "HPM: Failed to register PMU\n");
+ return err;
+ }
+
+ dev_set_drvdata(dev, hpm);
+
+ err = riscv_iommu_hpm_cpuhp_add(&hpm->node);
+ if (err) {
+ perf_pmu_unregister(&hpm->pmu);
+ return err;
+ }
+
+ auxiliary_set_drvdata(auxdev, hpm);
+
+ dev_info(dev, "HPM: Registered %s (%d counters, IRQ %d)\n",
+ hpm_name, hpm->num_counters, hpm->irq);
+
+ return 0;
+}
+
+static void riscv_iommu_hpm_remove(struct auxiliary_device *auxdev)
+{
+ struct riscv_iommu_hpm *hpm = auxiliary_get_drvdata(auxdev);
+
+ riscv_iommu_hpm_cpuhp_remove(&hpm->node);
+ perf_pmu_unregister(&hpm->pmu);
+}
+
+static const struct auxiliary_device_id riscv_iommu_hpm_ids[] = {
+ { .name = "iommu.riscv_iommu_hpm" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, riscv_iommu_hpm_ids);
+
+static struct auxiliary_driver riscv_iommu_hpm_driver = {
+ .name = "riscv_iommu_hpm",
+ .id_table = riscv_iommu_hpm_ids,
+ .probe = riscv_iommu_hpm_probe,
+ .remove = riscv_iommu_hpm_remove,
+};
+
+module_auxiliary_driver(riscv_iommu_hpm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RISC-V IOMMU Hardware Performance Monitor");
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
` (2 preceding siblings ...)
2026-02-28 14:44 ` [PATCH v5 3/8] iommu/riscv: Add HPM support for performance monitoring Lv Zheng
@ 2026-02-28 14:44 ` Lv Zheng
2026-02-28 21:58 ` Conor Dooley
2026-02-28 14:44 ` [PATCH v5 5/8] spacemit/t100: Add global filter awareness for RISC-V IOMMU HPM Lv Zheng
` (3 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:44 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
Adds device tree bindings for SpacemiT T100 specific features by
introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
each of which exposes pmiv interrupt.
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
---
.../bindings/iommu/riscv,iommu.yaml | 58 ++++++++++++++++++-
1 file changed, 56 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml b/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
index d4838c3b3741..6b775ebf7f42 100644
--- a/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
@@ -30,8 +30,19 @@ properties:
# Since PCI provides built-in identification methods, compatible is not
# actually required. For non-PCIe hardware implementations 'riscv,iommu'
# should be specified along with 'reg' property providing MMIO location.
+ # SpacemiT T100 (distributed IOMMU with split IOATCs placed in adjacent
+ # to the DMA masters) can be seen in SpacemiT SoCs. It is integerated in
+ # V100 (server SoC) with coherent page table walk supported and in K3
+ # (client SoC) without coherent page table walk supported.
compatible:
oneOf:
+ - description: SpacemiT distributed IOMMUs
+ items:
+ - enum:
+ - spacemit,k3-t100
+ - spacemit,v100-t100
+ - const: spacemit,t100
+ - const: riscv,iommu
- items:
- enum:
- qemu,riscv-iommu
@@ -57,11 +68,21 @@ properties:
interrupts:
minItems: 1
- maxItems: 4
+ maxItems: 68
description:
Wired interrupt vectors available for RISC-V IOMMU to notify the
RISC-V HARTS. The cause to interrupt vector is software defined
- using IVEC IOMMU register.
+ using ICVEC IOMMU register. For WSI only mode, the number of the
+ interrupt vectors should be 1 while for MSI possible mode, the
+ maximum of the interrupt vectors should be 4 with the cause indexed
+ as "CIV=0, FIV=1, PIV=2, PMIV=3".
+ SpacemiT distributed IOMMU includes additional interrupts for
+ IOATCs. Each IOATC exposes PMIV wired vector as standalone
+ interrupt and the maximum number of IOATCs can be up to 64. Thus for
+ WSI only mode, the maximum number of the interrupt vectors should be
+ 65 while for MSI possible mode, the maximum number of the interrupt
+ vectors should be 68 with the cause indexed as "IOATS CIV=0,
+ IOATS FIV=1, IOATS PIV=2, IOATS PMIV=3, IOATC0..n PMIV=4..4+n".
msi-parent: true
@@ -75,6 +96,18 @@ required:
additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ const: spacemit,t100
+ then:
+ properties:
+ interrupts:
+ maxItems: 4
+
examples:
- |+
/* Example 1 (IOMMU device with wired interrupts) */
@@ -145,3 +178,24 @@ examples:
};
};
};
+
+ - |+
+ /* Example 5 (SpacemiT distributed IOMMU) */
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ iommu4: iommu@1bccd000 {
+ compatible = "spacemit,k3-t100", "spacemit,t100", "riscv,iommu";
+ reg = <0x1bccd000 0x1000>;
+ interrupts = <58 IRQ_TYPE_LEVEL_HIGH>,
+ <62 IRQ_TYPE_LEVEL_HIGH>, <63 IRQ_TYPE_LEVEL_HIGH>,
+ <62 IRQ_TYPE_LEVEL_HIGH>, <63 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&saplic>;
+ #iommu-cells = <0x01>;
+ };
+
+ /* Device with four IOMMU device IDs */
+ master2 {
+ #iommu-cells = <1>;
+ iommus = <&iommu4 0xc0010>, <&iommu4 0xc0011>,
+ <&iommu4 0xc0012>, <&iommu4 0xc0013>;
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
2026-02-28 14:44 ` [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features Lv Zheng
@ 2026-02-28 21:58 ` Conor Dooley
2026-03-01 3:48 ` Lv Zheng
0 siblings, 1 reply; 13+ messages in thread
From: Conor Dooley @ 2026-02-28 21:58 UTC (permalink / raw)
To: Lv Zheng
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan, Jingyu Li, Zong Li, Yaxing Guo,
Zhijian Chen, iommu, linux-perf-users, linux-riscv, spacemit,
devicetree, linux-kernel
On Sat, 28 Feb 2026 22:44:49 +0800, Lv Zheng <lv.zheng@linux.spacemit.com> wrote:
> Adds device tree bindings for SpacemiT T100 specific features by
> introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
> each of which exposes pmiv interrupt.
>
> Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> Signed-off-by: Jingyu Li <joey.li@spacemit.com>
Patch content here is fine, but the signoff chain on this patch is not
right. What did Jingyu do? You're author and submitter so either Jingyu
did nothing and should be removed, or is a co-developer which requires
attribution via the co-developed-by tag. If the latter, their signoff
should be before yours (the submitter is always last).
pw-bot: changes-requested
Cheers,
Conor.
--
Conor Dooley <conor.dooley@microchip.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
2026-02-28 21:58 ` Conor Dooley
@ 2026-03-01 3:48 ` Lv Zheng
2026-03-01 14:01 ` Conor Dooley
0 siblings, 1 reply; 13+ messages in thread
From: Lv Zheng @ 2026-03-01 3:48 UTC (permalink / raw)
To: Conor Dooley
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan, Jingyu Li, Zong Li, Yaxing Guo, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
On 3/1/2026 5:58 AM, Conor Dooley wrote:
> On Sat, 28 Feb 2026 22:44:49 +0800, Lv Zheng <lv.zheng@linux.spacemit.com> wrote:
>> Adds device tree bindings for SpacemiT T100 specific features by
>> introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
>> each of which exposes pmiv interrupt.
>>
>> Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
>> Signed-off-by: Jingyu Li <joey.li@spacemit.com>
>
> Patch content here is fine, but the signoff chain on this patch is not
> right. What did Jingyu do? You're author and submitter so either Jingyu
> did nothing and should be removed, or is a co-developer which requires
> attribution via the co-developed-by tag. If the latter, their signoff
> should be before yours (the submitter is always last).
>
> pw-bot: changes-requested
She is responsible for the common HPM part, and I just gave my comments
to help to improve the quality of that implementation.
I was just focusing on enabling and maintaining the T100 specific features.
Our patches will go each other's local repository, leaving such tracks.
That means the patch from me is Reviewed-and-tested-by her.
And I'm responsible for sending the whole patchset for upstreaming.
Cheers,
Lv
>
> Cheers,
> Conor.
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
2026-03-01 3:48 ` Lv Zheng
@ 2026-03-01 14:01 ` Conor Dooley
2026-03-02 1:41 ` Lv Zheng
0 siblings, 1 reply; 13+ messages in thread
From: Conor Dooley @ 2026-03-01 14:01 UTC (permalink / raw)
To: Lv Zheng
Cc: Conor Dooley, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Robin Murphy, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark, Yixun Lan, Jingyu Li,
Zong Li, Yaxing Guo, iommu, linux-perf-users, linux-riscv,
spacemit, devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2206 bytes --]
On Sun, Mar 01, 2026 at 11:48:37AM +0800, Lv Zheng wrote:
> On 3/1/2026 5:58 AM, Conor Dooley wrote:
> > On Sat, 28 Feb 2026 22:44:49 +0800, Lv Zheng <lv.zheng@linux.spacemit.com> wrote:
> > > Adds device tree bindings for SpacemiT T100 specific features by
> > > introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
> > > each of which exposes pmiv interrupt.
> > >
> > > Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> > > Signed-off-by: Jingyu Li <joey.li@spacemit.com>
> >
> > Patch content here is fine, but the signoff chain on this patch is not
> > right. What did Jingyu do? You're author and submitter so either Jingyu
> > did nothing and should be removed, or is a co-developer which requires
> > attribution via the co-developed-by tag. If the latter, their signoff
> > should be before yours (the submitter is always last).
> >
> > pw-bot: changes-requested
>
> She is responsible for the common HPM part, and I just gave my comments to
> help to improve the quality of that implementation.
> I was just focusing on enabling and maintaining the T100 specific features.
>
> Our patches will go each other's local repository, leaving such tracks. That
> means the patch from me is Reviewed-and-tested-by her.
> And I'm responsible for sending the whole patchset for upstreaming.
Then you need to either fix your email setup (because it's not inserting
From: Jingyu Li <joey.li@spacemit.com>) so that she is marked as the
author or actually set her to the author in git if you haven't.
Currently it looks like this when applied:
commit 872978c875731be43e84bec38798cb8ecd42b7a0
Author: Lv Zheng <lv.zheng@linux.spacemit.com>
Date: Sat Feb 28 22:44:49 2026 +0800
dt-bindings: iommu: Add spacemit/t100 features
Adds device tree bindings for SpacemiT T100 specific features by
introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
each of which exposes pmiv interrupt.
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
In both cases, you need to change the order of signoffs so that yours is
last as the submitter.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
2026-03-01 14:01 ` Conor Dooley
@ 2026-03-02 1:41 ` Lv Zheng
2026-03-02 17:53 ` Conor Dooley
0 siblings, 1 reply; 13+ messages in thread
From: Lv Zheng @ 2026-03-02 1:41 UTC (permalink / raw)
To: Conor Dooley
Cc: Conor Dooley, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Robin Murphy, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark, Yixun Lan, Jingyu Li,
Zong Li, Yaxing Guo, iommu, linux-perf-users, linux-riscv,
spacemit, devicetree, linux-kernel
On 3/1/2026 10:01 PM, Conor Dooley wrote:
> On Sun, Mar 01, 2026 at 11:48:37AM +0800, Lv Zheng wrote:
>> On 3/1/2026 5:58 AM, Conor Dooley wrote:
>>> On Sat, 28 Feb 2026 22:44:49 +0800, Lv Zheng <lv.zheng@linux.spacemit.com> wrote:
>>>> Adds device tree bindings for SpacemiT T100 specific features by
>>>> introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
>>>> each of which exposes pmiv interrupt.
>>>>
>>>> Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
>>>> Signed-off-by: Jingyu Li <joey.li@spacemit.com>
>>>
>>> Patch content here is fine, but the signoff chain on this patch is not
>>> right. What did Jingyu do? You're author and submitter so either Jingyu
>>> did nothing and should be removed, or is a co-developer which requires
>>> attribution via the co-developed-by tag. If the latter, their signoff
>>> should be before yours (the submitter is always last).
>>>
>>> pw-bot: changes-requested
>>
>> She is responsible for the common HPM part, and I just gave my comments to
>> help to improve the quality of that implementation.
>> I was just focusing on enabling and maintaining the T100 specific features.
>>
>> Our patches will go each other's local repository, leaving such tracks. That
>> means the patch from me is Reviewed-and-tested-by her.
>> And I'm responsible for sending the whole patchset for upstreaming.
>
> Then you need to either fix your email setup (because it's not inserting
> From: Jingyu Li <joey.li@spacemit.com>) so that she is marked as the
> author or actually set her to the author in git if you haven't.
> Currently it looks like this when applied:
> commit 872978c875731be43e84bec38798cb8ecd42b7a0
> Author: Lv Zheng <lv.zheng@linux.spacemit.com>
> Date: Sat Feb 28 22:44:49 2026 +0800
>
> dt-bindings: iommu: Add spacemit/t100 features
>
> Adds device tree bindings for SpacemiT T100 specific features by
> introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
> each of which exposes pmiv interrupt.
>
> Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> Signed-off-by: Jingyu Li <joey.li@spacemit.com>
>
> In both cases, you need to change the order of signoffs so that yours is
> last as the submitter.
OK.
BTW, since this version of this patch has been worked out all due your
constructive feedback and suggestions. Now that it has functionally
converged to what the community requested, can I add "Reviewed-by: Conor
Dooley <conor@kernel.org>" for its next version?
Best regards,
Lv
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features
2026-03-02 1:41 ` Lv Zheng
@ 2026-03-02 17:53 ` Conor Dooley
0 siblings, 0 replies; 13+ messages in thread
From: Conor Dooley @ 2026-03-02 17:53 UTC (permalink / raw)
To: Lv Zheng
Cc: Conor Dooley, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Robin Murphy, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark, Yixun Lan, Jingyu Li,
Zong Li, Yaxing Guo, iommu, linux-perf-users, linux-riscv,
spacemit, devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 3198 bytes --]
On Mon, Mar 02, 2026 at 09:41:55AM +0800, Lv Zheng wrote:
> On 3/1/2026 10:01 PM, Conor Dooley wrote:
> > On Sun, Mar 01, 2026 at 11:48:37AM +0800, Lv Zheng wrote:
> > > On 3/1/2026 5:58 AM, Conor Dooley wrote:
> > > > On Sat, 28 Feb 2026 22:44:49 +0800, Lv Zheng <lv.zheng@linux.spacemit.com> wrote:
> > > > > Adds device tree bindings for SpacemiT T100 specific features by
> > > > > introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
> > > > > each of which exposes pmiv interrupt.
> > > > >
> > > > > Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> > > > > Signed-off-by: Jingyu Li <joey.li@spacemit.com>
> > > >
> > > > Patch content here is fine, but the signoff chain on this patch is not
> > > > right. What did Jingyu do? You're author and submitter so either Jingyu
> > > > did nothing and should be removed, or is a co-developer which requires
> > > > attribution via the co-developed-by tag. If the latter, their signoff
> > > > should be before yours (the submitter is always last).
> > > >
> > > > pw-bot: changes-requested
> > >
> > > She is responsible for the common HPM part, and I just gave my comments to
> > > help to improve the quality of that implementation.
> > > I was just focusing on enabling and maintaining the T100 specific features.
> > >
> > > Our patches will go each other's local repository, leaving such tracks. That
> > > means the patch from me is Reviewed-and-tested-by her.
> > > And I'm responsible for sending the whole patchset for upstreaming.
> >
> > Then you need to either fix your email setup (because it's not inserting
> > From: Jingyu Li <joey.li@spacemit.com>) so that she is marked as the
> > author or actually set her to the author in git if you haven't.
> > Currently it looks like this when applied:
> > commit 872978c875731be43e84bec38798cb8ecd42b7a0
> > Author: Lv Zheng <lv.zheng@linux.spacemit.com>
> > Date: Sat Feb 28 22:44:49 2026 +0800
> >
> > dt-bindings: iommu: Add spacemit/t100 features
> > Adds device tree bindings for SpacemiT T100 specific features by
> > introducing spacemit,t100 compatible. T100 contains distributed IOATCs,
> > each of which exposes pmiv interrupt.
> > Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> > Signed-off-by: Jingyu Li <joey.li@spacemit.com>
> >
> > In both cases, you need to change the order of signoffs so that yours is
> > last as the submitter.
>
> BTW, since this version of this patch has been worked out all due your
> constructive feedback and suggestions. Now that it has functionally
> converged to what the community requested, can I add "Reviewed-by: Conor
> Dooley <conor@kernel.org>" for its next version?
No, as I said the previous time you asked, I will provide one when I am
happy with the patch. I have not yet reached a point with you were I am
comfortable providing tags with the assumption that you will make the
correct changes. There's no need to hassle me for one at this point
anyway, because you have to send a v6 anyway. If v6 is satisfactory to
the maintainers, they'll grab my tag when they apply the series.
Cheers,
Conor.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 5/8] spacemit/t100: Add global filter awareness for RISC-V IOMMU HPM
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
` (3 preceding siblings ...)
2026-02-28 14:44 ` [PATCH v5 4/8] dt-bindings: iommu: Add spacemit/t100 features Lv Zheng
@ 2026-02-28 14:44 ` Lv Zheng
2026-02-28 14:45 ` [PATCH v5 6/8] iommu/riscv: Add SpacemiT T100 IOATC HPM support Lv Zheng
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:44 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
Adds global filter support for RISC-V IOMMU HPM. The global filter can be
seen in SpacemiT T100 which only supports single filter to be applied to
all event counters.
Drivers can program filters in each iohpmevt registers as normal in such a
silicon design, however the underlying hardware filters are wired together
as a global filter applying to all iohpmevt(s). Since the mechanism is
compatible with standard iohpmevt in programming interface, only adds
sanity checks to allow it to be configured with "global" awareness to
inform users a filter incompatiblity.
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
---
drivers/iommu/riscv/iommu.c | 5 ++
drivers/perf/riscv_iommu_hpm.c | 94 ++++++++++++++++++++++++++++++++--
include/linux/riscv_iommu.h | 2 +
3 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 1aa942486e3a..21ff7e50d115 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1717,6 +1717,11 @@ static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
.base = iommu->reg + RISCV_IOMMU_REG_IOCOUNTOVF,
};
+ if (of_device_is_compatible(iommu->dev->of_node, "spacemit,t100")) {
+ hpm_info->global_filter = true;
+ params.name = "spacemit_ioats_hpm";
+ }
+
ret = riscv_iommu_subdev_add(iommu, ¶ms);
if (ret) {
kfree(hpm_info);
diff --git a/drivers/perf/riscv_iommu_hpm.c b/drivers/perf/riscv_iommu_hpm.c
index 4d5ce0f380e7..efa65caef0dc 100644
--- a/drivers/perf/riscv_iommu_hpm.c
+++ b/drivers/perf/riscv_iommu_hpm.c
@@ -87,6 +87,7 @@ struct riscv_iommu_hpm {
void __iomem *base;
unsigned int irq;
unsigned int on_cpu;
+ bool global_filter;
struct hlist_node node;
/*
* Layout of events:
@@ -210,6 +211,33 @@ static inline void riscv_iommu_hpm_interrupt_clear(struct riscv_iommu_hpm *hpm)
riscv_iommu_clear_pmip(hpm->subdev);
}
+static bool riscv_iommu_hpm_check_global_filter(struct perf_event *curr,
+ struct perf_event *new)
+{
+ return get_filter_pid_pscid(curr) == get_filter_pid_pscid(new) &&
+ get_filter_did_gscid(curr) == get_filter_did_gscid(new) &&
+ get_filter_pv_pscv(curr) == get_filter_pv_pscv(new) &&
+ get_filter_dv_gscv(curr) == get_filter_dv_gscv(new) &&
+ get_filter_idt(curr) == get_filter_idt(new) &&
+ get_filter_dmask(curr) == get_filter_dmask(new);
+}
+
+static bool riscv_iommu_hpm_events_compatible(struct perf_event *curr,
+ struct perf_event *new)
+{
+ struct riscv_iommu_hpm *hpm;
+
+ if (new->pmu != curr->pmu)
+ return false;
+
+ hpm = to_iommu_hpm(new->pmu);
+ if (hpm->global_filter &&
+ !riscv_iommu_hpm_check_global_filter(curr, new))
+ return false;
+
+ return true;
+}
+
/**
* riscv_iommu_hpm_event_update() - Update and return RISC-V IOMMU HPM
* event counters
@@ -344,9 +372,10 @@ static void riscv_iommu_hpm_set_event_filter(struct perf_event *event, int idx,
RISCV_IOMMU_REG_IOHPMEVT(idx), event_cfg);
}
-static void riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *hpm,
- struct perf_event *event, int idx)
+static int riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *hpm,
+ struct perf_event *event, int idx)
{
+ unsigned int cur_idx, num_ctrs = hpm->num_counters;
u32 pid_pscid, did_gscid, pv_pscv, dv_gscv, idt, dmask;
pid_pscid = get_filter_pid_pscid(event);
@@ -356,8 +385,32 @@ static void riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *hpm,
idt = get_filter_idt(event);
dmask = get_filter_dmask(event);
+ if (hpm->global_filter) {
+ cur_idx = find_first_bit(hpm->used_counters, num_ctrs - 1);
+ if (cur_idx == num_ctrs - 1) {
+ /* First event, set the global filter at iohpmevt0 */
+ riscv_iommu_hpm_set_event_filter(event, 0, pid_pscid,
+ did_gscid,
+ pv_pscv, dv_gscv, idt, dmask);
+ } else {
+ /* Check if the new event's filter matches the global filter */
+ if (!riscv_iommu_hpm_check_global_filter(hpm->events[cur_idx + 1],
+ event)) {
+ dev_dbg(hpm->pmu.dev,
+ "HPM: Filter incompatible with global filter\n");
+ return -EAGAIN;
+ }
+ /* Program event at this counter; filter is shared by hardware */
+ riscv_iommu_hpm_set_event_filter(event, idx, pid_pscid,
+ did_gscid,
+ pv_pscv, dv_gscv, idt, dmask);
+ }
+ return 0;
+ }
+
riscv_iommu_hpm_set_event_filter(event, idx, pid_pscid, did_gscid,
pv_pscv, dv_gscv, idt, dmask);
+ return 0;
}
static int riscv_iommu_hpm_get_event_idx(struct riscv_iommu_hpm *hpm,
@@ -385,7 +438,8 @@ static int riscv_iommu_hpm_get_event_idx(struct riscv_iommu_hpm *hpm,
return -EAGAIN;
}
- riscv_iommu_hpm_apply_event_filter(hpm, event, idx);
+ if (riscv_iommu_hpm_apply_event_filter(hpm, event, idx))
+ return -EAGAIN;
set_bit(idx, hpm->used_counters);
return idx;
@@ -484,6 +538,8 @@ static int riscv_iommu_hpm_event_init(struct perf_event *event)
}
if (!is_software_event(event->group_leader)) {
+ if (!riscv_iommu_hpm_events_compatible(event->group_leader, event))
+ return -EINVAL;
if (++group_num_events > hpm->num_counters)
return -EINVAL;
}
@@ -491,6 +547,8 @@ static int riscv_iommu_hpm_event_init(struct perf_event *event)
for_each_sibling_event(sibling, event->group_leader) {
if (is_software_event(sibling))
continue;
+ if (!riscv_iommu_hpm_events_compatible(sibling, event))
+ return -EINVAL;
if (++group_num_events > hpm->num_counters)
return -EINVAL;
}
@@ -593,10 +651,33 @@ static const struct attribute_group riscv_iommu_hpm_format_group = {
.attrs = riscv_iommu_hpm_formats,
};
+static ssize_t riscv_iommu_hpm_global_filter_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct riscv_iommu_hpm *hpm = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n",
+ hpm->global_filter ? "true" : "false");
+}
+
+static struct device_attribute riscv_iommu_hpm_global_filter_attr =
+ __ATTR(global_filter, 0444, riscv_iommu_hpm_global_filter_show, NULL);
+
+static struct attribute *riscv_iommu_hpm_vendor_attrs[] = {
+ &riscv_iommu_hpm_global_filter_attr.attr,
+ NULL
+};
+
+static const struct attribute_group riscv_iommu_hpm_vendor_group = {
+ .attrs = riscv_iommu_hpm_vendor_attrs,
+};
+
static const struct attribute_group *riscv_iommu_hpm_attr_grps[] = {
&riscv_iommu_hpm_cpumask_group,
&riscv_iommu_hpm_events_group,
&riscv_iommu_hpm_format_group,
+ &riscv_iommu_hpm_vendor_group,
NULL
};
@@ -776,6 +857,7 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
hpm->base = subdev->base;
hpm->on_cpu = raw_smp_processor_id();
hpm->irq = info->irq;
+ hpm->global_filter = info->global_filter;
bitmap_zero(hpm->used_counters, RISCV_IOMMU_HPMCOUNTER_MAX);
bitmap_zero(hpm->supported_events, RISCV_IOMMU_HPMEVENT_MAX);
@@ -831,8 +913,9 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
auxiliary_set_drvdata(auxdev, hpm);
- dev_info(dev, "HPM: Registered %s (%d counters, IRQ %d)\n",
- hpm_name, hpm->num_counters, hpm->irq);
+ dev_info(dev, "HPM: Registered %s (%d counters, IRQ %d, %s filter)\n",
+ hpm_name, hpm->num_counters, hpm->irq,
+ hpm->global_filter ? "global" : "per-counter");
return 0;
}
@@ -847,6 +930,7 @@ static void riscv_iommu_hpm_remove(struct auxiliary_device *auxdev)
static const struct auxiliary_device_id riscv_iommu_hpm_ids[] = {
{ .name = "iommu.riscv_iommu_hpm" },
+ { .name = "iommu.spacemit_ioats_hpm" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, riscv_iommu_hpm_ids);
diff --git a/include/linux/riscv_iommu.h b/include/linux/riscv_iommu.h
index 0447bc4d1fab..6af592dfaa00 100644
--- a/include/linux/riscv_iommu.h
+++ b/include/linux/riscv_iommu.h
@@ -36,9 +36,11 @@ struct riscv_iommu_subdev {
/**
* struct riscv_iommu_hpm_info - HPM info for IOATS (main IOMMU HPM)
* @irq: interrupt number
+ * @global_filter: true if single global filter
*/
struct riscv_iommu_hpm_info {
unsigned int irq;
+ bool global_filter;
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 6/8] iommu/riscv: Add SpacemiT T100 IOATC HPM support
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
` (4 preceding siblings ...)
2026-02-28 14:44 ` [PATCH v5 5/8] spacemit/t100: Add global filter awareness for RISC-V IOMMU HPM Lv Zheng
@ 2026-02-28 14:45 ` Lv Zheng
2026-02-28 14:45 ` [PATCH v5 7/8] iommu/riscv: Add vendor event support for RISC-V IOMMU HPM Lv Zheng
2026-02-28 14:45 ` [PATCH v5 8/8] perf vendor events riscv: Add SpacemiT T100 HPM event aliases Lv Zheng
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:45 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
Add IOATC discovery and HPM support for SpacemiT T100.
SpacemiT T100 supports distributed architecture which allows IOTLBs to be
cached in adjacent to the DMA masters. Such IOTLB controllers are called
as IOATCs. Adds distributed HPM support for IOATCs.
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
---
drivers/iommu/riscv/iommu-bits.h | 12 +++
drivers/iommu/riscv/iommu-platform.c | 4 +-
drivers/iommu/riscv/iommu.c | 107 ++++++++++++++++++++++++++-
drivers/iommu/riscv/iommu.h | 2 +-
drivers/perf/riscv_iommu_hpm.c | 48 +++++++++++-
include/linux/riscv_iommu.h | 6 +-
6 files changed, 171 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index 98daf0e1a306..c8536c64ef42 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -278,6 +278,14 @@ enum riscv_iommu_hpmevent_id {
#define RISCV_IOMMU_ICVEC_PMIV GENMASK_ULL(11, 8)
#define RISCV_IOMMU_ICVEC_PIV GENMASK_ULL(15, 12)
+/* 5.28 Distributed translation interface status register (dtisr0-3) (4 * 32-bits) */
+#define RISCV_IOMMU_REG_DTISR_BASE 0x02B0
+#define RISCV_IOMMU_REG_DTISR(_n) (RISCV_IOMMU_REG_DTISR_BASE + ((_n) * 0x04))
+#define RISCV_IOMMU_DTI_STS_SHIFT(_n) (((_n) % 16) * 2)
+#define RISCV_IOMMU_DTI_STS_MASK(_n) (0x3 << RISCV_IOMMU_DTI_STS_SHIFT(_n))
+#define RISCV_IOMMU_DTI_STS_NONE 0x0
+#define RISCV_IOMMU_DTI_STS_IOATC 0x1
+
/* 5.28 MSI Configuration table (32 * 64bits) */
#define RISCV_IOMMU_REG_MSI_CFG_TBL 0x0300
#define RISCV_IOMMU_REG_MSI_CFG_TBL_ADDR(_n) \
@@ -292,6 +300,10 @@ enum riscv_iommu_hpmevent_id {
#define RISCV_IOMMU_REG_SIZE 0x1000
+/* SpacemiT IOMMU IOATC registers */
+#define MAX_RISCV_IOMMU_IOATC 64
+#define RISCV_IOMMU_IOATC_BASE(_idx) (((_idx) + 1) * RISCV_IOMMU_REG_SIZE)
+
/*
* Chapter 2: Data structures
*/
diff --git a/drivers/iommu/riscv/iommu-platform.c b/drivers/iommu/riscv/iommu-platform.c
index e8e52bca8856..469b8bfd2151 100644
--- a/drivers/iommu/riscv/iommu-platform.c
+++ b/drivers/iommu/riscv/iommu-platform.c
@@ -75,8 +75,8 @@ static int riscv_iommu_platform_probe(struct platform_device *pdev)
if (iommu->irqs_count <= 0)
return dev_err_probe(dev, -ENODEV,
"no IRQ resources provided\n");
- if (iommu->irqs_count > RISCV_IOMMU_INTR_COUNT)
- iommu->irqs_count = RISCV_IOMMU_INTR_COUNT;
+ if (iommu->irqs_count > RISCV_IOMMU_INTR_COUNT + MAX_RISCV_IOMMU_IOATC)
+ iommu->irqs_count = RISCV_IOMMU_INTR_COUNT + MAX_RISCV_IOMMU_IOATC;
igs = FIELD_GET(RISCV_IOMMU_CAPABILITIES_IGS, iommu->caps);
switch (igs) {
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 21ff7e50d115..e10011493228 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -55,9 +55,21 @@ struct riscv_iommu_devres {
void *addr;
};
+static unsigned int riscv_iommu_reg_ipsr(struct riscv_iommu_subdev *subdev)
+{
+ struct riscv_iommu_hpm_info *info = subdev->info;
+ unsigned int offset;
+
+ if (info && info->is_ioatc)
+ offset = RISCV_IOMMU_IOATC_BASE(info->index);
+ else
+ offset = 0;
+ return offset + RISCV_IOMMU_REG_IPSR;
+}
+
bool riscv_iommu_pmip_status(struct riscv_iommu_subdev *subdev)
{
- u32 ipsr = riscv_iommu_readl(subdev->iommu, RISCV_IOMMU_REG_IPSR);
+ u32 ipsr = riscv_iommu_readl(subdev->iommu, riscv_iommu_reg_ipsr(subdev));
return !!(ipsr & RISCV_IOMMU_IPSR_PMIP);
}
@@ -65,7 +77,7 @@ EXPORT_SYMBOL_GPL(riscv_iommu_pmip_status);
void riscv_iommu_clear_pmip(struct riscv_iommu_subdev *subdev)
{
- riscv_iommu_writel(subdev->iommu, RISCV_IOMMU_REG_IPSR,
+ riscv_iommu_writel(subdev->iommu, riscv_iommu_reg_ipsr(subdev),
RISCV_IOMMU_IPSR_PMIP);
}
EXPORT_SYMBOL_GPL(riscv_iommu_clear_pmip);
@@ -1710,6 +1722,7 @@ static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
return;
hpm_info->irq = irq;
+ hpm_info->is_ioatc = false;
params = (struct riscv_iommu_subdev_params) {
.name = "riscv_iommu_hpm",
@@ -1731,6 +1744,95 @@ static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
}
}
+struct riscv_iommu_ioatc_desc {
+ int irq;
+ u32 index;
+};
+
+static int riscv_iommu_collect_ioatcs(struct riscv_iommu_device *iommu,
+ struct riscv_iommu_ioatc_desc *descs,
+ int max_desc)
+{
+ struct device *dev = iommu->dev;
+ int count, index, i, j;
+ u32 dtisr, state;
+ int nr_ioats_irqs, nr_ioatc_irqs;
+
+ if (!of_device_is_compatible(dev->of_node, "spacemit,t100"))
+ return 0;
+
+ if (iommu->fctl & RISCV_IOMMU_FCTL_WSI)
+ nr_ioats_irqs = 1;
+ else
+ nr_ioats_irqs = RISCV_IOMMU_INTR_COUNT;
+
+ if (iommu->irqs_count > nr_ioats_irqs)
+ nr_ioatc_irqs = iommu->irqs_count - nr_ioats_irqs;
+ else
+ nr_ioatc_irqs = 0;
+
+ count = 0;
+ for (i = 0; i < 4 && count < max_desc; i++) {
+ dtisr = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_DTISR(i));
+ for (j = 0; j < 16 && count < max_desc; j++) {
+ index = i * 16 + j;
+ state = (dtisr & RISCV_IOMMU_DTI_STS_MASK(index)) >>
+ RISCV_IOMMU_DTI_STS_SHIFT(index);
+ if (state != RISCV_IOMMU_DTI_STS_IOATC)
+ continue;
+ descs[count].index = index;
+ if (count < nr_ioatc_irqs && index < MAX_RISCV_IOMMU_IOATC)
+ descs[count].irq = iommu->irqs[count + nr_ioats_irqs];
+ else
+ descs[count].irq = 0;
+ count++;
+ }
+ }
+ return count;
+}
+
+static void riscv_iommu_enumerate_ioatc(struct riscv_iommu_device *iommu)
+{
+ struct riscv_iommu_ioatc_desc ioatcs[MAX_RISCV_IOMMU_IOATC];
+ struct riscv_iommu_hpm_info *ioatc_info;
+ struct riscv_iommu_subdev_params params;
+ void __iomem *base;
+ int nr_ioatcs, i, ret;
+
+ nr_ioatcs = riscv_iommu_collect_ioatcs(iommu, ioatcs, ARRAY_SIZE(ioatcs));
+ if (nr_ioatcs <= 0)
+ return;
+
+ for (i = 0; i < nr_ioatcs; i++) {
+ if (ioatcs[i].irq <= 0)
+ continue;
+
+ ioatc_info = kzalloc(sizeof(*ioatc_info), GFP_KERNEL);
+ if (!ioatc_info)
+ continue;
+
+ ioatc_info->irq = ioatcs[i].irq;
+ ioatc_info->index = ioatcs[i].index;
+ ioatc_info->global_filter = true;
+ ioatc_info->is_ioatc = true;
+
+ base = iommu->reg + RISCV_IOMMU_IOATC_BASE(ioatcs[i].index);
+
+ params = (struct riscv_iommu_subdev_params) {
+ .name = "spacemit_ioatc_hpm",
+ .info = ioatc_info,
+ .base = base + RISCV_IOMMU_REG_IOCOUNTOVF,
+ };
+
+ ret = riscv_iommu_subdev_add(iommu, ¶ms);
+ if (ret) {
+ kfree(ioatc_info);
+ dev_warn(iommu->dev, "Failed to add IOATC%u: %d\n",
+ ioatcs[i].index, ret);
+ }
+ }
+}
+
/**
* riscv_iommu_subdev_setup - Enumerate auxiliary bus subdevices
*
@@ -1743,6 +1845,7 @@ static void riscv_iommu_enumerate_hpm(struct riscv_iommu_device *iommu)
void riscv_iommu_subdev_setup(struct riscv_iommu_device *iommu)
{
riscv_iommu_enumerate_hpm(iommu);
+ riscv_iommu_enumerate_ioatc(iommu);
}
/**
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 1296625488ef..f1bb682dd478 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -51,7 +51,7 @@ struct riscv_iommu_device {
u32 fctl;
/* available interrupt numbers, MSI or WSI */
- unsigned int irqs[RISCV_IOMMU_INTR_COUNT];
+ unsigned int irqs[RISCV_IOMMU_INTR_COUNT + MAX_RISCV_IOMMU_IOATC];
unsigned int irqs_count;
unsigned int icvec;
diff --git a/drivers/perf/riscv_iommu_hpm.c b/drivers/perf/riscv_iommu_hpm.c
index efa65caef0dc..b166b3cb6d4f 100644
--- a/drivers/perf/riscv_iommu_hpm.c
+++ b/drivers/perf/riscv_iommu_hpm.c
@@ -681,6 +681,31 @@ static const struct attribute_group *riscv_iommu_hpm_attr_grps[] = {
NULL
};
+#define IOMMU_IOATC_EVENT_ATTR(_name, _id) \
+ PMU_EVENT_ATTR_ID(_name, riscv_iommu_hpm_event_show, _id)
+
+static struct attribute *riscv_iommu_hpm_ioatc_events[] = {
+ IOMMU_IOATC_EVENT_ATTR(cycles, RISCV_IOMMU_HPMEVENT_CYCLES),
+ IOMMU_IOATC_EVENT_ATTR(untrans_rq, RISCV_IOMMU_HPMEVENT_URQ),
+ IOMMU_IOATC_EVENT_ATTR(trans_rq, RISCV_IOMMU_HPMEVENT_TRQ),
+ IOMMU_IOATC_EVENT_ATTR(tlb_mis, RISCV_IOMMU_HPMEVENT_TLB_MISS),
+ NULL
+};
+
+static const struct attribute_group riscv_iommu_hpm_ioatc_events_group = {
+ .name = "events",
+ .attrs = riscv_iommu_hpm_ioatc_events,
+ .is_visible = riscv_iommu_hpm_event_is_visible,
+};
+
+static const struct attribute_group *riscv_iommu_hpm_ioatc_attr_grps[] = {
+ &riscv_iommu_hpm_cpumask_group,
+ &riscv_iommu_hpm_ioatc_events_group,
+ &riscv_iommu_hpm_format_group,
+ &riscv_iommu_hpm_vendor_group,
+ NULL
+};
+
static irqreturn_t riscv_iommu_hpm_handle_irq(int irq_num, void *data)
{
struct riscv_iommu_hpm *hpm = data;
@@ -826,6 +851,15 @@ static void riscv_iommu_hpm_set_standard_events(struct riscv_iommu_hpm *hpm)
set_bit(RISCV_IOMMU_HPMEVENT_G_WALKS, hpm->supported_events);
}
+static void riscv_iommu_hpm_set_ioatc_events(struct riscv_iommu_hpm *hpm)
+{
+ /* SpacemiT T100 IOATC: subset of events (URQ, TRQ, TLB_MISS) */
+ set_bit(RISCV_IOMMU_HPMEVENT_CYCLES, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_URQ, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_TRQ, hpm->supported_events);
+ set_bit(RISCV_IOMMU_HPMEVENT_TLB_MISS, hpm->supported_events);
+}
+
static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
@@ -834,6 +868,8 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
struct device *dev = &auxdev->dev;
struct riscv_iommu_hpm_info *info;
const char *hpm_name;
+ const struct attribute_group **attr_grps;
+ bool is_ioatc;
u32 val;
int err;
@@ -858,6 +894,7 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
hpm->on_cpu = raw_smp_processor_id();
hpm->irq = info->irq;
hpm->global_filter = info->global_filter;
+ is_ioatc = info->is_ioatc;
bitmap_zero(hpm->used_counters, RISCV_IOMMU_HPMCOUNTER_MAX);
bitmap_zero(hpm->supported_events, RISCV_IOMMU_HPMEVENT_MAX);
@@ -869,7 +906,13 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
return -ENODEV;
riscv_iommu_hpm_reset(hpm);
- riscv_iommu_hpm_set_standard_events(hpm);
+ if (is_ioatc)
+ riscv_iommu_hpm_set_ioatc_events(hpm);
+ else
+ riscv_iommu_hpm_set_standard_events(hpm);
+
+ 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);
if (!hpm_name)
@@ -893,7 +936,7 @@ static int riscv_iommu_hpm_probe(struct auxiliary_device *auxdev,
.start = riscv_iommu_hpm_event_start,
.stop = riscv_iommu_hpm_event_stop,
.read = riscv_iommu_hpm_event_update,
- .attr_groups = riscv_iommu_hpm_attr_grps,
+ .attr_groups = attr_grps,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
};
@@ -931,6 +974,7 @@ static void riscv_iommu_hpm_remove(struct auxiliary_device *auxdev)
static const struct auxiliary_device_id riscv_iommu_hpm_ids[] = {
{ .name = "iommu.riscv_iommu_hpm" },
{ .name = "iommu.spacemit_ioats_hpm" },
+ { .name = "iommu.spacemit_ioatc_hpm" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, riscv_iommu_hpm_ids);
diff --git a/include/linux/riscv_iommu.h b/include/linux/riscv_iommu.h
index 6af592dfaa00..71a961731c22 100644
--- a/include/linux/riscv_iommu.h
+++ b/include/linux/riscv_iommu.h
@@ -34,13 +34,17 @@ struct riscv_iommu_subdev {
};
/**
- * struct riscv_iommu_hpm_info - HPM info for IOATS (main IOMMU HPM)
+ * struct riscv_iommu_hpm_info - HPM info for IOATS and IOATC
* @irq: interrupt number
* @global_filter: true if single global filter
+ * @is_ioatc: false for IOATS, true for IOATC
+ * @index: DTISR index for IOATC (0-63), 0 for IOATS
*/
struct riscv_iommu_hpm_info {
unsigned int irq;
bool global_filter;
+ bool is_ioatc;
+ u8 index;
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 7/8] iommu/riscv: Add vendor event support for RISC-V IOMMU HPM
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
` (5 preceding siblings ...)
2026-02-28 14:45 ` [PATCH v5 6/8] iommu/riscv: Add SpacemiT T100 IOATC HPM support Lv Zheng
@ 2026-02-28 14:45 ` Lv Zheng
2026-02-28 14:45 ` [PATCH v5 8/8] perf vendor events riscv: Add SpacemiT T100 HPM event aliases Lv Zheng
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:45 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
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@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@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, ¶ms);
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
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 8/8] perf vendor events riscv: Add SpacemiT T100 HPM event aliases
[not found] <cover.1772289741.git.lv.zheng@linux.spacemit.com>
` (6 preceding siblings ...)
2026-02-28 14:45 ` [PATCH v5 7/8] iommu/riscv: Add vendor event support for RISC-V IOMMU HPM Lv Zheng
@ 2026-02-28 14:45 ` Lv Zheng
7 siblings, 0 replies; 13+ messages in thread
From: Lv Zheng @ 2026-02-28 14:45 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Yixun Lan
Cc: Jingyu Li, Lv Zheng, Zong Li, Yaxing Guo, Zhijian Chen, iommu,
linux-perf-users, linux-riscv, spacemit, devicetree, linux-kernel
Add JSON HPM event aliases for SpacemiT distributed IOMMU (T100) which is
general and compatible for all SpacemiT RISC-V SoCs.
Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
---
MAINTAINERS | 5 +
.../arch/riscv/spacemit/iommu/sys/ioatc.json | 30 ++++
.../arch/riscv/spacemit/iommu/sys/ioats.json | 163 ++++++++++++++++++
3 files changed, 198 insertions(+)
create mode 100644 tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioatc.json
create mode 100644 tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioats.json
diff --git a/MAINTAINERS b/MAINTAINERS
index 7c50701b6001..8b6458c1f63a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22459,12 +22459,17 @@ K: riscv
RISC-V IOMMU
M: Tomasz Jeznach <tjeznach@rivosinc.com>
+M: Lv Zheng <lv.zheng@linux.spacemit.com>
+M: Jingyu Li <joey.li@spacemit.com>
L: iommu@lists.linux.dev
L: linux-riscv@lists.infradead.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
F: Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
F: drivers/iommu/riscv/
+F: drivers/perf/riscv_iommu_hpm.c
+F: include/linux/riscv_iommu.h
+F: tools/perf/pmu-events/arch/riscv/spacemit/iommu/
RISC-V MICROCHIP SUPPORT
M: Conor Dooley <conor.dooley@microchip.com>
diff --git a/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioatc.json b/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioatc.json
new file mode 100644
index 000000000000..eb2e6fe24c62
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioatc.json
@@ -0,0 +1,30 @@
+[
+ {
+ "EventName": "mtlb_lkp",
+ "EventCode": "0x38",
+ "BriefDescription": "IOATC main TLB (MTLB) lookups",
+ "Unit": "spacemit_ioatc_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "mtlb_mis",
+ "EventCode": "0x39",
+ "BriefDescription": "IOATC main TLB (MTLB) misses",
+ "Unit": "spacemit_ioatc_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "utlb_lkp",
+ "EventCode": "0x3A",
+ "BriefDescription": "IOATC micro TLB (uTLB) lookups",
+ "Unit": "spacemit_ioatc_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "utlb_mis",
+ "EventCode": "0x3B",
+ "BriefDescription": "IOATC micro TLB (uTLB) misses",
+ "Unit": "spacemit_ioatc_hpm",
+ "Compat": "spacemit,t100"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioats.json b/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioats.json
new file mode 100644
index 000000000000..709c0a9d2a8e
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/spacemit/iommu/sys/ioats.json
@@ -0,0 +1,163 @@
+[
+ {
+ "EventName": "pri_rq",
+ "EventCode": "0x10",
+ "BriefDescription": "IOATS PCIe page request interface (PRI) requests",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "ptwc_rq",
+ "EventCode": "0x11",
+ "BriefDescription": "IOATS page table walk (PTW) cache requests",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "dtwc_rq",
+ "EventCode": "0x12",
+ "BriefDescription": "IOATS directory table walk (DTW) cache requests",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "dtwc_mis",
+ "EventCode": "0x13",
+ "BriefDescription": "IOATS directory table walk (DTW) cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "ptwc_mis",
+ "EventCode": "0x14",
+ "BriefDescription": "IOATS page table walk (PTW) cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "all_trans_rq",
+ "EventCode": "0x15",
+ "BriefDescription": "IOATS all translation requests",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "dtwc_lkp",
+ "EventCode": "0x20",
+ "BriefDescription": "IOATS directory table walk (DTW) cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s0_ptwc_lkp",
+ "EventCode": "0x28",
+ "BriefDescription": "IOATS s-stage level-0 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s0_ptwc_mis",
+ "EventCode": "0x29",
+ "BriefDescription": "IOATS s-stage level-0 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s1_ptwc_lkp",
+ "EventCode": "0x2A",
+ "BriefDescription": "IOATS s-stage level-1 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s1_ptwc_mis",
+ "EventCode": "0x2B",
+ "BriefDescription": "IOATS s-stage level-1 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s2_ptwc_lkp",
+ "EventCode": "0x2C",
+ "BriefDescription": "IOATS s-stage level-2 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s2_ptwc_mis",
+ "EventCode": "0x2D",
+ "BriefDescription": "IOATS s-stage level-2 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s3_ptwc_lkp",
+ "EventCode": "0x2E",
+ "BriefDescription": "IOATS s-stage level-3 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "s3_ptwc_mis",
+ "EventCode": "0x2F",
+ "BriefDescription": "IOATS s-stage level-3 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g0_ptwc_lkp",
+ "EventCode": "0x30",
+ "BriefDescription": "IOATS g-stage level-0 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g0_ptwc_mis",
+ "EventCode": "0x31",
+ "BriefDescription": "IOATS g-stage level-0 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g1_ptwc_lkp",
+ "EventCode": "0x32",
+ "BriefDescription": "IOATS g-stage level-1 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g1_ptwc_mis",
+ "EventCode": "0x33",
+ "BriefDescription": "IOATS g-stage level-1 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g2_ptwc_lkp",
+ "EventCode": "0x34",
+ "BriefDescription": "IOATS g-stage level-2 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g2_ptwc_mis",
+ "EventCode": "0x35",
+ "BriefDescription": "IOATS g-stage level-2 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g3_ptwc_lkp",
+ "EventCode": "0x36",
+ "BriefDescription": "IOATS g-stage level-3 PTW cache lookups",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ },
+ {
+ "EventName": "g3_ptwc_mis",
+ "EventCode": "0x37",
+ "BriefDescription": "IOATS g-stage level-3 PTW cache misses",
+ "Unit": "spacemit_ioats_hpm",
+ "Compat": "spacemit,t100"
+ }
+]
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread