Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf: fsl_imx8_ddr: Add AXI ID PORT CHANNEL filter support

This is the extension of AXI ID filter.

Filter is defined with 2 configuration registers per counter 1-3 (counter
0 is not used for filtering and lacks these registers).
* Counter N MASK COMP register - AXI_ID and AXI_MASKING.
* Counter N MUX CNTL register - AXI CHANNEL and AXI PORT.
-- 0: address channel
-- 1: data channel

This filter is exposed to userspace as an additional (channel, port) pair.
The definition of axi_channel is inverted in userspace, and it will be
reverted in driver automatically.

AXI filter of Perf Monitor in DDR Subsystem, only a single port0 exist, so
axi_port is reserved which should be 0.

e.g.
perf stat -a -e imx8_ddr0/axid-read,axi_mask=0xMMMM,axi_id=0xDDDD,axi_channel=0xH/ cmd
perf stat -a -e imx8_ddr0/axid-write,axi_mask=0xMMMM,axi_id=0xDDDD,axi_channel=0xH/ cmd

Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20231120093317.2652866-1-xu.yang_2@nxp.com
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Xu Yang and committed by
Will Deacon
afd83967 118eb89b

+39
+39
drivers/perf/fsl_imx8_ddr_perf.c
··· 19 19 #define COUNTER_READ 0x20 20 20 21 21 #define COUNTER_DPCR1 0x30 22 + #define COUNTER_MUX_CNTL 0x50 23 + #define COUNTER_MASK_COMP 0x54 22 24 23 25 #define CNTL_OVER 0x1 24 26 #define CNTL_CLEAR 0x2 ··· 33 31 #define CNTL_CP_MASK (0xFF << CNTL_CP_SHIFT) 34 32 #define CNTL_CSV_SHIFT 24 35 33 #define CNTL_CSV_MASK (0xFFU << CNTL_CSV_SHIFT) 34 + 35 + #define READ_PORT_SHIFT 0 36 + #define READ_PORT_MASK (0x7 << READ_PORT_SHIFT) 37 + #define READ_CHANNEL_REVERT 0x00000008 /* bit 3 for read channel select */ 38 + #define WRITE_PORT_SHIFT 8 39 + #define WRITE_PORT_MASK (0x7 << WRITE_PORT_SHIFT) 40 + #define WRITE_CHANNEL_REVERT 0x00000800 /* bit 11 for write channel select */ 36 41 37 42 #define EVENT_CYCLES_ID 0 38 43 #define EVENT_CYCLES_COUNTER 0 ··· 59 50 /* DDR Perf hardware feature */ 60 51 #define DDR_CAP_AXI_ID_FILTER 0x1 /* support AXI ID filter */ 61 52 #define DDR_CAP_AXI_ID_FILTER_ENHANCED 0x3 /* support enhanced AXI ID filter */ 53 + #define DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER 0x4 /* support AXI ID PORT CHANNEL filter */ 62 54 63 55 struct fsl_ddr_devtype_data { 64 56 unsigned int quirks; /* quirks needed for different DDR Perf core */ ··· 154 144 enum ddr_perf_filter_capabilities { 155 145 PERF_CAP_AXI_ID_FILTER = 0, 156 146 PERF_CAP_AXI_ID_FILTER_ENHANCED, 147 + PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER, 157 148 PERF_CAP_AXI_ID_FEAT_MAX, 158 149 }; 159 150 ··· 168 157 case PERF_CAP_AXI_ID_FILTER_ENHANCED: 169 158 quirks &= DDR_CAP_AXI_ID_FILTER_ENHANCED; 170 159 return quirks == DDR_CAP_AXI_ID_FILTER_ENHANCED; 160 + case PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER: 161 + return !!(quirks & DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER); 171 162 default: 172 163 WARN(1, "unknown filter cap %d\n", cap); 173 164 } ··· 200 187 static struct attribute *ddr_perf_filter_cap_attr[] = { 201 188 PERF_FILTER_EXT_ATTR_ENTRY(filter, PERF_CAP_AXI_ID_FILTER), 202 189 PERF_FILTER_EXT_ATTR_ENTRY(enhanced_filter, PERF_CAP_AXI_ID_FILTER_ENHANCED), 190 + PERF_FILTER_EXT_ATTR_ENTRY(super_filter, PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER), 203 191 NULL, 204 192 }; 205 193 ··· 286 272 PMU_FORMAT_ATTR(event, "config:0-7"); 287 273 PMU_FORMAT_ATTR(axi_id, "config1:0-15"); 288 274 PMU_FORMAT_ATTR(axi_mask, "config1:16-31"); 275 + PMU_FORMAT_ATTR(axi_port, "config2:0-2"); 276 + PMU_FORMAT_ATTR(axi_channel, "config2:3-3"); 289 277 290 278 static struct attribute *ddr_perf_format_attrs[] = { 291 279 &format_attr_event.attr, 292 280 &format_attr_axi_id.attr, 293 281 &format_attr_axi_mask.attr, 282 + &format_attr_axi_port.attr, 283 + &format_attr_axi_channel.attr, 294 284 NULL, 295 285 }; 296 286 ··· 548 530 int counter; 549 531 int cfg = event->attr.config; 550 532 int cfg1 = event->attr.config1; 533 + int cfg2 = event->attr.config2; 551 534 552 535 if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) { 553 536 int i; ··· 570 551 if (counter < 0) { 571 552 dev_dbg(pmu->dev, "There are not enough counters\n"); 572 553 return -EOPNOTSUPP; 554 + } 555 + 556 + if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER) { 557 + if (ddr_perf_is_filtered(event)) { 558 + /* revert axi id masking(axi_mask) value */ 559 + cfg1 ^= AXI_MASKING_REVERT; 560 + writel(cfg1, pmu->base + COUNTER_MASK_COMP + ((counter - 1) << 4)); 561 + 562 + if (cfg == 0x41) { 563 + /* revert axi read channel(axi_channel) value */ 564 + cfg2 ^= READ_CHANNEL_REVERT; 565 + cfg2 |= FIELD_PREP(READ_PORT_MASK, cfg2); 566 + } else { 567 + /* revert axi write channel(axi_channel) value */ 568 + cfg2 ^= WRITE_CHANNEL_REVERT; 569 + cfg2 |= FIELD_PREP(WRITE_PORT_MASK, cfg2); 570 + } 571 + 572 + writel(cfg2, pmu->base + COUNTER_MUX_CNTL + ((counter - 1) << 4)); 573 + } 573 574 } 574 575 575 576 pmu->events[counter] = event;