]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/perf/fsl_imx8_ddr_perf.c
Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / perf / fsl_imx8_ddr_perf.c
index ce7345745b42c655dc2a3c9c831b47bd896590e1..55083c67b2bb0775e55bec195ed6dc617d9819ff 100644 (file)
@@ -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)