Support counters of the L2 cache crossbar connect.

Signed-off-by: Jan Glauber <jglau...@cavium.com>
---
 drivers/perf/uncore/Makefile                |   3 +-
 drivers/perf/uncore/uncore_cavium.c         |   3 +
 drivers/perf/uncore/uncore_cavium.h         |   4 +
 drivers/perf/uncore/uncore_cavium_l2c_cbc.c | 237 ++++++++++++++++++++++++++++
 4 files changed, 246 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_cbc.c

diff --git a/drivers/perf/uncore/Makefile b/drivers/perf/uncore/Makefile
index 6a16caf..d52ecc9 100644
--- a/drivers/perf/uncore/Makefile
+++ b/drivers/perf/uncore/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o          \
-                             uncore_cavium_l2c_tad.o
+                             uncore_cavium_l2c_tad.o   \
+                             uncore_cavium_l2c_cbc.o
diff --git a/drivers/perf/uncore/uncore_cavium.c 
b/drivers/perf/uncore/uncore_cavium.c
index b92b2ae..a230450 100644
--- a/drivers/perf/uncore/uncore_cavium.c
+++ b/drivers/perf/uncore/uncore_cavium.c
@@ -17,6 +17,8 @@ struct thunder_uncore *event_to_thunder_uncore(struct 
perf_event *event)
 {
        if (event->pmu->type == thunder_l2c_tad_pmu.type)
                return thunder_uncore_l2c_tad;
+       else if (event->pmu->type == thunder_l2c_cbc_pmu.type)
+               return thunder_uncore_l2c_cbc;
        else
                return NULL;
 }
@@ -300,6 +302,7 @@ static int __init thunder_uncore_init(void)
        pr_info("PMU version: %d\n", thunder_uncore_version);
 
        thunder_uncore_l2c_tad_setup();
+       thunder_uncore_l2c_cbc_setup();
        return 0;
 }
 late_initcall(thunder_uncore_init);
diff --git a/drivers/perf/uncore/uncore_cavium.h 
b/drivers/perf/uncore/uncore_cavium.h
index 7a9c367..94bd02c 100644
--- a/drivers/perf/uncore/uncore_cavium.h
+++ b/drivers/perf/uncore/uncore_cavium.h
@@ -8,6 +8,7 @@
 
 enum uncore_type {
        L2C_TAD_TYPE,
+       L2C_CBC_TYPE,
 };
 
 extern int thunder_uncore_version;
@@ -66,7 +67,9 @@ extern struct attribute_group thunder_uncore_attr_group;
 extern struct device_attribute format_attr_node;
 
 extern struct thunder_uncore *thunder_uncore_l2c_tad;
+extern struct thunder_uncore *thunder_uncore_l2c_cbc;
 extern struct pmu thunder_l2c_tad_pmu;
+extern struct pmu thunder_l2c_cbc_pmu;
 
 /* Prototypes */
 struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event);
@@ -81,3 +84,4 @@ ssize_t thunder_events_sysfs_show(struct device *dev,
                                  char *page);
 
 int thunder_uncore_l2c_tad_setup(void);
+int thunder_uncore_l2c_cbc_setup(void);
diff --git a/drivers/perf/uncore/uncore_cavium_l2c_cbc.c 
b/drivers/perf/uncore/uncore_cavium_l2c_cbc.c
new file mode 100644
index 0000000..bde7a51
--- /dev/null
+++ b/drivers/perf/uncore/uncore_cavium_l2c_cbc.c
@@ -0,0 +1,237 @@
+/*
+ * Cavium Thunder uncore PMU support, L2C CBC counters.
+ *
+ * Copyright 2016 Cavium Inc.
+ * Author: Jan Glauber <jan.glau...@cavium.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+
+#include "uncore_cavium.h"
+
+#ifndef PCI_DEVICE_ID_THUNDER_L2C_CBC
+#define PCI_DEVICE_ID_THUNDER_L2C_CBC  0xa02f
+#endif
+
+#define L2C_CBC_NR_COUNTERS             16
+
+/* L2C CBC event list */
+#define L2C_CBC_EVENT_XMC0             0x00
+#define L2C_CBC_EVENT_XMD0             0x01
+#define L2C_CBC_EVENT_RSC0             0x02
+#define L2C_CBC_EVENT_RSD0             0x03
+#define L2C_CBC_EVENT_INV0             0x04
+#define L2C_CBC_EVENT_IOC0             0x05
+#define L2C_CBC_EVENT_IOR0             0x06
+
+#define L2C_CBC_EVENT_XMC1             0x08    /* 0x40 */
+#define L2C_CBC_EVENT_XMD1             0x09
+#define L2C_CBC_EVENT_RSC1             0x0a
+#define L2C_CBC_EVENT_RSD1             0x0b
+#define L2C_CBC_EVENT_INV1             0x0c
+
+#define L2C_CBC_EVENT_XMC2             0x10    /* 0x80 */
+#define L2C_CBC_EVENT_XMD2             0x11
+#define L2C_CBC_EVENT_RSC2             0x12
+#define L2C_CBC_EVENT_RSD2             0x13
+
+struct thunder_uncore *thunder_uncore_l2c_cbc;
+
+int l2c_cbc_events[L2C_CBC_NR_COUNTERS] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c,
+       0x10, 0x11, 0x12, 0x13
+};
+
+static void thunder_uncore_start(struct perf_event *event, int flags)
+{
+       struct thunder_uncore *uncore = event_to_thunder_uncore(event);
+       struct hw_perf_event *hwc = &event->hw;
+       struct thunder_uncore_node *node;
+       struct thunder_uncore_unit *unit;
+       u64 prev;
+
+       node = get_node(hwc->config, uncore);
+
+       /* restore counter value divided by units into all counters */
+       if (flags & PERF_EF_RELOAD) {
+               prev = local64_read(&hwc->prev_count);
+               prev = prev / node->nr_units;
+
+               list_for_each_entry(unit, &node->unit_list, entry)
+                       writeq(prev, hwc->event_base + unit->map);
+       }
+
+       hwc->state = 0;
+       perf_event_update_userpage(event);
+}
+
+static void thunder_uncore_stop(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+               thunder_uncore_read(event);
+               hwc->state |= PERF_HES_UPTODATE;
+       }
+}
+
+static int thunder_uncore_add(struct perf_event *event, int flags)
+{
+       struct thunder_uncore *uncore = event_to_thunder_uncore(event);
+       struct hw_perf_event *hwc = &event->hw;
+       struct thunder_uncore_node *node;
+       int id, i;
+
+       WARN_ON_ONCE(!uncore);
+       node = get_node(hwc->config, uncore);
+       id = get_id(hwc->config);
+
+       /* are we already assigned? */
+       if (hwc->idx != -1 && node->events[hwc->idx] == event)
+               goto out;
+
+       for (i = 0; i < node->num_counters; i++) {
+               if (node->events[i] == event) {
+                       hwc->idx = i;
+                       goto out;
+               }
+       }
+
+       /* these counters are self-sustained so idx must match the counter! */
+       hwc->idx = -1;
+       for (i = 0; i < node->num_counters; i++) {
+               if (l2c_cbc_events[i] == id) {
+                       if (cmpxchg(&node->events[i], NULL, event) == NULL) {
+                               hwc->idx = i;
+                               break;
+                       }
+               }
+       }
+
+out:
+       if (hwc->idx == -1)
+               return -EBUSY;
+
+       hwc->event_base = id * sizeof(unsigned long long);
+
+       /* counter is not stoppable so avoiding PERF_HES_STOPPED */
+       hwc->state = PERF_HES_UPTODATE;
+
+       if (flags & PERF_EF_START)
+               thunder_uncore_start(event, 0);
+
+       return 0;
+}
+
+PMU_FORMAT_ATTR(event, "config:0-4");
+
+static struct attribute *thunder_l2c_cbc_format_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_node.attr,
+       NULL,
+};
+
+static struct attribute_group thunder_l2c_cbc_format_group = {
+       .name = "format",
+       .attrs = thunder_l2c_cbc_format_attr,
+};
+
+EVENT_ATTR(xmc0,       L2C_CBC_EVENT_XMC0);
+EVENT_ATTR(xmd0,       L2C_CBC_EVENT_XMD0);
+EVENT_ATTR(rsc0,       L2C_CBC_EVENT_RSC0);
+EVENT_ATTR(rsd0,       L2C_CBC_EVENT_RSD0);
+EVENT_ATTR(inv0,       L2C_CBC_EVENT_INV0);
+EVENT_ATTR(ioc0,       L2C_CBC_EVENT_IOC0);
+EVENT_ATTR(ior0,       L2C_CBC_EVENT_IOR0);
+EVENT_ATTR(xmc1,       L2C_CBC_EVENT_XMC1);
+EVENT_ATTR(xmd1,       L2C_CBC_EVENT_XMD1);
+EVENT_ATTR(rsc1,       L2C_CBC_EVENT_RSC1);
+EVENT_ATTR(rsd1,       L2C_CBC_EVENT_RSD1);
+EVENT_ATTR(inv1,       L2C_CBC_EVENT_INV1);
+EVENT_ATTR(xmc2,       L2C_CBC_EVENT_XMC2);
+EVENT_ATTR(xmd2,       L2C_CBC_EVENT_XMD2);
+EVENT_ATTR(rsc2,       L2C_CBC_EVENT_RSC2);
+EVENT_ATTR(rsd2,       L2C_CBC_EVENT_RSD2);
+
+static struct attribute *thunder_l2c_cbc_events_attr[] = {
+       EVENT_PTR(xmc0),
+       EVENT_PTR(xmd0),
+       EVENT_PTR(rsc0),
+       EVENT_PTR(rsd0),
+       EVENT_PTR(inv0),
+       EVENT_PTR(ioc0),
+       EVENT_PTR(ior0),
+       EVENT_PTR(xmc1),
+       EVENT_PTR(xmd1),
+       EVENT_PTR(rsc1),
+       EVENT_PTR(rsd1),
+       EVENT_PTR(inv1),
+       EVENT_PTR(xmc2),
+       EVENT_PTR(xmd2),
+       EVENT_PTR(rsc2),
+       EVENT_PTR(rsd2),
+       NULL,
+};
+
+static struct attribute_group thunder_l2c_cbc_events_group = {
+       .name = "events",
+       .attrs = thunder_l2c_cbc_events_attr,
+};
+
+static const struct attribute_group *thunder_l2c_cbc_attr_groups[] = {
+       &thunder_uncore_attr_group,
+       &thunder_l2c_cbc_format_group,
+       &thunder_l2c_cbc_events_group,
+       NULL,
+};
+
+struct pmu thunder_l2c_cbc_pmu = {
+       .attr_groups    = thunder_l2c_cbc_attr_groups,
+       .name           = "thunder_l2c_cbc",
+       .event_init     = thunder_uncore_event_init,
+       .add            = thunder_uncore_add,
+       .del            = thunder_uncore_del,
+       .start          = thunder_uncore_start,
+       .stop           = thunder_uncore_stop,
+       .read           = thunder_uncore_read,
+};
+
+static int event_valid(u64 config)
+{
+       if (config <= L2C_CBC_EVENT_IOR0 ||
+           (config >= L2C_CBC_EVENT_XMC1 && config <= L2C_CBC_EVENT_INV1) ||
+           (config >= L2C_CBC_EVENT_XMC2 && config <= L2C_CBC_EVENT_RSD2))
+               return 1;
+       else
+               return 0;
+}
+
+int __init thunder_uncore_l2c_cbc_setup(void)
+{
+       int ret = -ENOMEM;
+
+       thunder_uncore_l2c_cbc = kzalloc(sizeof(struct thunder_uncore),
+                                        GFP_KERNEL);
+       if (!thunder_uncore_l2c_cbc)
+               goto fail_nomem;
+
+       ret = thunder_uncore_setup(thunder_uncore_l2c_cbc,
+                                  PCI_DEVICE_ID_THUNDER_L2C_CBC,
+                                  0,
+                                  0x100,
+                                  &thunder_l2c_cbc_pmu,
+                                  L2C_CBC_NR_COUNTERS);
+       if (ret)
+               goto fail;
+
+       thunder_uncore_l2c_cbc->type = L2C_CBC_TYPE;
+       thunder_uncore_l2c_cbc->event_valid = event_valid;
+       return 0;
+
+fail:
+       kfree(thunder_uncore_l2c_cbc);
+fail_nomem:
+       return ret;
+}
-- 
1.9.1

Reply via email to