[PATCH v2 4/5] arm64/perf: Cavium ThunderX LMC uncore support

From: Jan Glauber
Date: Wed Mar 09 2016 - 11:22:03 EST


Support counters on the DRAM controllers.

Also support pass2 added counters by checking MIDR.

Signed-off-by: Jan Glauber <jglauber@xxxxxxxxxx>
---
drivers/perf/uncore/Makefile | 3 +-
drivers/perf/uncore/uncore_cavium.c | 3 +
drivers/perf/uncore/uncore_cavium.h | 4 +
drivers/perf/uncore/uncore_cavium_lmc.c | 196 ++++++++++++++++++++++++++++++++
4 files changed, 205 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/uncore/uncore_cavium_lmc.c

diff --git a/drivers/perf/uncore/Makefile b/drivers/perf/uncore/Makefile
index d52ecc9..81479e8 100644
--- a/drivers/perf/uncore/Makefile
+++ b/drivers/perf/uncore/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o \
uncore_cavium_l2c_tad.o \
- uncore_cavium_l2c_cbc.o
+ uncore_cavium_l2c_cbc.o \
+ uncore_cavium_lmc.o
diff --git a/drivers/perf/uncore/uncore_cavium.c b/drivers/perf/uncore/uncore_cavium.c
index a230450..45c81d0 100644
--- a/drivers/perf/uncore/uncore_cavium.c
+++ b/drivers/perf/uncore/uncore_cavium.c
@@ -19,6 +19,8 @@ struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event)
return thunder_uncore_l2c_tad;
else if (event->pmu->type == thunder_l2c_cbc_pmu.type)
return thunder_uncore_l2c_cbc;
+ else if (event->pmu->type == thunder_lmc_pmu.type)
+ return thunder_uncore_lmc;
else
return NULL;
}
@@ -303,6 +305,7 @@ static int __init thunder_uncore_init(void)

thunder_uncore_l2c_tad_setup();
thunder_uncore_l2c_cbc_setup();
+ thunder_uncore_lmc_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 94bd02c..f14f6be 100644
--- a/drivers/perf/uncore/uncore_cavium.h
+++ b/drivers/perf/uncore/uncore_cavium.h
@@ -9,6 +9,7 @@
enum uncore_type {
L2C_TAD_TYPE,
L2C_CBC_TYPE,
+ LMC_TYPE,
};

extern int thunder_uncore_version;
@@ -68,8 +69,10 @@ 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 thunder_uncore *thunder_uncore_lmc;
extern struct pmu thunder_l2c_tad_pmu;
extern struct pmu thunder_l2c_cbc_pmu;
+extern struct pmu thunder_lmc_pmu;

/* Prototypes */
struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event);
@@ -85,3 +88,4 @@ ssize_t thunder_events_sysfs_show(struct device *dev,

int thunder_uncore_l2c_tad_setup(void);
int thunder_uncore_l2c_cbc_setup(void);
+int thunder_uncore_lmc_setup(void);
diff --git a/drivers/perf/uncore/uncore_cavium_lmc.c b/drivers/perf/uncore/uncore_cavium_lmc.c
new file mode 100644
index 0000000..b8d21b4
--- /dev/null
+++ b/drivers/perf/uncore/uncore_cavium_lmc.c
@@ -0,0 +1,196 @@
+/*
+ * Cavium Thunder uncore PMU support, LMC counters.
+ *
+ * Copyright 2016 Cavium Inc.
+ * Author: Jan Glauber <jan.glauber@xxxxxxxxxx>
+ */
+
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+
+#include "uncore_cavium.h"
+
+#ifndef PCI_DEVICE_ID_THUNDER_LMC
+#define PCI_DEVICE_ID_THUNDER_LMC 0xa022
+#endif
+
+#define LMC_NR_COUNTERS 3
+#define LMC_PASS2_NR_COUNTERS 5
+#define LMC_MAX_NR_COUNTERS LMC_PASS2_NR_COUNTERS
+
+/* LMC event list */
+#define LMC_EVENT_IFB_CNT 0
+#define LMC_EVENT_OPS_CNT 1
+#define LMC_EVENT_DCLK_CNT 2
+
+/* pass 2 added counters */
+#define LMC_EVENT_BANK_CONFLICT1 3
+#define LMC_EVENT_BANK_CONFLICT2 4
+
+#define LMC_COUNTER_START LMC_EVENT_IFB_CNT
+#define LMC_COUNTER_END (LMC_EVENT_BANK_CONFLICT2 + 8)
+
+struct thunder_uncore *thunder_uncore_lmc;
+
+int lmc_events[LMC_MAX_NR_COUNTERS] = { 0x1d0, 0x1d8, 0x1e0, 0x360, 0x368 };
+
+static void thunder_uncore_start(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ 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;
+ if (cmpxchg(&node->events[id], NULL, event) == NULL)
+ hwc->idx = i;
+
+out:
+ if (hwc->idx == -1)
+ return -EBUSY;
+
+ hwc->event_base = lmc_events[id];
+ hwc->state = PERF_HES_UPTODATE;
+
+ /* counters are read-only, so avoid PERF_EF_RELOAD */
+ if (flags & PERF_EF_START)
+ thunder_uncore_start(event, 0);
+
+ return 0;
+}
+
+PMU_FORMAT_ATTR(event, "config:0-2");
+
+static struct attribute *thunder_lmc_format_attr[] = {
+ &format_attr_event.attr,
+ &format_attr_node.attr,
+ NULL,
+};
+
+static struct attribute_group thunder_lmc_format_group = {
+ .name = "format",
+ .attrs = thunder_lmc_format_attr,
+};
+
+EVENT_ATTR(ifb_cnt, LMC_EVENT_IFB_CNT);
+EVENT_ATTR(ops_cnt, LMC_EVENT_OPS_CNT);
+EVENT_ATTR(dclk_cnt, LMC_EVENT_DCLK_CNT);
+EVENT_ATTR(bank_conflict1, LMC_EVENT_BANK_CONFLICT1);
+EVENT_ATTR(bank_conflict2, LMC_EVENT_BANK_CONFLICT2);
+
+static struct attribute *thunder_lmc_events_attr[] = {
+ EVENT_PTR(ifb_cnt),
+ EVENT_PTR(ops_cnt),
+ EVENT_PTR(dclk_cnt),
+ NULL,
+};
+
+static struct attribute *thunder_lmc_pass2_events_attr[] = {
+ EVENT_PTR(ifb_cnt),
+ EVENT_PTR(ops_cnt),
+ EVENT_PTR(dclk_cnt),
+ EVENT_PTR(bank_conflict1),
+ EVENT_PTR(bank_conflict2),
+ NULL,
+};
+
+static struct attribute_group thunder_lmc_events_group = {
+ .name = "events",
+ .attrs = NULL,
+};
+
+static const struct attribute_group *thunder_lmc_attr_groups[] = {
+ &thunder_uncore_attr_group,
+ &thunder_lmc_format_group,
+ &thunder_lmc_events_group,
+ NULL,
+};
+
+struct pmu thunder_lmc_pmu = {
+ .attr_groups = thunder_lmc_attr_groups,
+ .name = "thunder_lmc",
+ .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 <= LMC_EVENT_DCLK_CNT)
+ return 1;
+
+ if (thunder_uncore_version == 1)
+ if (config == LMC_EVENT_BANK_CONFLICT1 ||
+ config == LMC_EVENT_BANK_CONFLICT2)
+ return 1;
+ return 0;
+}
+
+int __init thunder_uncore_lmc_setup(void)
+{
+ int ret = -ENOMEM;
+
+ thunder_uncore_lmc = kzalloc(sizeof(struct thunder_uncore), GFP_KERNEL);
+ if (!thunder_uncore_lmc)
+ goto fail_nomem;
+
+ /* pass2 is default */
+ thunder_lmc_events_group.attrs = (thunder_uncore_version == 0) ?
+ thunder_lmc_events_attr : thunder_lmc_pass2_events_attr;
+
+ ret = thunder_uncore_setup(thunder_uncore_lmc,
+ PCI_DEVICE_ID_THUNDER_LMC,
+ LMC_COUNTER_START,
+ LMC_COUNTER_END - LMC_COUNTER_START,
+ &thunder_lmc_pmu,
+ (thunder_uncore_version == 1) ?
+ LMC_PASS2_NR_COUNTERS : LMC_NR_COUNTERS);
+ if (ret)
+ goto fail;
+
+ thunder_uncore_lmc->type = LMC_TYPE;
+ thunder_uncore_lmc->event_valid = event_valid;
+ return 0;
+
+fail:
+ kfree(thunder_uncore_lmc);
+fail_nomem:
+ return ret;
+}
--
1.9.1