Re: [PATCH v6 09/11] drivers: perf: hisi: Miscellanous node(MN) event counting in perf

2017-03-21 Thread Mark Rutland
On Fri, Mar 10, 2017 at 01:28:53AM -0500, Anurup M wrote:
> +static u32 hisi_mn_read_counter(struct hisi_mn_data *mn_data, int cntr_idx)
> +{
> + struct hisi_djtag_client *client = mn_data->client;
> + u32 module_id = GET_MODULE_ID(mn_data);
> + u32 reg_off, value;
> +
> + reg_off = get_counter_reg_off(cntr_idx);
> + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, reg_off,
> +client, );

You need to handle this failing.

[...]

> + do {
> + /* Get count from the MN */
> + prev_raw_count = local64_read(>prev_count);
> + new_raw_count = hisi_mn_read_counter(mn_data, idx);
> + delta = (new_raw_count - prev_raw_count) & HISI_MAX_PERIOD;
> +
> + local64_add(delta, >count);
> + } while (local64_cmpxchg(>prev_count, prev_raw_count,
> +  new_raw_count) != prev_raw_count);

Same comment as for the l3 event update loop.

[...]

> + /*
> +  * Value to write to event select register
> +  * Each byte in the 32 bit select register is used to
> +  * configure the event code. Each byte correspond to a
> +  * counter register to use.
> +  */
> + val = event_value << (8 * idx);
> +
> + /*
> +  * Set the event in MN_EVENT_TYPE Register
> +  */
> + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, MN1_EVTYPE_REG_OFF,
> +client, );
> + value &= ~(0xff << (8 * idx));
> + value |= val;
> + hisi_djtag_writereg(module_id, MN1_BANK_SELECT, MN1_EVTYPE_REG_OFF,
> + value, client);

Use a helper for this common pattern.

[...]

> +static int hisi_mn_pmu_init(struct hisi_pmu *mn_pmu,
> + struct hisi_djtag_client *client)
> +{
> + struct device *dev = >dev;
> +
> + mn_pmu->num_events = HISI_HWEVENT_MN_EVENT_MAX;
> + mn_pmu->num_counters = HISI_IDX_MN_COUNTER_MAX;
> + mn_pmu->scl_id = hisi_djtag_get_sclid(client);
> +
> + mn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn_%d", mn_pmu->scl_id);

This is leaked if we fail in hisi_pmu_mn_dev_probe()...

> +static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
> +{
> + struct hisi_pmu *mn_pmu;
> + struct device *dev = >dev;
> + int ret;
> +
> + mn_pmu = hisi_pmu_alloc(dev);
> + if (!mn_pmu)
> + return -ENOMEM;
> +
> + ret = hisi_mn_pmu_init(mn_pmu, client);
> + if (ret)
> + return ret;
> +
> + ret = hisi_mn_init_data(mn_pmu, client);
> + if (ret)
> + return ret;

... e.g. here.

Thanks,
Mark.


Re: [PATCH v6 09/11] drivers: perf: hisi: Miscellanous node(MN) event counting in perf

2017-03-21 Thread Mark Rutland
On Fri, Mar 10, 2017 at 01:28:53AM -0500, Anurup M wrote:
> +static u32 hisi_mn_read_counter(struct hisi_mn_data *mn_data, int cntr_idx)
> +{
> + struct hisi_djtag_client *client = mn_data->client;
> + u32 module_id = GET_MODULE_ID(mn_data);
> + u32 reg_off, value;
> +
> + reg_off = get_counter_reg_off(cntr_idx);
> + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, reg_off,
> +client, );

You need to handle this failing.

[...]

> + do {
> + /* Get count from the MN */
> + prev_raw_count = local64_read(>prev_count);
> + new_raw_count = hisi_mn_read_counter(mn_data, idx);
> + delta = (new_raw_count - prev_raw_count) & HISI_MAX_PERIOD;
> +
> + local64_add(delta, >count);
> + } while (local64_cmpxchg(>prev_count, prev_raw_count,
> +  new_raw_count) != prev_raw_count);

Same comment as for the l3 event update loop.

[...]

> + /*
> +  * Value to write to event select register
> +  * Each byte in the 32 bit select register is used to
> +  * configure the event code. Each byte correspond to a
> +  * counter register to use.
> +  */
> + val = event_value << (8 * idx);
> +
> + /*
> +  * Set the event in MN_EVENT_TYPE Register
> +  */
> + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, MN1_EVTYPE_REG_OFF,
> +client, );
> + value &= ~(0xff << (8 * idx));
> + value |= val;
> + hisi_djtag_writereg(module_id, MN1_BANK_SELECT, MN1_EVTYPE_REG_OFF,
> + value, client);

Use a helper for this common pattern.

[...]

> +static int hisi_mn_pmu_init(struct hisi_pmu *mn_pmu,
> + struct hisi_djtag_client *client)
> +{
> + struct device *dev = >dev;
> +
> + mn_pmu->num_events = HISI_HWEVENT_MN_EVENT_MAX;
> + mn_pmu->num_counters = HISI_IDX_MN_COUNTER_MAX;
> + mn_pmu->scl_id = hisi_djtag_get_sclid(client);
> +
> + mn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn_%d", mn_pmu->scl_id);

This is leaked if we fail in hisi_pmu_mn_dev_probe()...

> +static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
> +{
> + struct hisi_pmu *mn_pmu;
> + struct device *dev = >dev;
> + int ret;
> +
> + mn_pmu = hisi_pmu_alloc(dev);
> + if (!mn_pmu)
> + return -ENOMEM;
> +
> + ret = hisi_mn_pmu_init(mn_pmu, client);
> + if (ret)
> + return ret;
> +
> + ret = hisi_mn_init_data(mn_pmu, client);
> + if (ret)
> + return ret;

... e.g. here.

Thanks,
Mark.


[PATCH v6 09/11] drivers: perf: hisi: Miscellanous node(MN) event counting in perf

2017-03-09 Thread Anurup M
From: Shaokun Zhang 

1. Add support to count MN hardware events.
2. Mn events are listed in sysfs at /sys/devices/hisi_mn_2/events/
   The events can be selected as shown in perf list
   e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
   event format is -e "hisi_mn_2/read_req/"

Signed-off-by: Shaokun Zhang 
Signed-off-by: Anurup M 
---
 drivers/perf/hisilicon/Makefile |   2 +-
 drivers/perf/hisilicon/hisi_uncore_mn.c | 489 
 2 files changed, 490 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 0887b56..26b2507 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += djtag.o hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += djtag.o hisi_uncore_pmu.o hisi_uncore_l3c.o 
hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c 
b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 000..18316dc
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,489 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2017 Hisilicon Limited
+ * Author: Shaokun Zhang 
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN event types.
+ */
+enum armv8_hisi_mn_event_types {
+   HISI_HWEVENT_MN_EO_BARR_REQ = 0x0,
+   HISI_HWEVENT_MN_EC_BARR_REQ = 0x01,
+   HISI_HWEVENT_MN_DVM_OP_REQ  = 0x02,
+   HISI_HWEVENT_MN_DVM_SYNC_REQ= 0x03,
+   HISI_HWEVENT_MN_READ_REQ= 0x04,
+   HISI_HWEVENT_MN_WRITE_REQ   = 0x05,
+   HISI_HWEVENT_MN_EVENT_MAX   = 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+   HISI_IDX_MN_COUNTER0= 0x0,
+   HISI_IDX_MN_COUNTER_MAX = 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR   0x04
+#define MN1_EVTYPE_REG_OFF 0x48
+#define MN1_EVCTRL_REG_OFF 0x40
+#define MN1_CNT0_REG_OFF 0x30
+#define MN1_EVENT_EN 0x01
+#define MN1_BANK_SELECT 0x01
+
+#define GET_MODULE_ID(hwmod_data) hwmod_data->mn_hwcfg.module_id
+
+struct hisi_mn_hwcfg {
+   u32 module_id;
+};
+
+struct hisi_mn_data {
+   struct hisi_djtag_client *client;
+   DECLARE_BITMAP(event_used_mask, HISI_MAX_CFG_MN_CNTR);
+   struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+   return (idx >= HISI_IDX_MN_COUNTER0 &&
+   idx < HISI_IDX_MN_COUNTER_MAX);
+}
+
+/* Select the counter register offset from the index */
+static inline u32 get_counter_reg_off(int cntr_idx)
+{
+   return (MN1_CNT0_REG_OFF + (cntr_idx * 4));
+}
+
+static u32 hisi_mn_read_counter(struct hisi_mn_data *mn_data, int cntr_idx)
+{
+   struct hisi_djtag_client *client = mn_data->client;
+   u32 module_id = GET_MODULE_ID(mn_data);
+   u32 reg_off, value;
+
+   reg_off = get_counter_reg_off(cntr_idx);
+   hisi_djtag_readreg(module_id, MN1_BANK_SELECT, reg_off,
+  client, );
+
+   return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+   struct hw_perf_event *hwc, int idx)
+{
+   struct hisi_pmu *mn_pmu = to_hisi_pmu(event->pmu);
+   struct hisi_mn_data *mn_data = mn_pmu->hwmod_data;
+   u64 delta, prev_raw_count, new_raw_count = 0;
+
+   if (!hisi_mn_counter_valid(idx)) {
+   dev_err(mn_pmu->dev,
+   "Unsupported event index:%d!\n", idx);
+   return 0;
+   }
+
+   do {
+   /* Get count from the MN */
+   prev_raw_count = local64_read(>prev_count);
+   new_raw_count = hisi_mn_read_counter(mn_data, idx);
+   delta = (new_raw_count - prev_raw_count) & HISI_MAX_PERIOD;
+
+   local64_add(delta, >count);
+   } while (local64_cmpxchg(>prev_count, prev_raw_count,
+new_raw_count) != prev_raw_count);
+
+   return new_raw_count;
+}
+
+static void hisi_mn_set_evtype(struct hisi_pmu *mn_pmu, 

[PATCH v6 09/11] drivers: perf: hisi: Miscellanous node(MN) event counting in perf

2017-03-09 Thread Anurup M
From: Shaokun Zhang 

1. Add support to count MN hardware events.
2. Mn events are listed in sysfs at /sys/devices/hisi_mn_2/events/
   The events can be selected as shown in perf list
   e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
   event format is -e "hisi_mn_2/read_req/"

Signed-off-by: Shaokun Zhang 
Signed-off-by: Anurup M 
---
 drivers/perf/hisilicon/Makefile |   2 +-
 drivers/perf/hisilicon/hisi_uncore_mn.c | 489 
 2 files changed, 490 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 0887b56..26b2507 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += djtag.o hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += djtag.o hisi_uncore_pmu.o hisi_uncore_l3c.o 
hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c 
b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 000..18316dc
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,489 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2017 Hisilicon Limited
+ * Author: Shaokun Zhang 
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN event types.
+ */
+enum armv8_hisi_mn_event_types {
+   HISI_HWEVENT_MN_EO_BARR_REQ = 0x0,
+   HISI_HWEVENT_MN_EC_BARR_REQ = 0x01,
+   HISI_HWEVENT_MN_DVM_OP_REQ  = 0x02,
+   HISI_HWEVENT_MN_DVM_SYNC_REQ= 0x03,
+   HISI_HWEVENT_MN_READ_REQ= 0x04,
+   HISI_HWEVENT_MN_WRITE_REQ   = 0x05,
+   HISI_HWEVENT_MN_EVENT_MAX   = 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+   HISI_IDX_MN_COUNTER0= 0x0,
+   HISI_IDX_MN_COUNTER_MAX = 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR   0x04
+#define MN1_EVTYPE_REG_OFF 0x48
+#define MN1_EVCTRL_REG_OFF 0x40
+#define MN1_CNT0_REG_OFF 0x30
+#define MN1_EVENT_EN 0x01
+#define MN1_BANK_SELECT 0x01
+
+#define GET_MODULE_ID(hwmod_data) hwmod_data->mn_hwcfg.module_id
+
+struct hisi_mn_hwcfg {
+   u32 module_id;
+};
+
+struct hisi_mn_data {
+   struct hisi_djtag_client *client;
+   DECLARE_BITMAP(event_used_mask, HISI_MAX_CFG_MN_CNTR);
+   struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+   return (idx >= HISI_IDX_MN_COUNTER0 &&
+   idx < HISI_IDX_MN_COUNTER_MAX);
+}
+
+/* Select the counter register offset from the index */
+static inline u32 get_counter_reg_off(int cntr_idx)
+{
+   return (MN1_CNT0_REG_OFF + (cntr_idx * 4));
+}
+
+static u32 hisi_mn_read_counter(struct hisi_mn_data *mn_data, int cntr_idx)
+{
+   struct hisi_djtag_client *client = mn_data->client;
+   u32 module_id = GET_MODULE_ID(mn_data);
+   u32 reg_off, value;
+
+   reg_off = get_counter_reg_off(cntr_idx);
+   hisi_djtag_readreg(module_id, MN1_BANK_SELECT, reg_off,
+  client, );
+
+   return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+   struct hw_perf_event *hwc, int idx)
+{
+   struct hisi_pmu *mn_pmu = to_hisi_pmu(event->pmu);
+   struct hisi_mn_data *mn_data = mn_pmu->hwmod_data;
+   u64 delta, prev_raw_count, new_raw_count = 0;
+
+   if (!hisi_mn_counter_valid(idx)) {
+   dev_err(mn_pmu->dev,
+   "Unsupported event index:%d!\n", idx);
+   return 0;
+   }
+
+   do {
+   /* Get count from the MN */
+   prev_raw_count = local64_read(>prev_count);
+   new_raw_count = hisi_mn_read_counter(mn_data, idx);
+   delta = (new_raw_count - prev_raw_count) & HISI_MAX_PERIOD;
+
+   local64_add(delta, >count);
+   } while (local64_cmpxchg(>prev_count, prev_raw_count,
+new_raw_count) != prev_raw_count);
+
+   return new_raw_count;
+}
+
+static void hisi_mn_set_evtype(struct hisi_pmu *mn_pmu, int idx, u32 val)
+{
+   struct hisi_mn_data *mn_data = mn_pmu->hwmod_data;
+   struct