[PATCH v1.1 6/7] spacemit/t100: Add global filter support for RISC-V IOMMU HPM

Lv Zheng
2026-01-29T06:09:41Z

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. This silicon design can save the number of
the event bus signals.

Signed-off-by: Lv Zheng <lv.zheng@spacemit.com>
Signed-off-by: Jingyu Li <joey.li@spacemit.com>
---
drivers/iommu/riscv/iommu-hpm.c | 93 ++++++++++++++++++++++++++++++---
drivers/iommu/riscv/iommu.h | 1 +
2 files changed, 87 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/riscv/iommu-hpm.c b/drivers/iommu/riscv/iommu-hpm.c
index b01d72dd056f..23e1afc262ea 100644
--- a/drivers/iommu/riscv/iommu-hpm.c
+++ b/drivers/iommu/riscv/iommu-hpm.c
@@ -134,6 +134,49 @@ 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)
+{
+ u32 curr_pid_pscid, curr_did_gscid, curr_pv_pscv;
+ u32 curr_dv_gscv, curr_idt, curr_dmask;
+ u32 new_pid_pscid, new_did_gscid, new_pv_pscv;
+ u32 new_dv_gscv, new_idt, new_dmask;
+
+ curr_pid_pscid = get_filter_pid_pscid(curr);
+ curr_did_gscid = get_filter_did_gscid(curr);
+ curr_pv_pscv = get_filter_pv_pscv(curr);
+ curr_dv_gscv = get_filter_dv_gscv(curr);
+ curr_idt = get_filter_idt(curr);
+ curr_dmask = get_filter_dmask(curr);
+
+ new_pid_pscid = get_filter_pid_pscid(new);
+ new_did_gscid = get_filter_did_gscid(new);
+ new_pv_pscv = get_filter_pv_pscv(new);
+ new_dv_gscv = get_filter_dv_gscv(new);
+ new_idt = get_filter_idt(new);
+ new_dmask = get_filter_dmask(new);
+
+ return (curr_pid_pscid == new_pid_pscid &&
+ curr_did_gscid == new_did_gscid &&
+ curr_pv_pscv == new_pv_pscv &&
+ curr_dv_gscv == new_dv_gscv &&
+ curr_idt == new_idt &&
+ curr_dmask == new_dmask);
+}
+
+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;
+}
+
static void riscv_iommu_hpm_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -246,9 +289,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);
@@ -258,14 +302,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);

@@ -287,7 +353,10 @@ 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;
@@ -363,6 +432,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;
}
@@ -370,6 +441,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;
}
@@ -685,6 +758,7 @@ static void riscv_iommu_hpm_remove(void *data)
static int riscv_iommu_hpm_register_unit(struct riscv_iommu_device *iommu,
struct riscv_iommu_hpm *iommu_hpm,
u32 offset, int irq,
+ bool global_filter,
const struct attribute_group **attr_groups,
const char *prefix, int index)
{
@@ -709,6 +783,8 @@ static int riscv_iommu_hpm_register_unit(struct riscv_iommu_device *iommu,
iommu_hpm->base = base;
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_property_read_bool(dev->of_node,
+ "global-filter");

riscv_iommu_hpm_writel(iommu_hpm,
RISCV_IOMMU_REG_IOCOUNTINH, 0xFFFFFFFF);
@@ -775,8 +851,10 @@ 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:
@@ -845,7 +923,8 @@ int riscv_iommu_add_hpm(struct riscv_iommu_device *iommu)
if (rc < 0)
return rc;

- rc = riscv_iommu_hpm_register_unit(iommu, &iommu->hpm, 0, irq,
+ rc = riscv_iommu_hpm_register_unit(iommu, &iommu->hpm,
+ 0, irq, true,
riscv_iommu_hpm_attr_grps,
"riscv_iommu_hpm", -1);
if (rc < 0)
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 0ad9f5cad4de..5ebf4e85962e 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -41,6 +41,7 @@ struct riscv_iommu_hpm {
int irq;
int on_cpu;
struct hlist_node node;
+ bool global_filter;
/*
* Layout of events:
* 0 -> HPMCYCLES
--
2.43.0

This message and any attachment are confidential and may be privileged or otherwise protected from disclosure. If you are not an intended recipient of this message, please delete it and any attachment from your system and notify the sender immediately by reply e-mail. Unintended recipients should not use, copy, disclose or take any action based on this message or any information contained in this message. Emails cannot be guaranteed to be secure or error free as they can be intercepted, amended, lost or destroyed, and you should take full responsibility for security checking.

本邮件及其任何附件具有保密性质，并可能受其他保护或不允许被披露给第三方。如阁下误收到本邮件，敬请立即以回复电子邮件的方式通知发件人，并将本邮件及其任何附件从阁下系统中予以删除。如阁下并非本邮件写明之收件人，敬请切勿使用、复制、披露本邮件或其任何内容，亦请切勿依本邮件或其任何内容而采取任何行动。电子邮件无法保证是一种安全和不会出现任何差错的通信方式，可能会被拦截、修改、丢失或损坏，收件人需自行负责做好安全检查。