[PATCH v4 5/7] spacemit/t100: Add global filter awareness for RISC-V IOMMU HPM

Lv Zheng
2026-02-05T09:11:39Z

Introduces 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-hpm.c | 68 ++++++++++++++++++++++++++++++---
drivers/iommu/riscv/iommu.h | 1 +
2 files changed, 63 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/riscv/iommu-hpm.c b/drivers/iommu/riscv/iommu-hpm.c
index 4615c4b161a0..6860714fb61e 100644
--- a/drivers/iommu/riscv/iommu-hpm.c
+++ b/drivers/iommu/riscv/iommu-hpm.c
@@ -134,6 +134,30 @@ static inline void riscv_iommu_hpm_interrupt_clear(struct riscv_iommu_hpm *hpm)
riscv_iommu_hpm_writel(hpm, RISCV_IOMMU_REG_IPSR, RISCV_IOMMU_IPSR_PMIP);
}

+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)
+{
+ if (new->pmu != curr->pmu)
+ return false;
+
+ if (to_iommu_hpm(new->pmu)->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
@@ -268,9 +292,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 *iommu_hpm,
- struct perf_event *event, int idx)
+static int riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *iommu_hpm,
+ struct perf_event *event, int idx)
{
+ unsigned int cur_idx, num_ctrs = iommu_hpm->num_counters;
u32 pid_pscid, did_gscid, pv_pscv, dv_gscv, idt, dmask;

pid_pscid = get_filter_pid_pscid(event);
@@ -280,14 +305,36 @@ static void riscv_iommu_hpm_apply_event_filter(struct riscv_iommu_hpm *iommu_hpm
idt = get_filter_idt(event);
dmask = get_filter_dmask(event);

+ if (iommu_hpm->global_filter) {
+ cur_idx = find_first_bit(iommu_hpm->used_counters, num_ctrs - 1);
+ if (cur_idx == num_ctrs - 1) {
+ /* First event, set the global filter */
+ 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 is compatible with
+ * the global filter
+ */
+ if (!riscv_iommu_hpm_check_global_filter(iommu_hpm->events[cur_idx + 1],
+ event)) {
+ dev_dbg(iommu_hpm->pmu.dev,
+ "HPM: Filter incompatible with global filter\n");
+ return -EAGAIN;
+ }
+ }
+ 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 *iommu_hpm,
struct perf_event *event)
{
- int idx;
+ int idx, err;
unsigned int num_ctrs = iommu_hpm->num_counters;
u16 event_id = get_event(event);

@@ -309,7 +356,9 @@ static int riscv_iommu_hpm_get_event_idx(struct riscv_iommu_hpm *iommu_hpm,
return -EAGAIN;
}

- riscv_iommu_hpm_apply_event_filter(iommu_hpm, event, idx);
+ err = riscv_iommu_hpm_apply_event_filter(iommu_hpm, event, idx);
+ if (err)
+ return err;
set_bit(idx, iommu_hpm->used_counters);

return idx;
@@ -405,6 +454,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 > iommu_hpm->num_counters)
return -EINVAL;
}
@@ -412,6 +463,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 > iommu_hpm->num_counters)
return -EINVAL;
}
@@ -729,6 +782,8 @@ static int riscv_iommu_hpm_register_unit(struct riscv_iommu_device *iommu,
bitmap_zero(iommu_hpm->used_counters, RISCV_IOMMU_HPMCOUNTER_MAX);
bitmap_zero(iommu_hpm->supported_events, RISCV_IOMMU_HPMEVENT_MAX);

+ iommu_hpm->global_filter = of_device_is_compatible(dev->of_node,
+ "spacemit,t100");
riscv_iommu_hpm_writel(iommu_hpm,
RISCV_IOMMU_REG_IOCOUNTINH, 0xFFFFFFFF);
val = riscv_iommu_hpm_readl(iommu_hpm,
@@ -793,8 +848,9 @@ static int riscv_iommu_hpm_register_unit(struct riscv_iommu_device *iommu,
if (err)
goto err_cpuhp;

- dev_info(dev, "HPM: Registered %s (%d counters, IRQ %d)\n",
- pmu_name, iommu_hpm->num_counters, iommu_hpm->irq);
+ dev_info(dev, "HPM: Registered %s (%d counters, IRQ %d, %s filter)\n",
+ pmu_name, iommu_hpm->num_counters, iommu_hpm->irq,
+ iommu_hpm->global_filter ? "global" : "per-counter");
return 0;

err_cpuhp:
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index d3c11abef5cf..80e96fd7e164 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -42,6 +42,7 @@ struct riscv_iommu_hpm {
int on_cpu;
struct hlist_node node;
const char *identifier;
+ bool global_filter;
/*
* Layout of events:
* 0 -> HPMCYCLES
--
2.43.0