diff options
Diffstat (limited to 'drivers/perf/fsl_imx8_ddr_perf.c')
| -rw-r--r-- | drivers/perf/fsl_imx8_ddr_perf.c | 124 | 
1 files changed, 103 insertions, 21 deletions
diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index ce7345745b42..55083c67b2bb 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -45,7 +45,8 @@  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			0x1     /* support AXI ID filter */ +#define DDR_CAP_AXI_ID_FILTER_ENHANCED		0x3     /* support enhanced AXI ID filter */  struct fsl_ddr_devtype_data {  	unsigned int quirks;    /* quirks needed for different DDR Perf core */ @@ -57,9 +58,14 @@ static const struct fsl_ddr_devtype_data imx8m_devtype_data = {  	.quirks = DDR_CAP_AXI_ID_FILTER,  }; +static const struct fsl_ddr_devtype_data imx8mp_devtype_data = { +	.quirks = DDR_CAP_AXI_ID_FILTER_ENHANCED, +}; +  static const struct of_device_id imx_ddr_pmu_dt_ids[] = {  	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},  	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data}, +	{ .compatible = "fsl,imx8mp-ddr-pmu", .data = &imx8mp_devtype_data},  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, imx_ddr_pmu_dt_ids); @@ -78,6 +84,61 @@ struct ddr_pmu {  	int id;  }; +enum ddr_perf_filter_capabilities { +	PERF_CAP_AXI_ID_FILTER = 0, +	PERF_CAP_AXI_ID_FILTER_ENHANCED, +	PERF_CAP_AXI_ID_FEAT_MAX, +}; + +static u32 ddr_perf_filter_cap_get(struct ddr_pmu *pmu, int cap) +{ +	u32 quirks = pmu->devtype_data->quirks; + +	switch (cap) { +	case PERF_CAP_AXI_ID_FILTER: +		return !!(quirks & DDR_CAP_AXI_ID_FILTER); +	case PERF_CAP_AXI_ID_FILTER_ENHANCED: +		quirks &= DDR_CAP_AXI_ID_FILTER_ENHANCED; +		return quirks == DDR_CAP_AXI_ID_FILTER_ENHANCED; +	default: +		WARN(1, "unknown filter cap %d\n", cap); +	} + +	return 0; +} + +static ssize_t ddr_perf_filter_cap_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct ddr_pmu *pmu = dev_get_drvdata(dev); +	struct dev_ext_attribute *ea = +		container_of(attr, struct dev_ext_attribute, attr); +	int cap = (long)ea->var; + +	return snprintf(buf, PAGE_SIZE, "%u\n", +			ddr_perf_filter_cap_get(pmu, cap)); +} + +#define PERF_EXT_ATTR_ENTRY(_name, _func, _var)				\ +	(&((struct dev_ext_attribute) {					\ +		__ATTR(_name, 0444, _func, NULL), (void *)_var		\ +	}).attr.attr) + +#define PERF_FILTER_EXT_ATTR_ENTRY(_name, _var)				\ +	PERF_EXT_ATTR_ENTRY(_name, ddr_perf_filter_cap_show, _var) + +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), +	NULL, +}; + +static struct attribute_group ddr_perf_filter_cap_attr_group = { +	.name = "caps", +	.attrs = ddr_perf_filter_cap_attr, +}; +  static ssize_t ddr_perf_cpumask_show(struct device *dev,  				struct device_attribute *attr, char *buf)  { @@ -175,9 +236,40 @@ static const struct attribute_group *attr_groups[] = {  	&ddr_perf_events_attr_group,  	&ddr_perf_format_attr_group,  	&ddr_perf_cpumask_attr_group, +	&ddr_perf_filter_cap_attr_group,  	NULL,  }; +static bool ddr_perf_is_filtered(struct perf_event *event) +{ +	return event->attr.config == 0x41 || event->attr.config == 0x42; +} + +static u32 ddr_perf_filter_val(struct perf_event *event) +{ +	return event->attr.config1; +} + +static bool ddr_perf_filters_compatible(struct perf_event *a, +					struct perf_event *b) +{ +	if (!ddr_perf_is_filtered(a)) +		return true; +	if (!ddr_perf_is_filtered(b)) +		return true; +	return ddr_perf_filter_val(a) == ddr_perf_filter_val(b); +} + +static bool ddr_perf_is_enhanced_filtered(struct perf_event *event) +{ +	unsigned int filt; +	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + +	filt = pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER_ENHANCED; +	return (filt == DDR_CAP_AXI_ID_FILTER_ENHANCED) && +		ddr_perf_is_filtered(event); +} +  static u32 ddr_perf_alloc_counter(struct ddr_pmu *pmu, int event)  {  	int i; @@ -209,27 +301,17 @@ static void ddr_perf_free_counter(struct ddr_pmu *pmu, int counter)  static u32 ddr_perf_read_counter(struct ddr_pmu *pmu, int counter)  { -	return readl_relaxed(pmu->base + COUNTER_READ + counter * 4); -} - -static bool ddr_perf_is_filtered(struct perf_event *event) -{ -	return event->attr.config == 0x41 || event->attr.config == 0x42; -} +	struct perf_event *event = pmu->events[counter]; +	void __iomem *base = pmu->base; -static u32 ddr_perf_filter_val(struct perf_event *event) -{ -	return event->attr.config1; -} - -static bool ddr_perf_filters_compatible(struct perf_event *a, -					struct perf_event *b) -{ -	if (!ddr_perf_is_filtered(a)) -		return true; -	if (!ddr_perf_is_filtered(b)) -		return true; -	return ddr_perf_filter_val(a) == ddr_perf_filter_val(b); +	/* +	 * return bytes instead of bursts from ddr transaction for +	 * axid-read and axid-write event if PMU core supports enhanced +	 * filter. +	 */ +	base += ddr_perf_is_enhanced_filtered(event) ? COUNTER_DPCR1 : +						       COUNTER_READ; +	return readl_relaxed(base + counter * 4);  }  static int ddr_perf_event_init(struct perf_event *event)  | 
