perf: imx8_ddr_perf: add AXI ID filter support
diff mbox series

Message ID 20190719075421.11093-1-qiangqing.zhang@nxp.com
State New
Headers show
Series
  • perf: imx8_ddr_perf: add AXI ID filter support
Related show

Commit Message

Joakim Zhang July 19, 2019, 7:56 a.m. UTC
AXI filtering is used by CSV modes 0x41 and 0x42 to count reads or
writes with an ARID or AXID matching filter setting. Granularity is at
subsystem level. Implementation does not allow filtring between masters
within a subsystem. Filter is defined with 2 configuration registers.

--AXI_ID defines AxID matching value
--AXI_MASKING defines which bits of AxID are meaningful for the matching

When non-masked bits are matching corresponding AXI_ID bits then counter
is incremented. This filter allows counting read or write access from a
subsystem or multiple subsystems.

Perf counter is incremented if AxID && AXI_MASKING == AXI_ID && AXI_MASKING

AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance counter.

e.g.
perf stat -a -e imx8_ddr0/axi-id-read,axi_id=0xMMMMDDDD/ cmd
MMMM: AXI_MASKING
DDDD: AXI_ID

Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
---
 drivers/perf/fsl_imx8_ddr_perf.c | 38 ++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

Comments

Robin Murphy July 19, 2019, 10:25 a.m. UTC | #1
On 19/07/2019 08:56, Joakim Zhang wrote:
> AXI filtering is used by CSV modes 0x41 and 0x42 to count reads or
> writes with an ARID or AXID matching filter setting. Granularity is at
> subsystem level. Implementation does not allow filtring between masters
> within a subsystem. Filter is defined with 2 configuration registers.
> 
> --AXI_ID defines AxID matching value
> --AXI_MASKING defines which bits of AxID are meaningful for the matching
> 
> When non-masked bits are matching corresponding AXI_ID bits then counter
> is incremented. This filter allows counting read or write access from a
> subsystem or multiple subsystems.
> 
> Perf counter is incremented if AxID && AXI_MASKING == AXI_ID && AXI_MASKING
> 
> AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance counter.
> 
> e.g.
> perf stat -a -e imx8_ddr0/axi-id-read,axi_id=0xMMMMDDDD/ cmd
> MMMM: AXI_MASKING
> DDDD: AXI_ID
> 
> Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
> ---
>   drivers/perf/fsl_imx8_ddr_perf.c | 38 ++++++++++++++++++++++++++++++--
>   1 file changed, 36 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c
> index 63fe21600072..cd05f12ed5c4 100644
> --- a/drivers/perf/fsl_imx8_ddr_perf.c
> +++ b/drivers/perf/fsl_imx8_ddr_perf.c
> @@ -42,9 +42,22 @@
>   
>   static DEFINE_IDA(ddr_ida);
>   
> +/* DDR Perf hardware feature */
> +#define DDR_CAP_AXI_ID_FILTER		0x1	/* support AXI ID filter */
> +
> +struct fsl_ddr_devtype_data {
> +	unsigned int quirks;	/* quirks needed for different DDR Perf core */
> +};
> +
> +static const struct fsl_ddr_devtype_data imx8_devtype_data;
> +
> +static const struct fsl_ddr_devtype_data imx8m_devtype_data = {
> +	.quirks = DDR_CAP_AXI_ID_FILTER,
> +};
> +
>   static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
> -	{ .compatible = "fsl,imx8-ddr-pmu",},
> -	{ .compatible = "fsl,imx8m-ddr-pmu",},
> +	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
> +	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
>   	{ /* sentinel */ }
>   };
>   
> @@ -57,6 +70,7 @@ struct ddr_pmu {
>   	struct perf_event *events[NUM_COUNTERS];
>   	int active_events;
>   	enum cpuhp_state cpuhp_state;
> +	const struct fsl_ddr_devtype_data *devtype_data;
>   	int irq;
>   	int id;
>   };
> @@ -128,6 +142,8 @@ static struct attribute *ddr_perf_events_attrs[] = {
>   	IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37),
>   	IMX8_DDR_PMU_EVENT_ATTR(write, 0x38),
>   	IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39),
> +	IMX8_DDR_PMU_EVENT_ATTR(axi-id-read, 0x41),
> +	IMX8_DDR_PMU_EVENT_ATTR(axi-id-write, 0x42),
>   	NULL,
>   };
>   
> @@ -137,9 +153,11 @@ static struct attribute_group ddr_perf_events_attr_group = {
>   };
>   
>   PMU_FORMAT_ATTR(event, "config:0-7");
> +PMU_FORMAT_ATTR(axi_id, "config1:0-31");
>   
>   static struct attribute *ddr_perf_format_attrs[] = {
>   	&format_attr_event.attr,
> +	&format_attr_axi_id.attr,
>   	NULL,
>   };
>   
> @@ -274,6 +292,15 @@ static void ddr_perf_event_start(struct perf_event *event, int flags)
>   	struct hw_perf_event *hwc = &event->hw;
>   	int counter = hwc->idx;
>   
> +	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
> +		if (event->attr.config == 0x41 ||
> +		    event->attr.config == 0x42) {
> +			int val = event->attr.config1;
> +
> +			writel(val, pmu->base + COUNTER_DPCR1);

As was touched upon in the original driver review, if this filter is 
shared between counters, then what happens if the user specifies 
EVENT_AXI_READ and EVENT_AXI_WRITE at the same time but with different 
masks?

> +		}
> +	}
> +
>   	local64_set(&hwc->prev_count, 0);
>   
>   	ddr_perf_counter_enable(pmu, event->attr.config, counter, true);
> @@ -445,6 +472,7 @@ static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
>   
>   static int ddr_perf_probe(struct platform_device *pdev)
>   {
> +	const struct of_device_id *of_id;
>   	struct ddr_pmu *pmu;
>   	struct device_node *np;
>   	void __iomem *base;
> @@ -472,6 +500,12 @@ static int ddr_perf_probe(struct platform_device *pdev)
>   	if (!name)
>   		return -ENOMEM;
>   
> +	of_id = of_match_device(imx_ddr_pmu_dt_ids, &pdev->dev);
> +	if (of_id)
> +		pmu->devtype_data = of_id->data;

Use of_device_get_match_data()...

> +	else
> +		return -ENODEV;

...and either way this should be impossible - if there was no match, the 
OF code would never have called our probe routine in the first place.

Robin.

> +
>   	pmu->cpu = raw_smp_processor_id();
>   	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
>   				      DDR_CPUHP_CB_NAME,
>
Joakim Zhang July 25, 2019, 7:15 a.m. UTC | #2
> -----Original Message-----
> From: Robin Murphy <robin.murphy@arm.com>
> Sent: 2019年7月19日 18:26
> To: Joakim Zhang <qiangqing.zhang@nxp.com>; will@kernel.org;
> mark.rutland@arm.com; Frank Li <frank.li@nxp.com>
> Cc: linux-arm-kernel@lists.infradead.org; dl-linux-imx <linux-imx@nxp.com>;
> kernel@pengutronix.de
> Subject: Re: [PATCH] perf: imx8_ddr_perf: add AXI ID filter support
> 
> On 19/07/2019 08:56, Joakim Zhang wrote:
> > AXI filtering is used by CSV modes 0x41 and 0x42 to count reads or
> > writes with an ARID or AXID matching filter setting. Granularity is at
> > subsystem level. Implementation does not allow filtring between
> > masters within a subsystem. Filter is defined with 2 configuration registers.
> >
> > --AXI_ID defines AxID matching value
> > --AXI_MASKING defines which bits of AxID are meaningful for the
> > matching
> >
> > When non-masked bits are matching corresponding AXI_ID bits then
> > counter is incremented. This filter allows counting read or write
> > access from a subsystem or multiple subsystems.
> >
> > Perf counter is incremented if AxID && AXI_MASKING == AXI_ID &&
> > AXI_MASKING
> >
> > AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance
> counter.
> >
> > e.g.
> > perf stat -a -e imx8_ddr0/axi-id-read,axi_id=0xMMMMDDDD/ cmd
> > MMMM: AXI_MASKING
> > DDDD: AXI_ID
> >
> > Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
> > ---
> >   drivers/perf/fsl_imx8_ddr_perf.c | 38
> ++++++++++++++++++++++++++++++--
> >   1 file changed, 36 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/perf/fsl_imx8_ddr_perf.c
> > b/drivers/perf/fsl_imx8_ddr_perf.c
> > index 63fe21600072..cd05f12ed5c4 100644
> > --- a/drivers/perf/fsl_imx8_ddr_perf.c
> > +++ b/drivers/perf/fsl_imx8_ddr_perf.c
> > @@ -42,9 +42,22 @@
> >
> >   static DEFINE_IDA(ddr_ida);
> >
> > +/* DDR Perf hardware feature */
> > +#define DDR_CAP_AXI_ID_FILTER		0x1	/* support AXI ID filter */
> > +
> > +struct fsl_ddr_devtype_data {
> > +	unsigned int quirks;	/* quirks needed for different DDR Perf core */
> > +};
> > +
> > +static const struct fsl_ddr_devtype_data imx8_devtype_data;
> > +
> > +static const struct fsl_ddr_devtype_data imx8m_devtype_data = {
> > +	.quirks = DDR_CAP_AXI_ID_FILTER,
> > +};
> > +
> >   static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
> > -	{ .compatible = "fsl,imx8-ddr-pmu",},
> > -	{ .compatible = "fsl,imx8m-ddr-pmu",},
> > +	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
> > +	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
> >   	{ /* sentinel */ }
> >   };
> >
> > @@ -57,6 +70,7 @@ struct ddr_pmu {
> >   	struct perf_event *events[NUM_COUNTERS];
> >   	int active_events;
> >   	enum cpuhp_state cpuhp_state;
> > +	const struct fsl_ddr_devtype_data *devtype_data;
> >   	int irq;
> >   	int id;
> >   };
> > @@ -128,6 +142,8 @@ static struct attribute *ddr_perf_events_attrs[] = {
> >   	IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37),
> >   	IMX8_DDR_PMU_EVENT_ATTR(write, 0x38),
> >   	IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39),
> > +	IMX8_DDR_PMU_EVENT_ATTR(axi-id-read, 0x41),
> > +	IMX8_DDR_PMU_EVENT_ATTR(axi-id-write, 0x42),
> >   	NULL,
> >   };
> >
> > @@ -137,9 +153,11 @@ static struct attribute_group
> ddr_perf_events_attr_group = {
> >   };
> >
> >   PMU_FORMAT_ATTR(event, "config:0-7");
> > +PMU_FORMAT_ATTR(axi_id, "config1:0-31");
> >
> >   static struct attribute *ddr_perf_format_attrs[] = {
> >   	&format_attr_event.attr,
> > +	&format_attr_axi_id.attr,
> >   	NULL,
> >   };
> >
> > @@ -274,6 +292,15 @@ static void ddr_perf_event_start(struct perf_event
> *event, int flags)
> >   	struct hw_perf_event *hwc = &event->hw;
> >   	int counter = hwc->idx;
> >
> > +	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
> > +		if (event->attr.config == 0x41 ||
> > +		    event->attr.config == 0x42) {
> > +			int val = event->attr.config1;
> > +
> > +			writel(val, pmu->base + COUNTER_DPCR1);
> 
> As was touched upon in the original driver review, if this filter is shared
> between counters, then what happens if the user specifies EVENT_AXI_READ
> and EVENT_AXI_WRITE at the same time but with different masks?

Hi Robin,

Yes, you are right. It is actually an issue. I will add limitation for EVENT_AXI_READ and EVENT_AXI_WRITE. It will show error log if user specifies these two filter event at the same time.

> > +		}
> > +	}
> > +
> >   	local64_set(&hwc->prev_count, 0);
> >
> >   	ddr_perf_counter_enable(pmu, event->attr.config, counter, true);
> @@
> > -445,6 +472,7 @@ static int ddr_perf_offline_cpu(unsigned int cpu,
> > struct hlist_node *node)
> >
> >   static int ddr_perf_probe(struct platform_device *pdev)
> >   {
> > +	const struct of_device_id *of_id;
> >   	struct ddr_pmu *pmu;
> >   	struct device_node *np;
> >   	void __iomem *base;
> > @@ -472,6 +500,12 @@ static int ddr_perf_probe(struct platform_device
> *pdev)
> >   	if (!name)
> >   		return -ENOMEM;
> >
> > +	of_id = of_match_device(imx_ddr_pmu_dt_ids, &pdev->dev);
> > +	if (of_id)
> > +		pmu->devtype_data = of_id->data;
> 
> Use of_device_get_match_data()...
> 
> > +	else
> > +		return -ENODEV;
> 
> ...and either way this should be impossible - if there was no match, the OF code
> would never have called our probe routine in the first place.

Okay, I will use of_device_get_match_data() instead of of_match_device, and remove the check of the return value.

Best Regards,
Joakim Zhang
> Robin.
> 
> > +
> >   	pmu->cpu = raw_smp_processor_id();
> >   	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
> >   				      DDR_CPUHP_CB_NAME,
> >

Patch
diff mbox series

diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c
index 63fe21600072..cd05f12ed5c4 100644
--- a/drivers/perf/fsl_imx8_ddr_perf.c
+++ b/drivers/perf/fsl_imx8_ddr_perf.c
@@ -42,9 +42,22 @@ 
 
 static DEFINE_IDA(ddr_ida);
 
+/* DDR Perf hardware feature */
+#define DDR_CAP_AXI_ID_FILTER		0x1	/* support AXI ID filter */
+
+struct fsl_ddr_devtype_data {
+	unsigned int quirks;	/* quirks needed for different DDR Perf core */
+};
+
+static const struct fsl_ddr_devtype_data imx8_devtype_data;
+
+static const struct fsl_ddr_devtype_data imx8m_devtype_data = {
+	.quirks = DDR_CAP_AXI_ID_FILTER,
+};
+
 static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
-	{ .compatible = "fsl,imx8-ddr-pmu",},
-	{ .compatible = "fsl,imx8m-ddr-pmu",},
+	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
+	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
 	{ /* sentinel */ }
 };
 
@@ -57,6 +70,7 @@  struct ddr_pmu {
 	struct perf_event *events[NUM_COUNTERS];
 	int active_events;
 	enum cpuhp_state cpuhp_state;
+	const struct fsl_ddr_devtype_data *devtype_data;
 	int irq;
 	int id;
 };
@@ -128,6 +142,8 @@  static struct attribute *ddr_perf_events_attrs[] = {
 	IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37),
 	IMX8_DDR_PMU_EVENT_ATTR(write, 0x38),
 	IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39),
+	IMX8_DDR_PMU_EVENT_ATTR(axi-id-read, 0x41),
+	IMX8_DDR_PMU_EVENT_ATTR(axi-id-write, 0x42),
 	NULL,
 };
 
@@ -137,9 +153,11 @@  static struct attribute_group ddr_perf_events_attr_group = {
 };
 
 PMU_FORMAT_ATTR(event, "config:0-7");
+PMU_FORMAT_ATTR(axi_id, "config1:0-31");
 
 static struct attribute *ddr_perf_format_attrs[] = {
 	&format_attr_event.attr,
+	&format_attr_axi_id.attr,
 	NULL,
 };
 
@@ -274,6 +292,15 @@  static void ddr_perf_event_start(struct perf_event *event, int flags)
 	struct hw_perf_event *hwc = &event->hw;
 	int counter = hwc->idx;
 
+	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
+		if (event->attr.config == 0x41 ||
+		    event->attr.config == 0x42) {
+			int val = event->attr.config1;
+
+			writel(val, pmu->base + COUNTER_DPCR1);
+		}
+	}
+
 	local64_set(&hwc->prev_count, 0);
 
 	ddr_perf_counter_enable(pmu, event->attr.config, counter, true);
@@ -445,6 +472,7 @@  static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
 
 static int ddr_perf_probe(struct platform_device *pdev)
 {
+	const struct of_device_id *of_id;
 	struct ddr_pmu *pmu;
 	struct device_node *np;
 	void __iomem *base;
@@ -472,6 +500,12 @@  static int ddr_perf_probe(struct platform_device *pdev)
 	if (!name)
 		return -ENOMEM;
 
+	of_id = of_match_device(imx_ddr_pmu_dt_ids, &pdev->dev);
+	if (of_id)
+		pmu->devtype_data = of_id->data;
+	else
+		return -ENODEV;
+
 	pmu->cpu = raw_smp_processor_id();
 	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
 				      DDR_CPUHP_CB_NAME,