perf: fsl_imx8_ddr: Add AXI ID PORT CHANNEL filter support
authorXu Yang <xu.yang_2@nxp.com>
Mon, 20 Nov 2023 09:33:13 +0000 (17:33 +0800)
committerWill Deacon <will@kernel.org>
Tue, 5 Dec 2023 14:12:07 +0000 (14:12 +0000)
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>
drivers/perf/fsl_imx8_ddr_perf.c

index 92611c98120f12bd4cd3dcbd1d24d2b67e95098d..d0eae2d7e64bb855f7507c601896e5ef8794b27a 100644 (file)
@@ -19,6 +19,8 @@
 #define COUNTER_READ           0x20
 
 #define COUNTER_DPCR1          0x30
+#define COUNTER_MUX_CNTL       0x50
+#define COUNTER_MASK_COMP      0x54
 
 #define CNTL_OVER              0x1
 #define CNTL_CLEAR             0x2
 #define CNTL_CSV_SHIFT         24
 #define CNTL_CSV_MASK          (0xFFU << CNTL_CSV_SHIFT)
 
+#define READ_PORT_SHIFT                0
+#define READ_PORT_MASK         (0x7 << READ_PORT_SHIFT)
+#define READ_CHANNEL_REVERT    0x00000008      /* bit 3 for read channel select */
+#define WRITE_PORT_SHIFT       8
+#define WRITE_PORT_MASK                (0x7 << WRITE_PORT_SHIFT)
+#define WRITE_CHANNEL_REVERT   0x00000800      /* bit 11 for write channel select */
+
 #define EVENT_CYCLES_ID                0
 #define EVENT_CYCLES_COUNTER   0
 #define NUM_COUNTERS           4
@@ -50,6 +59,7 @@ static DEFINE_IDA(ddr_ida);
 /* DDR Perf hardware feature */
 #define DDR_CAP_AXI_ID_FILTER                  0x1     /* support AXI ID filter */
 #define DDR_CAP_AXI_ID_FILTER_ENHANCED         0x3     /* support enhanced AXI ID filter */
+#define DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER     0x4     /* support AXI ID PORT CHANNEL filter */
 
 struct fsl_ddr_devtype_data {
        unsigned int quirks;    /* quirks needed for different DDR Perf core */
@@ -144,6 +154,7 @@ static const struct attribute_group ddr_perf_identifier_attr_group = {
 enum ddr_perf_filter_capabilities {
        PERF_CAP_AXI_ID_FILTER = 0,
        PERF_CAP_AXI_ID_FILTER_ENHANCED,
+       PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER,
        PERF_CAP_AXI_ID_FEAT_MAX,
 };
 
@@ -157,6 +168,8 @@ static u32 ddr_perf_filter_cap_get(struct ddr_pmu *pmu, int cap)
        case PERF_CAP_AXI_ID_FILTER_ENHANCED:
                quirks &= DDR_CAP_AXI_ID_FILTER_ENHANCED;
                return quirks == DDR_CAP_AXI_ID_FILTER_ENHANCED;
+       case PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER:
+               return !!(quirks & DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER);
        default:
                WARN(1, "unknown filter cap %d\n", cap);
        }
@@ -187,6 +200,7 @@ static ssize_t ddr_perf_filter_cap_show(struct device *dev,
 static struct attribute *ddr_perf_filter_cap_attr[] = {
        PERF_FILTER_EXT_ATTR_ENTRY(filter, PERF_CAP_AXI_ID_FILTER),
        PERF_FILTER_EXT_ATTR_ENTRY(enhanced_filter, PERF_CAP_AXI_ID_FILTER_ENHANCED),
+       PERF_FILTER_EXT_ATTR_ENTRY(super_filter, PERF_CAP_AXI_ID_PORT_CHANNEL_FILTER),
        NULL,
 };
 
@@ -272,11 +286,15 @@ static const struct attribute_group ddr_perf_events_attr_group = {
 PMU_FORMAT_ATTR(event, "config:0-7");
 PMU_FORMAT_ATTR(axi_id, "config1:0-15");
 PMU_FORMAT_ATTR(axi_mask, "config1:16-31");
+PMU_FORMAT_ATTR(axi_port, "config2:0-2");
+PMU_FORMAT_ATTR(axi_channel, "config2:3-3");
 
 static struct attribute *ddr_perf_format_attrs[] = {
        &format_attr_event.attr,
        &format_attr_axi_id.attr,
        &format_attr_axi_mask.attr,
+       &format_attr_axi_port.attr,
+       &format_attr_axi_channel.attr,
        NULL,
 };
 
@@ -530,6 +548,7 @@ static int ddr_perf_event_add(struct perf_event *event, int flags)
        int counter;
        int cfg = event->attr.config;
        int cfg1 = event->attr.config1;
+       int cfg2 = event->attr.config2;
 
        if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
                int i;
@@ -553,6 +572,26 @@ static int ddr_perf_event_add(struct perf_event *event, int flags)
                return -EOPNOTSUPP;
        }
 
+       if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER) {
+               if (ddr_perf_is_filtered(event)) {
+                       /* revert axi id masking(axi_mask) value */
+                       cfg1 ^= AXI_MASKING_REVERT;
+                       writel(cfg1, pmu->base + COUNTER_MASK_COMP + ((counter - 1) << 4));
+
+                       if (cfg == 0x41) {
+                               /* revert axi read channel(axi_channel) value */
+                               cfg2 ^= READ_CHANNEL_REVERT;
+                               cfg2 |= FIELD_PREP(READ_PORT_MASK, cfg2);
+                       } else {
+                               /* revert axi write channel(axi_channel) value */
+                               cfg2 ^= WRITE_CHANNEL_REVERT;
+                               cfg2 |= FIELD_PREP(WRITE_PORT_MASK, cfg2);
+                       }
+
+                       writel(cfg2, pmu->base + COUNTER_MUX_CNTL + ((counter - 1) << 4));
+               }
+       }
+
        pmu->events[counter] = event;
        hwc->idx = counter;