hwtracing: hisi_ptt: Add tune function support for HiSilicon PCIe Tune and Trace...
authorYicong Yang <yangyicong@hisilicon.com>
Tue, 16 Aug 2022 11:44:12 +0000 (19:44 +0800)
committerMathieu Poirier <mathieu.poirier@linaro.org>
Thu, 8 Sep 2022 22:57:23 +0000 (16:57 -0600)
Add tune function for the HiSilicon Tune and Trace device. The interface
of tune is exposed through sysfs attributes of PTT PMU device.

Acked-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: John Garry <john.garry@huawei.com>
Signed-off-by: Yicong Yang <yangyicong@hisilicon.com>
Link: https://lore.kernel.org/r/20220816114414.4092-4-yangyicong@huawei.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
drivers/hwtracing/ptt/hisi_ptt.c
drivers/hwtracing/ptt/hisi_ptt.h

index 1a56b31..666a0f1 100644 (file)
 /* Dynamic CPU hotplug state used by PTT */
 static enum cpuhp_state hisi_ptt_pmu_online;
 
+static bool hisi_ptt_wait_tuning_finish(struct hisi_ptt *hisi_ptt)
+{
+       u32 val;
+
+       return !readl_poll_timeout(hisi_ptt->iobase + HISI_PTT_TUNING_INT_STAT,
+                                  val, !(val & HISI_PTT_TUNING_INT_STAT_MASK),
+                                  HISI_PTT_WAIT_POLL_INTERVAL_US,
+                                  HISI_PTT_WAIT_TUNE_TIMEOUT_US);
+}
+
+static ssize_t hisi_ptt_tune_attr_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev));
+       struct dev_ext_attribute *ext_attr;
+       struct hisi_ptt_tune_desc *desc;
+       u32 reg;
+       u16 val;
+
+       ext_attr = container_of(attr, struct dev_ext_attribute, attr);
+       desc = ext_attr->var;
+
+       mutex_lock(&hisi_ptt->tune_lock);
+
+       reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL);
+       reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB);
+       reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB,
+                         desc->event_code);
+       writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL);
+
+       /* Write all 1 to indicates it's the read process */
+       writel(~0U, hisi_ptt->iobase + HISI_PTT_TUNING_DATA);
+
+       if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) {
+               mutex_unlock(&hisi_ptt->tune_lock);
+               return -ETIMEDOUT;
+       }
+
+       reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_DATA);
+       reg &= HISI_PTT_TUNING_DATA_VAL_MASK;
+       val = FIELD_GET(HISI_PTT_TUNING_DATA_VAL_MASK, reg);
+
+       mutex_unlock(&hisi_ptt->tune_lock);
+       return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t hisi_ptt_tune_attr_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev));
+       struct dev_ext_attribute *ext_attr;
+       struct hisi_ptt_tune_desc *desc;
+       u32 reg;
+       u16 val;
+
+       ext_attr = container_of(attr, struct dev_ext_attribute, attr);
+       desc = ext_attr->var;
+
+       if (kstrtou16(buf, 10, &val))
+               return -EINVAL;
+
+       mutex_lock(&hisi_ptt->tune_lock);
+
+       reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL);
+       reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB);
+       reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB,
+                         desc->event_code);
+       writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL);
+       writel(FIELD_PREP(HISI_PTT_TUNING_DATA_VAL_MASK, val),
+              hisi_ptt->iobase + HISI_PTT_TUNING_DATA);
+
+       if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) {
+               mutex_unlock(&hisi_ptt->tune_lock);
+               return -ETIMEDOUT;
+       }
+
+       mutex_unlock(&hisi_ptt->tune_lock);
+       return count;
+}
+
+#define HISI_PTT_TUNE_ATTR(_name, _val, _show, _store)                 \
+       static struct hisi_ptt_tune_desc _name##_desc = {               \
+               .name = #_name,                                         \
+               .event_code = (_val),                                   \
+       };                                                              \
+       static struct dev_ext_attribute hisi_ptt_##_name##_attr = {     \
+               .attr   = __ATTR(_name, 0600, _show, _store),           \
+               .var    = &_name##_desc,                                \
+       }
+
+#define HISI_PTT_TUNE_ATTR_COMMON(_name, _val)         \
+       HISI_PTT_TUNE_ATTR(_name, _val,                 \
+                          hisi_ptt_tune_attr_show,     \
+                          hisi_ptt_tune_attr_store)
+
+/*
+ * The value of the tuning event are composed of two parts: main event code
+ * in BIT[0,15] and subevent code in BIT[16,23]. For example, qox_tx_cpl is
+ * a subevent of 'Tx path QoS control' which for tuning the weight of Tx
+ * completion TLPs. See hisi_ptt.rst documentation for more information.
+ */
+#define HISI_PTT_TUNE_QOS_TX_CPL               (0x4 | (3 << 16))
+#define HISI_PTT_TUNE_QOS_TX_NP                        (0x4 | (4 << 16))
+#define HISI_PTT_TUNE_QOS_TX_P                 (0x4 | (5 << 16))
+#define HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL       (0x5 | (6 << 16))
+#define HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL       (0x5 | (7 << 16))
+
+HISI_PTT_TUNE_ATTR_COMMON(qos_tx_cpl, HISI_PTT_TUNE_QOS_TX_CPL);
+HISI_PTT_TUNE_ATTR_COMMON(qos_tx_np, HISI_PTT_TUNE_QOS_TX_NP);
+HISI_PTT_TUNE_ATTR_COMMON(qos_tx_p, HISI_PTT_TUNE_QOS_TX_P);
+HISI_PTT_TUNE_ATTR_COMMON(rx_alloc_buf_level, HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL);
+HISI_PTT_TUNE_ATTR_COMMON(tx_alloc_buf_level, HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL);
+
+static struct attribute *hisi_ptt_tune_attrs[] = {
+       &hisi_ptt_qos_tx_cpl_attr.attr.attr,
+       &hisi_ptt_qos_tx_np_attr.attr.attr,
+       &hisi_ptt_qos_tx_p_attr.attr.attr,
+       &hisi_ptt_rx_alloc_buf_level_attr.attr.attr,
+       &hisi_ptt_tx_alloc_buf_level_attr.attr.attr,
+       NULL,
+};
+
+static struct attribute_group hisi_ptt_tune_group = {
+       .name   = "tune",
+       .attrs  = hisi_ptt_tune_attrs,
+};
+
 static u16 hisi_ptt_get_filter_val(u16 devid, bool is_port)
 {
        if (is_port)
@@ -393,6 +522,7 @@ static struct attribute_group hisi_ptt_pmu_format_group = {
 static const struct attribute_group *hisi_ptt_pmu_groups[] = {
        &hisi_ptt_cpumask_attr_group,
        &hisi_ptt_pmu_format_group,
+       &hisi_ptt_tune_group,
        NULL
 };
 
@@ -727,6 +857,7 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
        if (ret)
                return ret;
 
+       mutex_init(&hisi_ptt->tune_lock);
        spin_lock_init(&hisi_ptt->pmu_lock);
 
        hisi_ptt->hisi_ptt_pmu = (struct pmu) {
index 10446dc..5beb164 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/bits.h>
 #include <linux/cpumask.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/perf_event.h>
 #include <linux/spinlock.h>
 /*
  * The definition of the device registers and register fields.
  */
+#define HISI_PTT_TUNING_CTRL           0x0000
+#define   HISI_PTT_TUNING_CTRL_CODE    GENMASK(15, 0)
+#define   HISI_PTT_TUNING_CTRL_SUB     GENMASK(23, 16)
+#define HISI_PTT_TUNING_DATA           0x0004
+#define   HISI_PTT_TUNING_DATA_VAL_MASK        GENMASK(15, 0)
 #define HISI_PTT_TRACE_ADDR_SIZE       0x0800
 #define HISI_PTT_TRACE_ADDR_BASE_LO_0  0x0810
 #define HISI_PTT_TRACE_ADDR_BASE_HI_0  0x0814
@@ -37,6 +43,8 @@
 #define HISI_PTT_TRACE_INT_STAT                0x0890
 #define   HISI_PTT_TRACE_INT_STAT_MASK GENMASK(3, 0)
 #define HISI_PTT_TRACE_INT_MASK                0x0894
+#define HISI_PTT_TUNING_INT_STAT       0x0898
+#define   HISI_PTT_TUNING_INT_STAT_MASK        BIT(0)
 #define HISI_PTT_TRACE_WR_STS          0x08a0
 #define   HISI_PTT_TRACE_WR_STS_WRITE  GENMASK(27, 0)
 #define   HISI_PTT_TRACE_WR_STS_BUFFER GENMASK(29, 28)
@@ -59,6 +67,7 @@
 #define HISI_PTT_RESET_TIMEOUT_US      10UL
 #define HISI_PTT_RESET_POLL_INTERVAL_US        1UL
 /* Poll timeout and interval for waiting hardware work to finish */
+#define HISI_PTT_WAIT_TUNE_TIMEOUT_US  1000000UL
 #define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL
 #define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL
 
 #define HISI_PTT_PMU_FORMAT_MASK       GENMASK(35, 32)
 
 /**
+ * struct hisi_ptt_tune_desc - Describe tune event for PTT tune
+ * @hisi_ptt:   PTT device this tune event belongs to
+ * @name:       name of this event
+ * @event_code: code of the event
+ */
+struct hisi_ptt_tune_desc {
+       struct hisi_ptt *hisi_ptt;
+       const char *name;
+       u32 event_code;
+};
+
+/**
  * struct hisi_ptt_dma_buffer - Describe a single trace buffer of PTT trace.
  *                              The detail of the data format is described
  *                              in the documentation of PTT device.
@@ -143,6 +164,7 @@ struct hisi_ptt_pmu_buf {
  * @hisi_ptt_pmu: the pum device of trace
  * @iobase:       base IO address of the device
  * @pdev:         pci_dev of this PTT device
+ * @tune_lock:    lock to serialize the tune process
  * @pmu_lock:     lock to serialize the perf process
  * @upper_bdf:    the upper BDF range of the PCI devices managed by this PTT device
  * @lower_bdf:    the lower BDF range of the PCI devices managed by this PTT device
@@ -156,6 +178,7 @@ struct hisi_ptt {
        struct pmu hisi_ptt_pmu;
        void __iomem *iobase;
        struct pci_dev *pdev;
+       struct mutex tune_lock;
        spinlock_t pmu_lock;
        u32 upper_bdf;
        u32 lower_bdf;