mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
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>
This commit is contained in:
parent
118eb89b1e
commit
afd83967e7
@ -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
|
||||
@ -32,6 +34,13 @@
|
||||
#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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user