From: Shivani Nittor <shivani@linux.ibm.com>
To: maddy@linux.ibm.com, linuxppc-dev@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org, atrajeev@linux.ibm.com,
shivani@linux.ibm.com, tshah@linux.ibm.com
Subject: [RFC 2/3] powerpc/perf: Add DTS-based MMCR computation
Date: Mon, 29 Jun 2026 11:40:21 +0530 [thread overview]
Message-ID: <20260629061101.43119-3-shivani@linux.ibm.com> (raw)
In-Reply-To: <20260629061101.43119-1-shivani@linux.ibm.com>
Add support for computing MMCR register values from event
descriptions provided in the device tree.
Parse the evt_code_format node to build mappings between event
code fields and MMCR programming fields. Use these mappings to
generate MMCR register values dynamically from the event code.
This allows event encodings and MMCR programming information to
be described in the device tree rather than being hardcoded in
the PMU implementation.
Add compute_mmcr_dts() to isa207-common.c to compute MMCR register
values dynamically from event codes described in the device tree.
The function extracts PMC and field values from the event code using
field_maps[], which are populated by parsing the evt_code_format node
in the DTS during pmu_dts_probe(). Each field entry captures the bit
range in the event code, the target MMCR register, and the shift
position to use when programming the register.
For non-PMC, non-MMCRA fields, the shift is computed either using a
per-PMC target_field_shift formula (target_field_base - pmc *
target_field_shift) or a fixed pgm_start offset. MMCRA and PMC fields
always use the fixed pgm_start path.
MMCR2 privilege filtering (FCP, FCS, FCH) is applied per-event based
on perf_event attr exclude_* flags.
Wire up the DTS PMU to use dts_compute_mmcr() as its compute_mmcr
callback, and carry the ISA207 constraint fields, alternatives,
memory data source and weight helpers, and PPMU flags to match the
Power10 PMU profile.
Also temporarily comment out the duplicate compute_mmcr call site and
the surrounding mtspr block in power_pmu_enable() while the DTS path
is brought up; these will be restored once the transition is complete.
Signed-off-by: Shivani Nittor <shivani@linux.ibm.com>
---
arch/powerpc/include/asm/dts_pmu.h | 17 ++++
arch/powerpc/perf/dts_pmu.c | 118 +++++++++++++++++++++++++++-
arch/powerpc/perf/isa207-common.c | 120 +++++++++++++++++++++++++++++
arch/powerpc/perf/isa207-common.h | 2 +
4 files changed, 253 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/dts_pmu.h b/arch/powerpc/include/asm/dts_pmu.h
index 791dda370de8..1309a45ce604 100644
--- a/arch/powerpc/include/asm/dts_pmu.h
+++ b/arch/powerpc/include/asm/dts_pmu.h
@@ -3,10 +3,27 @@
#include <linux/types.h>
+#define MAX_FIELDS 17
#define MAX_MMCR 5
#define MAX_DTS_EVENTS 32
#define MAX_PMU_COUNTERS 6
+struct dts_field_map {
+ u32 bits_start, bits_end;
+ u32 target_field_base, target_field_shift;
+ u32 pgm_start, mmcr;
+ bool use_target_field_shift, is_pmc;
+ char name[32];
+};
+
+extern struct dts_field_map field_maps[MAX_FIELDS];
+extern int field_count;
+
extern u32 mmcr_regs_sprs[MAX_MMCR];
extern int mmcr_count;
+
+int compute_mmcr_dts(u64 event[], int n_ev,
+ unsigned int hwc[], struct mmcr_regs *mmcr,
+ struct perf_event *pevents[], u32 flags);
+
#endif
diff --git a/arch/powerpc/perf/dts_pmu.c b/arch/powerpc/perf/dts_pmu.c
index 67eabd5ec6e5..39107af6d467 100644
--- a/arch/powerpc/perf/dts_pmu.c
+++ b/arch/powerpc/perf/dts_pmu.c
@@ -10,10 +10,14 @@
#include <linux/string.h>
#include <asm/reg.h>
#include <asm/dts_pmu.h>
-
+#include "isa207-common.h"
extern void unregister_power_pmu(struct power_pmu *pmu);
static u32 pmu_dts_nr_pmc;
+struct dts_field_map pmcsel_map;
+struct dts_field_map pmc_map;
+struct dts_field_map field_maps[MAX_FIELDS];
+int field_count;
u32 mmcr_regs_sprs[MAX_MMCR];
int mmcr_count;
@@ -84,10 +88,55 @@ static const struct attribute_group *pmu_dts_attr_groups[] = {
NULL,
};
+static int dts_compute_mmcr(u64 event[], int n_ev,
+ unsigned int hwc[], struct mmcr_regs *mmcr,
+ struct perf_event *pevents[], u32 flags)
+{
+ int ret;
+
+ ret = compute_mmcr_dts(event, n_ev, hwc, mmcr, pevents, flags);
+ if (!ret)
+ mmcr->mmcr0 |= MMCR0_C56RUN;
+ return ret;
+}
+
+static const unsigned int dts_event_alternatives[][MAX_ALT] = {
+ { 0x600f4, 0x1001e },
+};
+
+static int dts_get_alternatives(u64 event, unsigned int flags, u64 alt[])
+{
+ int num_alt = 0;
+
+ num_alt = isa207_get_alternatives(event, alt,
+ ARRAY_SIZE(dts_event_alternatives), flags,
+ dts_event_alternatives);
+
+ return num_alt;
+}
+
static struct power_pmu dts_pmu = {
- .name = "cpu_dts",
- .n_counter = MAX_PMU_COUNTERS,
- .attr_groups = pmu_dts_attr_groups,
+ .name = "cpu_dts",
+ .n_counter = MAX_PMU_COUNTERS,
+ .attr_groups = pmu_dts_attr_groups,
+ .add_fields = ISA207_ADD_FIELDS,
+ .test_adder = ISA207_TEST_ADDER,
+ .group_constraint_mask = CNST_CACHE_PMC4_MASK,
+ .group_constraint_val = CNST_CACHE_PMC4_VAL,
+ .compute_mmcr = dts_compute_mmcr,
+ // .config_bhrb = power10_config_bhrb,
+ // .bhrb_filter_map = power10_bhrb_filte-r_map,
+ .get_alternatives = dts_get_alternatives,
+ .get_mem_data_src = isa207_get_mem_data_src,
+ .get_mem_weight = isa207_get_mem_weight,
+ .disable_pmc = isa207_disable_pmc,
+ .flags = PPMU_HAS_SIER | PPMU_ARCH_207S |
+ PPMU_ARCH_31 | PPMU_HAS_ATTR_CONFIG1 |
+ PPMU_P10,
+ .attr_groups = pmu_dts_attr_groups,
+ //.bhrb_nr = 32,
+ .capabilities = PERF_PMU_CAP_EXTENDED_REGS,
+ //.check_attr_config = power10_check_attr_config,
};
/* Device Tree match */
@@ -108,6 +157,8 @@ static int pmu_dts_probe(struct platform_device *pdev)
u32 code64[2];
u32 code128[4];
int cells;
+ struct device_node *fmt_np, *field_np;
+ u32 bits[2], pgm[2];
const char *str;
pr_info("PMU DTS probe node = %s\n", np->full_name);
@@ -170,6 +221,65 @@ static int pmu_dts_probe(struct platform_device *pdev)
}
/* Parse events */
+ fmt_np = of_get_child_by_name(np, "evt_code_format");
+ if (!fmt_np) {
+ pr_err("pmu_dts: no evt_code_format node\n");
+ return -EINVAL;
+ }
+
+ field_count = 0;
+ for_each_child_of_node(fmt_np, field_np) {
+
+ struct dts_field_map *f = &field_maps[field_count];
+
+ printk("field_np->name is %s\n", field_np->name);
+ snprintf(f->name, sizeof(f->name), "%s", field_np->name);
+ f->is_pmc = false;
+ f->use_target_field_shift = false;
+
+ /* Identify PMC field */
+ if (!strcmp(field_np->name, "PMCx"))
+ f->is_pmc = true;
+
+ if (of_property_read_u32_array(field_np, "bits", bits, 2))
+ continue;
+
+ f->bits_start = bits[0];
+ f->bits_end = bits[1];
+
+ if (of_property_read_u32(field_np, "mmcr", &f->mmcr))
+ continue;
+
+ /* Check target_field_shift-based mapping */
+ if (!f->is_pmc && f->mmcr != 4) {
+ if (!of_property_read_u32(field_np, "target_field_base",
+ &f->target_field_base)) {
+ of_property_read_u32(field_np, "target_field_shift",
+ &f->target_field_shift);
+ f->use_target_field_shift = true;
+
+ } else {
+ if (!of_property_read_u32_array(field_np, "target_fields", pgm, 2))
+ f->pgm_start = pgm[0];
+ else
+ f->pgm_start = 0;
+ }
+
+ } else {
+ /* MMCRA or PMC field → no target_field_shift */
+ if (!of_property_read_u32_array(field_np, "target_fields", pgm, 2))
+ f->pgm_start = pgm[0];
+ else
+ f->pgm_start = 0;
+
+ f->use_target_field_shift = false;
+ }
+
+ field_count++;
+ if (field_count >= MAX_FIELDS)
+ break;
+ }
+
events_np = of_get_child_by_name(np, "events");
if (!events_np) {
pr_err("pmu_dts: no events node found\n");
diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c
index e11d1bbbc27b..f912432fd6db 100644
--- a/arch/powerpc/perf/isa207-common.c
+++ b/arch/powerpc/perf/isa207-common.c
@@ -80,6 +80,11 @@ static unsigned long sdar_mod_val(u64 event)
return p9_SDAR_MODE(event);
}
+static u64 extract_bits(u64 val, int start, int end)
+{
+ return (val >> start) & ((1ULL << (end - start + 1)) - 1);
+}
+
static void mmcra_sdar_mode(u64 event, unsigned long *mmcra)
{
/*
@@ -743,6 +748,121 @@ int isa207_compute_mmcr(u64 event[], int n_ev,
return 0;
}
+int compute_mmcr_dts(u64 event[], int n_ev,
+ unsigned int hwc[], struct mmcr_regs *mmcr,
+ struct perf_event *pevents[], u32 flags)
+{
+ u64 mmcr_val[MAX_MMCR] = {0};
+ u32 pmc = 0, pmc_inuse = 0;
+ int i, ev;
+ u32 pmc_arr[MAX_HWEVENTS] = {0};
+
+ for (ev = 0; ev < n_ev; ev++) {
+ pmc = (event[ev] >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK;
+ pmc_arr[ev] = pmc;
+ if (pmc)
+ pmc_inuse |= 1 << pmc;
+
+ hwc[ev] = pmc - 1;
+ }
+
+ /*
+ * Disable bhrb unless explicitly requested
+ * by setting MMCRA (BHRBRD) bit.
+ */
+ if (cpu_has_feature(CPU_FTR_ARCH_31))
+ mmcr->mmcra |= MMCRA_BHRB_DISABLE;
+
+ if (!pmc) {
+ for (pmc = 1; pmc <= 4; ++pmc) {
+ if (!(pmc_inuse & (1 << pmc)))
+ break;
+ }
+
+ pmc_inuse |= 1 << pmc;
+ }
+
+ /* Extract PMC from DTS field */
+ for (i = 0; i < field_count; i++) {
+ struct dts_field_map *f = &field_maps[i];
+
+ if (f->is_pmc) {
+ pmc = extract_bits(event[0], f->bits_start, f->bits_end);
+ break;
+ }
+ }
+ for (ev = 0; ev < n_ev; ev++) {
+ pmc = pmc_arr[ev];
+
+ for (i = 0; i < field_count; i++) {
+ struct dts_field_map *f = &field_maps[i];
+ u64 val, shift;
+
+ val = extract_bits(event[ev], f->bits_start, f->bits_end);
+ if (f->is_pmc)
+ continue;
+
+ if (pmc == 5 || pmc == 6)
+ continue;
+
+ if (f->use_target_field_shift)
+ shift = f->target_field_base - (pmc * f->target_field_shift);
+ else
+ shift = f->pgm_start;
+
+ mmcr_val[f->mmcr] |= (val << shift);
+ }
+
+ /* MMCR2 privilege filtering */
+ if (pmc <= 6 && pevents && pevents[ev]) {
+
+ if (pevents[ev]->attr.exclude_user)
+ mmcr_val[2] |= MMCR2_FCP(pmc);
+
+ if (pevents[ev]->attr.exclude_hv)
+ mmcr_val[2] |= MMCR2_FCH(pmc);
+
+ if (pevents[ev]->attr.exclude_kernel) {
+ if (cpu_has_feature(CPU_FTR_HVMODE))
+ mmcr_val[2] |= MMCR2_FCH(pmc);
+ else
+ mmcr_val[2] |= MMCR2_FCS(pmc);
+ }
+ }
+ }
+
+ mmcr->mmcr0 = 0;
+
+ /* pmc_inuse is 1-based */
+ if (pmc_inuse & 2)
+ mmcr->mmcr0 = MMCR0_PMC1CE;
+
+ if (pmc_inuse & 0x7c)
+ mmcr->mmcr0 |= MMCR0_PMCjCE;
+
+ /* If we're not using PMC 5 or 6, freeze them */
+ if (!(pmc_inuse & 0x60))
+ mmcr->mmcr0 |= MMCR0_FC56;
+
+ /*
+ * Set mmcr0 (PMCCEXT) for p10 which
+ * will restrict access to group B registers
+ * when MMCR0 PMCC=0b00.
+ */
+ if (cpu_has_feature(CPU_FTR_ARCH_31))
+ mmcr->mmcr0 |= MMCR0_PMCCEXT;
+
+ /*
+ * Many places in core-book3s uses cpuhw->mmcr for enabling events
+ * Till we move away completely to DTS, maintain values in cpu->mmcr
+ */
+ mmcr->mmcr1 = mmcr_val[1];
+ mmcr->mmcra = mmcr_val[4];
+ mmcr->mmcr2 = mmcr_val[2];
+ mmcr->mmcr3 = mmcr_val[3];
+ return 0;
+}
+
void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr)
{
if (pmc <= 3)
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index f594fa6580d1..e9a7c5a39ec2 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -281,6 +281,8 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp,
int isa207_compute_mmcr(u64 event[], int n_ev,
unsigned int hwc[], struct mmcr_regs *mmcr,
struct perf_event *pevents[], u32 flags);
+int compute_mmcr_dts(u64 event[], int n_ev, unsigned int hwc[], struct mmcr_regs *mmcr,
+ struct perf_event *pevents[], u32 flags);
void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr);
int isa207_get_alternatives(u64 event, u64 alt[], int size, unsigned int flags,
const unsigned int ev_alt[][MAX_ALT]);
--
2.54.0
next prev parent reply other threads:[~2026-06-29 6:11 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-29 6:10 [RFC 0/3] powerpc/perf: Add Device Tree based PMU description framework Shivani Nittor
2026-06-29 6:10 ` [RFC 1/3] powerpc/perf: Register PMU from device tree and expose events Shivani Nittor
2026-06-29 6:10 ` Shivani Nittor [this message]
2026-06-29 6:10 ` [RFC 3/3] powerpc/perf: Add DTS-based event constraints Shivani Nittor
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260629061101.43119-3-shivani@linux.ibm.com \
--to=shivani@linux.ibm.com \
--cc=atrajeev@linux.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=tshah@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox