1ff0de066SYicong Yang // SPDX-License-Identifier: GPL-2.0 2ff0de066SYicong Yang /* 3ff0de066SYicong Yang * Driver for HiSilicon PCIe tune and trace device 4ff0de066SYicong Yang * 5ff0de066SYicong Yang * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. 6ff0de066SYicong Yang * Author: Yicong Yang <yangyicong@hisilicon.com> 7ff0de066SYicong Yang */ 8ff0de066SYicong Yang 9ff0de066SYicong Yang #include <linux/bitfield.h> 10ff0de066SYicong Yang #include <linux/bitops.h> 11ff0de066SYicong Yang #include <linux/cpuhotplug.h> 12ff0de066SYicong Yang #include <linux/delay.h> 13ff0de066SYicong Yang #include <linux/dma-mapping.h> 14ff0de066SYicong Yang #include <linux/interrupt.h> 15ff0de066SYicong Yang #include <linux/io.h> 16ff0de066SYicong Yang #include <linux/iommu.h> 17ff0de066SYicong Yang #include <linux/iopoll.h> 18ff0de066SYicong Yang #include <linux/module.h> 19ff0de066SYicong Yang #include <linux/sysfs.h> 20ff0de066SYicong Yang #include <linux/vmalloc.h> 21ff0de066SYicong Yang 22ff0de066SYicong Yang #include "hisi_ptt.h" 23ff0de066SYicong Yang 24ff0de066SYicong Yang /* Dynamic CPU hotplug state used by PTT */ 25ff0de066SYicong Yang static enum cpuhp_state hisi_ptt_pmu_online; 26ff0de066SYicong Yang 275ca57b03SYicong Yang static bool hisi_ptt_wait_tuning_finish(struct hisi_ptt *hisi_ptt) 285ca57b03SYicong Yang { 295ca57b03SYicong Yang u32 val; 305ca57b03SYicong Yang 315ca57b03SYicong Yang return !readl_poll_timeout(hisi_ptt->iobase + HISI_PTT_TUNING_INT_STAT, 325ca57b03SYicong Yang val, !(val & HISI_PTT_TUNING_INT_STAT_MASK), 335ca57b03SYicong Yang HISI_PTT_WAIT_POLL_INTERVAL_US, 345ca57b03SYicong Yang HISI_PTT_WAIT_TUNE_TIMEOUT_US); 355ca57b03SYicong Yang } 365ca57b03SYicong Yang 375ca57b03SYicong Yang static ssize_t hisi_ptt_tune_attr_show(struct device *dev, 385ca57b03SYicong Yang struct device_attribute *attr, 395ca57b03SYicong Yang char *buf) 405ca57b03SYicong Yang { 415ca57b03SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); 425ca57b03SYicong Yang struct dev_ext_attribute *ext_attr; 435ca57b03SYicong Yang struct hisi_ptt_tune_desc *desc; 445ca57b03SYicong Yang u32 reg; 455ca57b03SYicong Yang u16 val; 465ca57b03SYicong Yang 475ca57b03SYicong Yang ext_attr = container_of(attr, struct dev_ext_attribute, attr); 485ca57b03SYicong Yang desc = ext_attr->var; 495ca57b03SYicong Yang 505ca57b03SYicong Yang mutex_lock(&hisi_ptt->tune_lock); 515ca57b03SYicong Yang 525ca57b03SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); 535ca57b03SYicong Yang reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB); 545ca57b03SYicong Yang reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB, 555ca57b03SYicong Yang desc->event_code); 565ca57b03SYicong Yang writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); 575ca57b03SYicong Yang 585ca57b03SYicong Yang /* Write all 1 to indicates it's the read process */ 595ca57b03SYicong Yang writel(~0U, hisi_ptt->iobase + HISI_PTT_TUNING_DATA); 605ca57b03SYicong Yang 615ca57b03SYicong Yang if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) { 625ca57b03SYicong Yang mutex_unlock(&hisi_ptt->tune_lock); 635ca57b03SYicong Yang return -ETIMEDOUT; 645ca57b03SYicong Yang } 655ca57b03SYicong Yang 665ca57b03SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_DATA); 675ca57b03SYicong Yang reg &= HISI_PTT_TUNING_DATA_VAL_MASK; 685ca57b03SYicong Yang val = FIELD_GET(HISI_PTT_TUNING_DATA_VAL_MASK, reg); 695ca57b03SYicong Yang 705ca57b03SYicong Yang mutex_unlock(&hisi_ptt->tune_lock); 715ca57b03SYicong Yang return sysfs_emit(buf, "%u\n", val); 725ca57b03SYicong Yang } 735ca57b03SYicong Yang 745ca57b03SYicong Yang static ssize_t hisi_ptt_tune_attr_store(struct device *dev, 755ca57b03SYicong Yang struct device_attribute *attr, 765ca57b03SYicong Yang const char *buf, size_t count) 775ca57b03SYicong Yang { 785ca57b03SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); 795ca57b03SYicong Yang struct dev_ext_attribute *ext_attr; 805ca57b03SYicong Yang struct hisi_ptt_tune_desc *desc; 815ca57b03SYicong Yang u32 reg; 825ca57b03SYicong Yang u16 val; 835ca57b03SYicong Yang 845ca57b03SYicong Yang ext_attr = container_of(attr, struct dev_ext_attribute, attr); 855ca57b03SYicong Yang desc = ext_attr->var; 865ca57b03SYicong Yang 875ca57b03SYicong Yang if (kstrtou16(buf, 10, &val)) 885ca57b03SYicong Yang return -EINVAL; 895ca57b03SYicong Yang 905ca57b03SYicong Yang mutex_lock(&hisi_ptt->tune_lock); 915ca57b03SYicong Yang 925ca57b03SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); 935ca57b03SYicong Yang reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB); 945ca57b03SYicong Yang reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB, 955ca57b03SYicong Yang desc->event_code); 965ca57b03SYicong Yang writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); 975ca57b03SYicong Yang writel(FIELD_PREP(HISI_PTT_TUNING_DATA_VAL_MASK, val), 985ca57b03SYicong Yang hisi_ptt->iobase + HISI_PTT_TUNING_DATA); 995ca57b03SYicong Yang 1005ca57b03SYicong Yang if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) { 1015ca57b03SYicong Yang mutex_unlock(&hisi_ptt->tune_lock); 1025ca57b03SYicong Yang return -ETIMEDOUT; 1035ca57b03SYicong Yang } 1045ca57b03SYicong Yang 1055ca57b03SYicong Yang mutex_unlock(&hisi_ptt->tune_lock); 1065ca57b03SYicong Yang return count; 1075ca57b03SYicong Yang } 1085ca57b03SYicong Yang 1095ca57b03SYicong Yang #define HISI_PTT_TUNE_ATTR(_name, _val, _show, _store) \ 1105ca57b03SYicong Yang static struct hisi_ptt_tune_desc _name##_desc = { \ 1115ca57b03SYicong Yang .name = #_name, \ 1125ca57b03SYicong Yang .event_code = (_val), \ 1135ca57b03SYicong Yang }; \ 1145ca57b03SYicong Yang static struct dev_ext_attribute hisi_ptt_##_name##_attr = { \ 1155ca57b03SYicong Yang .attr = __ATTR(_name, 0600, _show, _store), \ 1165ca57b03SYicong Yang .var = &_name##_desc, \ 1175ca57b03SYicong Yang } 1185ca57b03SYicong Yang 1195ca57b03SYicong Yang #define HISI_PTT_TUNE_ATTR_COMMON(_name, _val) \ 1205ca57b03SYicong Yang HISI_PTT_TUNE_ATTR(_name, _val, \ 1215ca57b03SYicong Yang hisi_ptt_tune_attr_show, \ 1225ca57b03SYicong Yang hisi_ptt_tune_attr_store) 1235ca57b03SYicong Yang 1245ca57b03SYicong Yang /* 1255ca57b03SYicong Yang * The value of the tuning event are composed of two parts: main event code 1265ca57b03SYicong Yang * in BIT[0,15] and subevent code in BIT[16,23]. For example, qox_tx_cpl is 1275ca57b03SYicong Yang * a subevent of 'Tx path QoS control' which for tuning the weight of Tx 1285ca57b03SYicong Yang * completion TLPs. See hisi_ptt.rst documentation for more information. 1295ca57b03SYicong Yang */ 1305ca57b03SYicong Yang #define HISI_PTT_TUNE_QOS_TX_CPL (0x4 | (3 << 16)) 1315ca57b03SYicong Yang #define HISI_PTT_TUNE_QOS_TX_NP (0x4 | (4 << 16)) 1325ca57b03SYicong Yang #define HISI_PTT_TUNE_QOS_TX_P (0x4 | (5 << 16)) 1335ca57b03SYicong Yang #define HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL (0x5 | (6 << 16)) 1345ca57b03SYicong Yang #define HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL (0x5 | (7 << 16)) 1355ca57b03SYicong Yang 1365ca57b03SYicong Yang HISI_PTT_TUNE_ATTR_COMMON(qos_tx_cpl, HISI_PTT_TUNE_QOS_TX_CPL); 1375ca57b03SYicong Yang HISI_PTT_TUNE_ATTR_COMMON(qos_tx_np, HISI_PTT_TUNE_QOS_TX_NP); 1385ca57b03SYicong Yang HISI_PTT_TUNE_ATTR_COMMON(qos_tx_p, HISI_PTT_TUNE_QOS_TX_P); 1395ca57b03SYicong Yang HISI_PTT_TUNE_ATTR_COMMON(rx_alloc_buf_level, HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL); 1405ca57b03SYicong Yang HISI_PTT_TUNE_ATTR_COMMON(tx_alloc_buf_level, HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL); 1415ca57b03SYicong Yang 1425ca57b03SYicong Yang static struct attribute *hisi_ptt_tune_attrs[] = { 1435ca57b03SYicong Yang &hisi_ptt_qos_tx_cpl_attr.attr.attr, 1445ca57b03SYicong Yang &hisi_ptt_qos_tx_np_attr.attr.attr, 1455ca57b03SYicong Yang &hisi_ptt_qos_tx_p_attr.attr.attr, 1465ca57b03SYicong Yang &hisi_ptt_rx_alloc_buf_level_attr.attr.attr, 1475ca57b03SYicong Yang &hisi_ptt_tx_alloc_buf_level_attr.attr.attr, 1485ca57b03SYicong Yang NULL, 1495ca57b03SYicong Yang }; 1505ca57b03SYicong Yang 1515ca57b03SYicong Yang static struct attribute_group hisi_ptt_tune_group = { 1525ca57b03SYicong Yang .name = "tune", 1535ca57b03SYicong Yang .attrs = hisi_ptt_tune_attrs, 1545ca57b03SYicong Yang }; 1555ca57b03SYicong Yang 156ff0de066SYicong Yang static u16 hisi_ptt_get_filter_val(u16 devid, bool is_port) 157ff0de066SYicong Yang { 158ff0de066SYicong Yang if (is_port) 159ff0de066SYicong Yang return BIT(HISI_PCIE_CORE_PORT_ID(devid & 0xff)); 160ff0de066SYicong Yang 161ff0de066SYicong Yang return devid; 162ff0de066SYicong Yang } 163ff0de066SYicong Yang 164ff0de066SYicong Yang static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) 165ff0de066SYicong Yang { 166ff0de066SYicong Yang u32 val; 167ff0de066SYicong Yang 168ff0de066SYicong Yang return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_STS, 169ff0de066SYicong Yang val, val & HISI_PTT_TRACE_IDLE, 170ff0de066SYicong Yang HISI_PTT_WAIT_POLL_INTERVAL_US, 171ff0de066SYicong Yang HISI_PTT_WAIT_TRACE_TIMEOUT_US); 172ff0de066SYicong Yang } 173ff0de066SYicong Yang 174ff0de066SYicong Yang static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) 175ff0de066SYicong Yang { 176ff0de066SYicong Yang u32 val; 177ff0de066SYicong Yang 178ff0de066SYicong Yang readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, 179ff0de066SYicong Yang val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, 180ff0de066SYicong Yang HISI_PTT_RESET_TIMEOUT_US); 181ff0de066SYicong Yang } 182ff0de066SYicong Yang 183ff0de066SYicong Yang static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) 184ff0de066SYicong Yang { 185ff0de066SYicong Yang writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 186ff0de066SYicong Yang hisi_ptt->trace_ctrl.started = false; 187ff0de066SYicong Yang } 188ff0de066SYicong Yang 189ff0de066SYicong Yang static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) 190ff0de066SYicong Yang { 191ff0de066SYicong Yang struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; 192ff0de066SYicong Yang u32 val; 193ff0de066SYicong Yang int i; 194ff0de066SYicong Yang 195ff0de066SYicong Yang /* Check device idle before start trace */ 196ff0de066SYicong Yang if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) { 197ff0de066SYicong Yang pci_err(hisi_ptt->pdev, "Failed to start trace, the device is still busy\n"); 198ff0de066SYicong Yang return -EBUSY; 199ff0de066SYicong Yang } 200ff0de066SYicong Yang 201ff0de066SYicong Yang ctrl->started = true; 202ff0de066SYicong Yang 203ff0de066SYicong Yang /* Reset the DMA before start tracing */ 204ff0de066SYicong Yang val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 205ff0de066SYicong Yang val |= HISI_PTT_TRACE_CTRL_RST; 206ff0de066SYicong Yang writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 207ff0de066SYicong Yang 208ff0de066SYicong Yang hisi_ptt_wait_dma_reset_done(hisi_ptt); 209ff0de066SYicong Yang 210ff0de066SYicong Yang val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 211ff0de066SYicong Yang val &= ~HISI_PTT_TRACE_CTRL_RST; 212ff0de066SYicong Yang writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 213ff0de066SYicong Yang 214ff0de066SYicong Yang /* Reset the index of current buffer */ 215ff0de066SYicong Yang hisi_ptt->trace_ctrl.buf_index = 0; 216ff0de066SYicong Yang 217ff0de066SYicong Yang /* Zero the trace buffers */ 218ff0de066SYicong Yang for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) 219ff0de066SYicong Yang memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE); 220ff0de066SYicong Yang 221ff0de066SYicong Yang /* Clear the interrupt status */ 222ff0de066SYicong Yang writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); 223ff0de066SYicong Yang writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); 224ff0de066SYicong Yang 225ff0de066SYicong Yang /* Set the trace control register */ 226ff0de066SYicong Yang val = FIELD_PREP(HISI_PTT_TRACE_CTRL_TYPE_SEL, ctrl->type); 227ff0de066SYicong Yang val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_RXTX_SEL, ctrl->direction); 228ff0de066SYicong Yang val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_DATA_FORMAT, ctrl->format); 229ff0de066SYicong Yang val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_TARGET_SEL, hisi_ptt->trace_ctrl.filter); 230ff0de066SYicong Yang if (!hisi_ptt->trace_ctrl.is_port) 231ff0de066SYicong Yang val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; 232ff0de066SYicong Yang 233ff0de066SYicong Yang /* Start the Trace */ 234ff0de066SYicong Yang val |= HISI_PTT_TRACE_CTRL_EN; 235ff0de066SYicong Yang writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); 236ff0de066SYicong Yang 237ff0de066SYicong Yang return 0; 238ff0de066SYicong Yang } 239ff0de066SYicong Yang 240ff0de066SYicong Yang static int hisi_ptt_update_aux(struct hisi_ptt *hisi_ptt, int index, bool stop) 241ff0de066SYicong Yang { 242ff0de066SYicong Yang struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; 243ff0de066SYicong Yang struct perf_output_handle *handle = &ctrl->handle; 244ff0de066SYicong Yang struct perf_event *event = handle->event; 245ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf; 246ff0de066SYicong Yang size_t size; 247ff0de066SYicong Yang void *addr; 248ff0de066SYicong Yang 249ff0de066SYicong Yang buf = perf_get_aux(handle); 250ff0de066SYicong Yang if (!buf || !handle->size) 251ff0de066SYicong Yang return -EINVAL; 252ff0de066SYicong Yang 253ff0de066SYicong Yang addr = ctrl->trace_buf[ctrl->buf_index].addr; 254ff0de066SYicong Yang 255ff0de066SYicong Yang /* 256ff0de066SYicong Yang * If we're going to stop, read the size of already traced data from 257ff0de066SYicong Yang * HISI_PTT_TRACE_WR_STS. Otherwise we're coming from the interrupt, 258ff0de066SYicong Yang * the data size is always HISI_PTT_TRACE_BUF_SIZE. 259ff0de066SYicong Yang */ 260ff0de066SYicong Yang if (stop) { 261ff0de066SYicong Yang u32 reg; 262ff0de066SYicong Yang 263ff0de066SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS); 264ff0de066SYicong Yang size = FIELD_GET(HISI_PTT_TRACE_WR_STS_WRITE, reg); 265ff0de066SYicong Yang } else { 266ff0de066SYicong Yang size = HISI_PTT_TRACE_BUF_SIZE; 267ff0de066SYicong Yang } 268ff0de066SYicong Yang 269ff0de066SYicong Yang memcpy(buf->base + buf->pos, addr, size); 270ff0de066SYicong Yang buf->pos += size; 271ff0de066SYicong Yang 272ff0de066SYicong Yang /* 273ff0de066SYicong Yang * Just commit the traced data if we're going to stop. Otherwise if the 274ff0de066SYicong Yang * resident AUX buffer cannot contain the data of next trace buffer, 275ff0de066SYicong Yang * apply a new one. 276ff0de066SYicong Yang */ 277ff0de066SYicong Yang if (stop) { 278ff0de066SYicong Yang perf_aux_output_end(handle, buf->pos); 279ff0de066SYicong Yang } else if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { 280ff0de066SYicong Yang perf_aux_output_end(handle, buf->pos); 281ff0de066SYicong Yang 282ff0de066SYicong Yang buf = perf_aux_output_begin(handle, event); 283ff0de066SYicong Yang if (!buf) 284ff0de066SYicong Yang return -EINVAL; 285ff0de066SYicong Yang 286ff0de066SYicong Yang buf->pos = handle->head % buf->length; 287ff0de066SYicong Yang if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { 288ff0de066SYicong Yang perf_aux_output_end(handle, 0); 289ff0de066SYicong Yang return -EINVAL; 290ff0de066SYicong Yang } 291ff0de066SYicong Yang } 292ff0de066SYicong Yang 293ff0de066SYicong Yang return 0; 294ff0de066SYicong Yang } 295ff0de066SYicong Yang 296ff0de066SYicong Yang static irqreturn_t hisi_ptt_isr(int irq, void *context) 297ff0de066SYicong Yang { 298ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = context; 299ff0de066SYicong Yang u32 status, buf_idx; 300ff0de066SYicong Yang 301ff0de066SYicong Yang status = readl(hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); 302ff0de066SYicong Yang if (!(status & HISI_PTT_TRACE_INT_STAT_MASK)) 303ff0de066SYicong Yang return IRQ_NONE; 304ff0de066SYicong Yang 305ff0de066SYicong Yang buf_idx = ffs(status) - 1; 306ff0de066SYicong Yang 307ff0de066SYicong Yang /* Clear the interrupt status of buffer @buf_idx */ 308ff0de066SYicong Yang writel(status, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); 309ff0de066SYicong Yang 310ff0de066SYicong Yang /* 311ff0de066SYicong Yang * Update the AUX buffer and cache the current buffer index, 312ff0de066SYicong Yang * as we need to know this and save the data when the trace 313ff0de066SYicong Yang * is ended out of the interrupt handler. End the trace 314ff0de066SYicong Yang * if the updating fails. 315ff0de066SYicong Yang */ 316ff0de066SYicong Yang if (hisi_ptt_update_aux(hisi_ptt, buf_idx, false)) 317ff0de066SYicong Yang hisi_ptt_trace_end(hisi_ptt); 318ff0de066SYicong Yang else 319ff0de066SYicong Yang hisi_ptt->trace_ctrl.buf_index = (buf_idx + 1) % HISI_PTT_TRACE_BUF_CNT; 320ff0de066SYicong Yang 321ff0de066SYicong Yang return IRQ_HANDLED; 322ff0de066SYicong Yang } 323ff0de066SYicong Yang 324ff0de066SYicong Yang static void hisi_ptt_irq_free_vectors(void *pdev) 325ff0de066SYicong Yang { 326ff0de066SYicong Yang pci_free_irq_vectors(pdev); 327ff0de066SYicong Yang } 328ff0de066SYicong Yang 329ff0de066SYicong Yang static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt) 330ff0de066SYicong Yang { 331ff0de066SYicong Yang struct pci_dev *pdev = hisi_ptt->pdev; 332ff0de066SYicong Yang int ret; 333ff0de066SYicong Yang 334ff0de066SYicong Yang ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); 335ff0de066SYicong Yang if (ret < 0) { 336ff0de066SYicong Yang pci_err(pdev, "failed to allocate irq vector, ret = %d\n", ret); 337ff0de066SYicong Yang return ret; 338ff0de066SYicong Yang } 339ff0de066SYicong Yang 340ff0de066SYicong Yang ret = devm_add_action_or_reset(&pdev->dev, hisi_ptt_irq_free_vectors, pdev); 341ff0de066SYicong Yang if (ret < 0) 342ff0de066SYicong Yang return ret; 343ff0de066SYicong Yang 3446c50384eSYicong Yang hisi_ptt->trace_irq = pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ); 3456c50384eSYicong Yang ret = devm_request_threaded_irq(&pdev->dev, hisi_ptt->trace_irq, 346ff0de066SYicong Yang NULL, hisi_ptt_isr, 0, 347ff0de066SYicong Yang DRV_NAME, hisi_ptt); 348ff0de066SYicong Yang if (ret) { 349ff0de066SYicong Yang pci_err(pdev, "failed to request irq %d, ret = %d\n", 3506c50384eSYicong Yang hisi_ptt->trace_irq, ret); 351ff0de066SYicong Yang return ret; 352ff0de066SYicong Yang } 353ff0de066SYicong Yang 354ff0de066SYicong Yang return 0; 355ff0de066SYicong Yang } 356ff0de066SYicong Yang 357a3ecaba7SYicong Yang static void hisi_ptt_del_free_filter(struct hisi_ptt *hisi_ptt, 358a3ecaba7SYicong Yang struct hisi_ptt_filter_desc *filter) 359a3ecaba7SYicong Yang { 360556ef093SYicong Yang if (filter->is_port) 361556ef093SYicong Yang hisi_ptt->port_mask &= ~hisi_ptt_get_filter_val(filter->devid, true); 362556ef093SYicong Yang 363a3ecaba7SYicong Yang list_del(&filter->list); 364556ef093SYicong Yang kfree(filter->name); 365a3ecaba7SYicong Yang kfree(filter); 366a3ecaba7SYicong Yang } 367a3ecaba7SYicong Yang 368a3ecaba7SYicong Yang static struct hisi_ptt_filter_desc * 369556ef093SYicong Yang hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) 370a3ecaba7SYicong Yang { 371a3ecaba7SYicong Yang struct hisi_ptt_filter_desc *filter; 372556ef093SYicong Yang u8 devfn = devid & 0xff; 373556ef093SYicong Yang char *filter_name; 374556ef093SYicong Yang 375556ef093SYicong Yang filter_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d", pci_domain_nr(hisi_ptt->pdev->bus), 376556ef093SYicong Yang PCI_BUS_NUM(devid), PCI_SLOT(devfn), PCI_FUNC(devfn)); 377556ef093SYicong Yang if (!filter_name) { 378556ef093SYicong Yang pci_err(hisi_ptt->pdev, "failed to allocate name for filter %04x:%02x:%02x.%d\n", 379556ef093SYicong Yang pci_domain_nr(hisi_ptt->pdev->bus), PCI_BUS_NUM(devid), 380556ef093SYicong Yang PCI_SLOT(devfn), PCI_FUNC(devfn)); 381556ef093SYicong Yang return NULL; 382556ef093SYicong Yang } 383a3ecaba7SYicong Yang 384a3ecaba7SYicong Yang filter = kzalloc(sizeof(*filter), GFP_KERNEL); 385a3ecaba7SYicong Yang if (!filter) { 386a3ecaba7SYicong Yang pci_err(hisi_ptt->pdev, "failed to add filter for %s\n", 387556ef093SYicong Yang filter_name); 388556ef093SYicong Yang kfree(filter_name); 389a3ecaba7SYicong Yang return NULL; 390a3ecaba7SYicong Yang } 391a3ecaba7SYicong Yang 392556ef093SYicong Yang filter->name = filter_name; 393556ef093SYicong Yang filter->is_port = is_port; 394556ef093SYicong Yang filter->devid = devid; 395556ef093SYicong Yang 396a3ecaba7SYicong Yang if (filter->is_port) { 397a3ecaba7SYicong Yang list_add_tail(&filter->list, &hisi_ptt->port_filters); 398a3ecaba7SYicong Yang 399a3ecaba7SYicong Yang /* Update the available port mask */ 400a3ecaba7SYicong Yang hisi_ptt->port_mask |= hisi_ptt_get_filter_val(filter->devid, true); 401a3ecaba7SYicong Yang } else { 402a3ecaba7SYicong Yang list_add_tail(&filter->list, &hisi_ptt->req_filters); 403a3ecaba7SYicong Yang } 404a3ecaba7SYicong Yang 405a3ecaba7SYicong Yang return filter; 406a3ecaba7SYicong Yang } 407a3ecaba7SYicong Yang 4086373c463SYicong Yang static ssize_t hisi_ptt_filter_show(struct device *dev, struct device_attribute *attr, 4096373c463SYicong Yang char *buf) 4106373c463SYicong Yang { 4116373c463SYicong Yang struct hisi_ptt_filter_desc *filter; 4126373c463SYicong Yang unsigned long filter_val; 4136373c463SYicong Yang 4146373c463SYicong Yang filter = container_of(attr, struct hisi_ptt_filter_desc, attr); 4156373c463SYicong Yang filter_val = hisi_ptt_get_filter_val(filter->devid, filter->is_port) | 4166373c463SYicong Yang (filter->is_port ? HISI_PTT_PMU_FILTER_IS_PORT : 0); 4176373c463SYicong Yang 4186373c463SYicong Yang return sysfs_emit(buf, "0x%05lx\n", filter_val); 4196373c463SYicong Yang } 4206373c463SYicong Yang 4216373c463SYicong Yang static int hisi_ptt_create_rp_filter_attr(struct hisi_ptt *hisi_ptt, 4226373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4236373c463SYicong Yang { 4246373c463SYicong Yang struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; 4256373c463SYicong Yang 4266373c463SYicong Yang sysfs_attr_init(&filter->attr.attr); 4276373c463SYicong Yang filter->attr.attr.name = filter->name; 4286373c463SYicong Yang filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */ 4296373c463SYicong Yang filter->attr.show = hisi_ptt_filter_show; 4306373c463SYicong Yang 4316373c463SYicong Yang return sysfs_add_file_to_group(kobj, &filter->attr.attr, 4326373c463SYicong Yang HISI_PTT_RP_FILTERS_GRP_NAME); 4336373c463SYicong Yang } 4346373c463SYicong Yang 4356373c463SYicong Yang static void hisi_ptt_remove_rp_filter_attr(struct hisi_ptt *hisi_ptt, 4366373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4376373c463SYicong Yang { 4386373c463SYicong Yang struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; 4396373c463SYicong Yang 4406373c463SYicong Yang sysfs_remove_file_from_group(kobj, &filter->attr.attr, 4416373c463SYicong Yang HISI_PTT_RP_FILTERS_GRP_NAME); 4426373c463SYicong Yang } 4436373c463SYicong Yang 4446373c463SYicong Yang static int hisi_ptt_create_req_filter_attr(struct hisi_ptt *hisi_ptt, 4456373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4466373c463SYicong Yang { 4476373c463SYicong Yang struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; 4486373c463SYicong Yang 4496373c463SYicong Yang sysfs_attr_init(&filter->attr.attr); 4506373c463SYicong Yang filter->attr.attr.name = filter->name; 4516373c463SYicong Yang filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */ 4526373c463SYicong Yang filter->attr.show = hisi_ptt_filter_show; 4536373c463SYicong Yang 4546373c463SYicong Yang return sysfs_add_file_to_group(kobj, &filter->attr.attr, 4556373c463SYicong Yang HISI_PTT_REQ_FILTERS_GRP_NAME); 4566373c463SYicong Yang } 4576373c463SYicong Yang 4586373c463SYicong Yang static void hisi_ptt_remove_req_filter_attr(struct hisi_ptt *hisi_ptt, 4596373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4606373c463SYicong Yang { 4616373c463SYicong Yang struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; 4626373c463SYicong Yang 4636373c463SYicong Yang sysfs_remove_file_from_group(kobj, &filter->attr.attr, 4646373c463SYicong Yang HISI_PTT_REQ_FILTERS_GRP_NAME); 4656373c463SYicong Yang } 4666373c463SYicong Yang 4676373c463SYicong Yang static int hisi_ptt_create_filter_attr(struct hisi_ptt *hisi_ptt, 4686373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4696373c463SYicong Yang { 4706373c463SYicong Yang int ret; 4716373c463SYicong Yang 4726373c463SYicong Yang if (filter->is_port) 4736373c463SYicong Yang ret = hisi_ptt_create_rp_filter_attr(hisi_ptt, filter); 4746373c463SYicong Yang else 4756373c463SYicong Yang ret = hisi_ptt_create_req_filter_attr(hisi_ptt, filter); 4766373c463SYicong Yang 4776373c463SYicong Yang if (ret) 4786373c463SYicong Yang pci_err(hisi_ptt->pdev, "failed to create sysfs attribute for filter %s\n", 4796373c463SYicong Yang filter->name); 4806373c463SYicong Yang 4816373c463SYicong Yang return ret; 4826373c463SYicong Yang } 4836373c463SYicong Yang 4846373c463SYicong Yang static void hisi_ptt_remove_filter_attr(struct hisi_ptt *hisi_ptt, 4856373c463SYicong Yang struct hisi_ptt_filter_desc *filter) 4866373c463SYicong Yang { 4876373c463SYicong Yang if (filter->is_port) 4886373c463SYicong Yang hisi_ptt_remove_rp_filter_attr(hisi_ptt, filter); 4896373c463SYicong Yang else 4906373c463SYicong Yang hisi_ptt_remove_req_filter_attr(hisi_ptt, filter); 4916373c463SYicong Yang } 4926373c463SYicong Yang 4936373c463SYicong Yang static void hisi_ptt_remove_all_filter_attributes(void *data) 4946373c463SYicong Yang { 4956373c463SYicong Yang struct hisi_ptt_filter_desc *filter; 4966373c463SYicong Yang struct hisi_ptt *hisi_ptt = data; 4976373c463SYicong Yang 4986373c463SYicong Yang mutex_lock(&hisi_ptt->filter_lock); 4996373c463SYicong Yang 5006373c463SYicong Yang list_for_each_entry(filter, &hisi_ptt->req_filters, list) 5016373c463SYicong Yang hisi_ptt_remove_filter_attr(hisi_ptt, filter); 5026373c463SYicong Yang 5036373c463SYicong Yang list_for_each_entry(filter, &hisi_ptt->port_filters, list) 5046373c463SYicong Yang hisi_ptt_remove_filter_attr(hisi_ptt, filter); 5056373c463SYicong Yang 5066373c463SYicong Yang hisi_ptt->sysfs_inited = false; 5076373c463SYicong Yang mutex_unlock(&hisi_ptt->filter_lock); 5086373c463SYicong Yang } 5096373c463SYicong Yang 5106373c463SYicong Yang static int hisi_ptt_init_filter_attributes(struct hisi_ptt *hisi_ptt) 5116373c463SYicong Yang { 5126373c463SYicong Yang struct hisi_ptt_filter_desc *filter; 5136373c463SYicong Yang int ret; 5146373c463SYicong Yang 5156373c463SYicong Yang mutex_lock(&hisi_ptt->filter_lock); 5166373c463SYicong Yang 5176373c463SYicong Yang /* 5186373c463SYicong Yang * Register the reset callback in the first stage. In reset we traverse 5196373c463SYicong Yang * the filters list to remove the sysfs attributes so the callback can 5206373c463SYicong Yang * be called safely even without below filter attributes creation. 5216373c463SYicong Yang */ 5226373c463SYicong Yang ret = devm_add_action(&hisi_ptt->pdev->dev, 5236373c463SYicong Yang hisi_ptt_remove_all_filter_attributes, 5246373c463SYicong Yang hisi_ptt); 5256373c463SYicong Yang if (ret) 5266373c463SYicong Yang goto out; 5276373c463SYicong Yang 5286373c463SYicong Yang list_for_each_entry(filter, &hisi_ptt->port_filters, list) { 5296373c463SYicong Yang ret = hisi_ptt_create_filter_attr(hisi_ptt, filter); 5306373c463SYicong Yang if (ret) 5316373c463SYicong Yang goto out; 5326373c463SYicong Yang } 5336373c463SYicong Yang 5346373c463SYicong Yang list_for_each_entry(filter, &hisi_ptt->req_filters, list) { 5356373c463SYicong Yang ret = hisi_ptt_create_filter_attr(hisi_ptt, filter); 5366373c463SYicong Yang if (ret) 5376373c463SYicong Yang goto out; 5386373c463SYicong Yang } 5396373c463SYicong Yang 5406373c463SYicong Yang hisi_ptt->sysfs_inited = true; 5416373c463SYicong Yang out: 5426373c463SYicong Yang mutex_unlock(&hisi_ptt->filter_lock); 5436373c463SYicong Yang return ret; 5446373c463SYicong Yang } 5456373c463SYicong Yang 546556ef093SYicong Yang static void hisi_ptt_update_filters(struct work_struct *work) 547556ef093SYicong Yang { 548556ef093SYicong Yang struct delayed_work *delayed_work = to_delayed_work(work); 549556ef093SYicong Yang struct hisi_ptt_filter_update_info info; 550556ef093SYicong Yang struct hisi_ptt_filter_desc *filter; 551556ef093SYicong Yang struct hisi_ptt *hisi_ptt; 552556ef093SYicong Yang 553556ef093SYicong Yang hisi_ptt = container_of(delayed_work, struct hisi_ptt, work); 554556ef093SYicong Yang 555556ef093SYicong Yang if (!mutex_trylock(&hisi_ptt->filter_lock)) { 556556ef093SYicong Yang schedule_delayed_work(&hisi_ptt->work, HISI_PTT_WORK_DELAY_MS); 557556ef093SYicong Yang return; 558556ef093SYicong Yang } 559556ef093SYicong Yang 560556ef093SYicong Yang while (kfifo_get(&hisi_ptt->filter_update_kfifo, &info)) { 561556ef093SYicong Yang if (info.is_add) { 562556ef093SYicong Yang /* 563556ef093SYicong Yang * Notify the users if failed to add this filter, others 564556ef093SYicong Yang * still work and available. See the comments in 565556ef093SYicong Yang * hisi_ptt_init_filters(). 566556ef093SYicong Yang */ 567556ef093SYicong Yang filter = hisi_ptt_alloc_add_filter(hisi_ptt, info.devid, info.is_port); 568556ef093SYicong Yang if (!filter) 569556ef093SYicong Yang continue; 5706373c463SYicong Yang 5716373c463SYicong Yang /* 5726373c463SYicong Yang * If filters' sysfs entries hasn't been initialized, 5736373c463SYicong Yang * then we're still at probe stage. Add the filters to 5746373c463SYicong Yang * the list and later hisi_ptt_init_filter_attributes() 5756373c463SYicong Yang * will create sysfs attributes for all the filters. 5766373c463SYicong Yang */ 5776373c463SYicong Yang if (hisi_ptt->sysfs_inited && 5786373c463SYicong Yang hisi_ptt_create_filter_attr(hisi_ptt, filter)) { 5796373c463SYicong Yang hisi_ptt_del_free_filter(hisi_ptt, filter); 5806373c463SYicong Yang continue; 5816373c463SYicong Yang } 582556ef093SYicong Yang } else { 583556ef093SYicong Yang struct hisi_ptt_filter_desc *tmp; 584556ef093SYicong Yang struct list_head *target_list; 585556ef093SYicong Yang 586556ef093SYicong Yang target_list = info.is_port ? &hisi_ptt->port_filters : 587556ef093SYicong Yang &hisi_ptt->req_filters; 588556ef093SYicong Yang 589556ef093SYicong Yang list_for_each_entry_safe(filter, tmp, target_list, list) 590556ef093SYicong Yang if (filter->devid == info.devid) { 5916373c463SYicong Yang if (hisi_ptt->sysfs_inited) 5926373c463SYicong Yang hisi_ptt_remove_filter_attr(hisi_ptt, filter); 5936373c463SYicong Yang 594556ef093SYicong Yang hisi_ptt_del_free_filter(hisi_ptt, filter); 595556ef093SYicong Yang break; 596556ef093SYicong Yang } 597556ef093SYicong Yang } 598556ef093SYicong Yang } 599556ef093SYicong Yang 600556ef093SYicong Yang mutex_unlock(&hisi_ptt->filter_lock); 601556ef093SYicong Yang } 602556ef093SYicong Yang 603556ef093SYicong Yang /* 604556ef093SYicong Yang * A PCI bus notifier is used here for dynamically updating the filter 605556ef093SYicong Yang * list. 606556ef093SYicong Yang */ 607556ef093SYicong Yang static int hisi_ptt_notifier_call(struct notifier_block *nb, unsigned long action, 608556ef093SYicong Yang void *data) 609556ef093SYicong Yang { 610556ef093SYicong Yang struct hisi_ptt *hisi_ptt = container_of(nb, struct hisi_ptt, hisi_ptt_nb); 611556ef093SYicong Yang struct hisi_ptt_filter_update_info info; 612556ef093SYicong Yang struct pci_dev *pdev, *root_port; 613556ef093SYicong Yang struct device *dev = data; 614556ef093SYicong Yang u32 port_devid; 615556ef093SYicong Yang 616556ef093SYicong Yang pdev = to_pci_dev(dev); 617556ef093SYicong Yang root_port = pcie_find_root_port(pdev); 618556ef093SYicong Yang if (!root_port) 619556ef093SYicong Yang return 0; 620556ef093SYicong Yang 621484281bdSXiongfeng Wang port_devid = pci_dev_id(root_port); 622556ef093SYicong Yang if (port_devid < hisi_ptt->lower_bdf || 623556ef093SYicong Yang port_devid > hisi_ptt->upper_bdf) 624556ef093SYicong Yang return 0; 625556ef093SYicong Yang 626556ef093SYicong Yang info.is_port = pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT; 627484281bdSXiongfeng Wang info.devid = pci_dev_id(pdev); 628556ef093SYicong Yang 629556ef093SYicong Yang switch (action) { 630556ef093SYicong Yang case BUS_NOTIFY_ADD_DEVICE: 631556ef093SYicong Yang info.is_add = true; 632556ef093SYicong Yang break; 633556ef093SYicong Yang case BUS_NOTIFY_DEL_DEVICE: 634556ef093SYicong Yang info.is_add = false; 635556ef093SYicong Yang break; 636556ef093SYicong Yang default: 637556ef093SYicong Yang return 0; 638556ef093SYicong Yang } 639556ef093SYicong Yang 640556ef093SYicong Yang /* 641556ef093SYicong Yang * The FIFO size is 16 which is sufficient for almost all the cases, 642556ef093SYicong Yang * since each PCIe core will have most 8 Root Ports (typically only 643556ef093SYicong Yang * 1~4 Root Ports). On failure log the failed filter and let user 644556ef093SYicong Yang * handle it. 645556ef093SYicong Yang */ 646556ef093SYicong Yang if (kfifo_in_spinlocked(&hisi_ptt->filter_update_kfifo, &info, 1, 647556ef093SYicong Yang &hisi_ptt->filter_update_lock)) 648556ef093SYicong Yang schedule_delayed_work(&hisi_ptt->work, 0); 649556ef093SYicong Yang else 650556ef093SYicong Yang pci_warn(hisi_ptt->pdev, 651556ef093SYicong Yang "filter update fifo overflow for target %s\n", 652556ef093SYicong Yang pci_name(pdev)); 653556ef093SYicong Yang 654556ef093SYicong Yang return 0; 655556ef093SYicong Yang } 656556ef093SYicong Yang 657ff0de066SYicong Yang static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data) 658ff0de066SYicong Yang { 659b8d976c7SYicong Yang struct pci_dev *root_port = pcie_find_root_port(pdev); 660ff0de066SYicong Yang struct hisi_ptt_filter_desc *filter; 661ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = data; 662b8d976c7SYicong Yang u32 port_devid; 663b8d976c7SYicong Yang 664b8d976c7SYicong Yang if (!root_port) 665b8d976c7SYicong Yang return 0; 666b8d976c7SYicong Yang 667484281bdSXiongfeng Wang port_devid = pci_dev_id(root_port); 668b8d976c7SYicong Yang if (port_devid < hisi_ptt->lower_bdf || 669b8d976c7SYicong Yang port_devid > hisi_ptt->upper_bdf) 670b8d976c7SYicong Yang return 0; 671ff0de066SYicong Yang 672ff0de066SYicong Yang /* 673ff0de066SYicong Yang * We won't fail the probe if filter allocation failed here. The filters 674ff0de066SYicong Yang * should be partial initialized and users would know which filter fails 675ff0de066SYicong Yang * through the log. Other functions of PTT device are still available. 676ff0de066SYicong Yang */ 677484281bdSXiongfeng Wang filter = hisi_ptt_alloc_add_filter(hisi_ptt, pci_dev_id(pdev), 678556ef093SYicong Yang pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT); 679a3ecaba7SYicong Yang if (!filter) 680ff0de066SYicong Yang return -ENOMEM; 681ff0de066SYicong Yang 682ff0de066SYicong Yang return 0; 683ff0de066SYicong Yang } 684ff0de066SYicong Yang 685ff0de066SYicong Yang static void hisi_ptt_release_filters(void *data) 686ff0de066SYicong Yang { 687ff0de066SYicong Yang struct hisi_ptt_filter_desc *filter, *tmp; 688ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = data; 689ff0de066SYicong Yang 690a3ecaba7SYicong Yang list_for_each_entry_safe(filter, tmp, &hisi_ptt->req_filters, list) 691a3ecaba7SYicong Yang hisi_ptt_del_free_filter(hisi_ptt, filter); 692ff0de066SYicong Yang 693a3ecaba7SYicong Yang list_for_each_entry_safe(filter, tmp, &hisi_ptt->port_filters, list) 694a3ecaba7SYicong Yang hisi_ptt_del_free_filter(hisi_ptt, filter); 695ff0de066SYicong Yang } 696ff0de066SYicong Yang 697ff0de066SYicong Yang static int hisi_ptt_config_trace_buf(struct hisi_ptt *hisi_ptt) 698ff0de066SYicong Yang { 699ff0de066SYicong Yang struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; 700ff0de066SYicong Yang struct device *dev = &hisi_ptt->pdev->dev; 701ff0de066SYicong Yang int i; 702ff0de066SYicong Yang 703ff0de066SYicong Yang ctrl->trace_buf = devm_kcalloc(dev, HISI_PTT_TRACE_BUF_CNT, 704ff0de066SYicong Yang sizeof(*ctrl->trace_buf), GFP_KERNEL); 705ff0de066SYicong Yang if (!ctrl->trace_buf) 706ff0de066SYicong Yang return -ENOMEM; 707ff0de066SYicong Yang 708ff0de066SYicong Yang for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; ++i) { 709ff0de066SYicong Yang ctrl->trace_buf[i].addr = dmam_alloc_coherent(dev, HISI_PTT_TRACE_BUF_SIZE, 710ff0de066SYicong Yang &ctrl->trace_buf[i].dma, 711ff0de066SYicong Yang GFP_KERNEL); 712ff0de066SYicong Yang if (!ctrl->trace_buf[i].addr) 713ff0de066SYicong Yang return -ENOMEM; 714ff0de066SYicong Yang } 715ff0de066SYicong Yang 716ff0de066SYicong Yang /* Configure the trace DMA buffer */ 717ff0de066SYicong Yang for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) { 718ff0de066SYicong Yang writel(lower_32_bits(ctrl->trace_buf[i].dma), 719ff0de066SYicong Yang hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_BASE_LO_0 + 720ff0de066SYicong Yang i * HISI_PTT_TRACE_ADDR_STRIDE); 721ff0de066SYicong Yang writel(upper_32_bits(ctrl->trace_buf[i].dma), 722ff0de066SYicong Yang hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_BASE_HI_0 + 723ff0de066SYicong Yang i * HISI_PTT_TRACE_ADDR_STRIDE); 724ff0de066SYicong Yang } 725ff0de066SYicong Yang writel(HISI_PTT_TRACE_BUF_SIZE, hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_SIZE); 726ff0de066SYicong Yang 727ff0de066SYicong Yang return 0; 728ff0de066SYicong Yang } 729ff0de066SYicong Yang 730ff0de066SYicong Yang static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt) 731ff0de066SYicong Yang { 732ff0de066SYicong Yang struct pci_dev *pdev = hisi_ptt->pdev; 733ff0de066SYicong Yang struct pci_bus *bus; 734ff0de066SYicong Yang int ret; 735ff0de066SYicong Yang u32 reg; 736ff0de066SYicong Yang 737556ef093SYicong Yang INIT_DELAYED_WORK(&hisi_ptt->work, hisi_ptt_update_filters); 738556ef093SYicong Yang INIT_KFIFO(hisi_ptt->filter_update_kfifo); 739556ef093SYicong Yang spin_lock_init(&hisi_ptt->filter_update_lock); 740556ef093SYicong Yang 741ff0de066SYicong Yang INIT_LIST_HEAD(&hisi_ptt->port_filters); 742ff0de066SYicong Yang INIT_LIST_HEAD(&hisi_ptt->req_filters); 743556ef093SYicong Yang mutex_init(&hisi_ptt->filter_lock); 744ff0de066SYicong Yang 745ff0de066SYicong Yang ret = hisi_ptt_config_trace_buf(hisi_ptt); 746ff0de066SYicong Yang if (ret) 747ff0de066SYicong Yang return ret; 748ff0de066SYicong Yang 749ff0de066SYicong Yang /* 750ff0de066SYicong Yang * The device range register provides the information about the root 751ff0de066SYicong Yang * ports which the RCiEP can control and trace. The RCiEP and the root 752ff0de066SYicong Yang * ports which it supports are on the same PCIe core, with same domain 753ff0de066SYicong Yang * number but maybe different bus number. The device range register 754ff0de066SYicong Yang * will tell us which root ports we can support, Bit[31:16] indicates 755ff0de066SYicong Yang * the upper BDF numbers of the root port, while Bit[15:0] indicates 756ff0de066SYicong Yang * the lower. 757ff0de066SYicong Yang */ 758ff0de066SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_DEVICE_RANGE); 759ff0de066SYicong Yang hisi_ptt->upper_bdf = FIELD_GET(HISI_PTT_DEVICE_RANGE_UPPER, reg); 760ff0de066SYicong Yang hisi_ptt->lower_bdf = FIELD_GET(HISI_PTT_DEVICE_RANGE_LOWER, reg); 761ff0de066SYicong Yang 762ff0de066SYicong Yang bus = pci_find_bus(pci_domain_nr(pdev->bus), PCI_BUS_NUM(hisi_ptt->upper_bdf)); 763ff0de066SYicong Yang if (bus) 764ff0de066SYicong Yang pci_walk_bus(bus, hisi_ptt_init_filters, hisi_ptt); 765ff0de066SYicong Yang 766ff0de066SYicong Yang ret = devm_add_action_or_reset(&pdev->dev, hisi_ptt_release_filters, hisi_ptt); 767ff0de066SYicong Yang if (ret) 768ff0de066SYicong Yang return ret; 769ff0de066SYicong Yang 770ff0de066SYicong Yang hisi_ptt->trace_ctrl.on_cpu = -1; 771ff0de066SYicong Yang return 0; 772ff0de066SYicong Yang } 773ff0de066SYicong Yang 774ff0de066SYicong Yang static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, 775ff0de066SYicong Yang char *buf) 776ff0de066SYicong Yang { 777ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); 778ff0de066SYicong Yang const cpumask_t *cpumask = cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)); 779ff0de066SYicong Yang 780ff0de066SYicong Yang return cpumap_print_to_pagebuf(true, buf, cpumask); 781ff0de066SYicong Yang } 782ff0de066SYicong Yang static DEVICE_ATTR_RO(cpumask); 783ff0de066SYicong Yang 784ff0de066SYicong Yang static struct attribute *hisi_ptt_cpumask_attrs[] = { 785ff0de066SYicong Yang &dev_attr_cpumask.attr, 786ff0de066SYicong Yang NULL 787ff0de066SYicong Yang }; 788ff0de066SYicong Yang 789ff0de066SYicong Yang static const struct attribute_group hisi_ptt_cpumask_attr_group = { 790ff0de066SYicong Yang .attrs = hisi_ptt_cpumask_attrs, 791ff0de066SYicong Yang }; 792ff0de066SYicong Yang 793ff0de066SYicong Yang /* 794ff0de066SYicong Yang * Bit 19 indicates the filter type, 1 for Root Port filter and 0 for Requester 795ff0de066SYicong Yang * filter. Bit[15:0] indicates the filter value, for Root Port filter it's 796ff0de066SYicong Yang * a bit mask of desired ports and for Requester filter it's the Requester ID 797ff0de066SYicong Yang * of the desired PCIe function. Bit[18:16] is reserved for extension. 798ff0de066SYicong Yang * 799ff0de066SYicong Yang * See hisi_ptt.rst documentation for detailed information. 800ff0de066SYicong Yang */ 801ff0de066SYicong Yang PMU_FORMAT_ATTR(filter, "config:0-19"); 802ff0de066SYicong Yang PMU_FORMAT_ATTR(direction, "config:20-23"); 803ff0de066SYicong Yang PMU_FORMAT_ATTR(type, "config:24-31"); 804ff0de066SYicong Yang PMU_FORMAT_ATTR(format, "config:32-35"); 805ff0de066SYicong Yang 806ff0de066SYicong Yang static struct attribute *hisi_ptt_pmu_format_attrs[] = { 807ff0de066SYicong Yang &format_attr_filter.attr, 808ff0de066SYicong Yang &format_attr_direction.attr, 809ff0de066SYicong Yang &format_attr_type.attr, 810ff0de066SYicong Yang &format_attr_format.attr, 811ff0de066SYicong Yang NULL 812ff0de066SYicong Yang }; 813ff0de066SYicong Yang 814ff0de066SYicong Yang static struct attribute_group hisi_ptt_pmu_format_group = { 815ff0de066SYicong Yang .name = "format", 816ff0de066SYicong Yang .attrs = hisi_ptt_pmu_format_attrs, 817ff0de066SYicong Yang }; 818ff0de066SYicong Yang 8196373c463SYicong Yang static ssize_t hisi_ptt_filter_multiselect_show(struct device *dev, 8206373c463SYicong Yang struct device_attribute *attr, 8216373c463SYicong Yang char *buf) 8226373c463SYicong Yang { 8236373c463SYicong Yang struct dev_ext_attribute *ext_attr; 8246373c463SYicong Yang 8256373c463SYicong Yang ext_attr = container_of(attr, struct dev_ext_attribute, attr); 8266373c463SYicong Yang return sysfs_emit(buf, "%s\n", (char *)ext_attr->var); 8276373c463SYicong Yang } 8286373c463SYicong Yang 8296373c463SYicong Yang static struct dev_ext_attribute root_port_filters_multiselect = { 8306373c463SYicong Yang .attr = { 8316373c463SYicong Yang .attr = { .name = "multiselect", .mode = 0400 }, 8326373c463SYicong Yang .show = hisi_ptt_filter_multiselect_show, 8336373c463SYicong Yang }, 8346373c463SYicong Yang .var = "1", 8356373c463SYicong Yang }; 8366373c463SYicong Yang 8376373c463SYicong Yang static struct attribute *hisi_ptt_pmu_root_ports_attrs[] = { 8386373c463SYicong Yang &root_port_filters_multiselect.attr.attr, 8396373c463SYicong Yang NULL 8406373c463SYicong Yang }; 8416373c463SYicong Yang 8426373c463SYicong Yang static struct attribute_group hisi_ptt_pmu_root_ports_group = { 8436373c463SYicong Yang .name = HISI_PTT_RP_FILTERS_GRP_NAME, 8446373c463SYicong Yang .attrs = hisi_ptt_pmu_root_ports_attrs, 8456373c463SYicong Yang }; 8466373c463SYicong Yang 8476373c463SYicong Yang static struct dev_ext_attribute requester_filters_multiselect = { 8486373c463SYicong Yang .attr = { 8496373c463SYicong Yang .attr = { .name = "multiselect", .mode = 0400 }, 8506373c463SYicong Yang .show = hisi_ptt_filter_multiselect_show, 8516373c463SYicong Yang }, 8526373c463SYicong Yang .var = "0", 8536373c463SYicong Yang }; 8546373c463SYicong Yang 8556373c463SYicong Yang static struct attribute *hisi_ptt_pmu_requesters_attrs[] = { 8566373c463SYicong Yang &requester_filters_multiselect.attr.attr, 8576373c463SYicong Yang NULL 8586373c463SYicong Yang }; 8596373c463SYicong Yang 8606373c463SYicong Yang static struct attribute_group hisi_ptt_pmu_requesters_group = { 8616373c463SYicong Yang .name = HISI_PTT_REQ_FILTERS_GRP_NAME, 8626373c463SYicong Yang .attrs = hisi_ptt_pmu_requesters_attrs, 8636373c463SYicong Yang }; 8646373c463SYicong Yang 865ff0de066SYicong Yang static const struct attribute_group *hisi_ptt_pmu_groups[] = { 866ff0de066SYicong Yang &hisi_ptt_cpumask_attr_group, 867ff0de066SYicong Yang &hisi_ptt_pmu_format_group, 8685ca57b03SYicong Yang &hisi_ptt_tune_group, 8696373c463SYicong Yang &hisi_ptt_pmu_root_ports_group, 8706373c463SYicong Yang &hisi_ptt_pmu_requesters_group, 871ff0de066SYicong Yang NULL 872ff0de066SYicong Yang }; 873ff0de066SYicong Yang 874ff0de066SYicong Yang static int hisi_ptt_trace_valid_direction(u32 val) 875ff0de066SYicong Yang { 876ff0de066SYicong Yang /* 877ff0de066SYicong Yang * The direction values have different effects according to the data 878ff0de066SYicong Yang * format (specified in the parentheses). TLP set A/B means different 879ff0de066SYicong Yang * set of TLP types. See hisi_ptt.rst documentation for more details. 880ff0de066SYicong Yang */ 881ff0de066SYicong Yang static const u32 hisi_ptt_trace_available_direction[] = { 882ff0de066SYicong Yang 0, /* inbound(4DW) or reserved(8DW) */ 883ff0de066SYicong Yang 1, /* outbound(4DW) */ 884ff0de066SYicong Yang 2, /* {in, out}bound(4DW) or inbound(8DW), TLP set A */ 885ff0de066SYicong Yang 3, /* {in, out}bound(4DW) or inbound(8DW), TLP set B */ 886ff0de066SYicong Yang }; 887ff0de066SYicong Yang int i; 888ff0de066SYicong Yang 889ff0de066SYicong Yang for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_available_direction); i++) { 890ff0de066SYicong Yang if (val == hisi_ptt_trace_available_direction[i]) 891ff0de066SYicong Yang return 0; 892ff0de066SYicong Yang } 893ff0de066SYicong Yang 894ff0de066SYicong Yang return -EINVAL; 895ff0de066SYicong Yang } 896ff0de066SYicong Yang 897ff0de066SYicong Yang static int hisi_ptt_trace_valid_type(u32 val) 898ff0de066SYicong Yang { 899ff0de066SYicong Yang /* Different types can be set simultaneously */ 900ff0de066SYicong Yang static const u32 hisi_ptt_trace_available_type[] = { 901ff0de066SYicong Yang 1, /* posted_request */ 902ff0de066SYicong Yang 2, /* non-posted_request */ 903ff0de066SYicong Yang 4, /* completion */ 904ff0de066SYicong Yang }; 905ff0de066SYicong Yang int i; 906ff0de066SYicong Yang 907ff0de066SYicong Yang if (!val) 908ff0de066SYicong Yang return -EINVAL; 909ff0de066SYicong Yang 910ff0de066SYicong Yang /* 911ff0de066SYicong Yang * Walk the available list and clear the valid bits of 912ff0de066SYicong Yang * the config. If there is any resident bit after the 913ff0de066SYicong Yang * walk then the config is invalid. 914ff0de066SYicong Yang */ 915ff0de066SYicong Yang for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_available_type); i++) 916ff0de066SYicong Yang val &= ~hisi_ptt_trace_available_type[i]; 917ff0de066SYicong Yang 918ff0de066SYicong Yang if (val) 919ff0de066SYicong Yang return -EINVAL; 920ff0de066SYicong Yang 921ff0de066SYicong Yang return 0; 922ff0de066SYicong Yang } 923ff0de066SYicong Yang 924ff0de066SYicong Yang static int hisi_ptt_trace_valid_format(u32 val) 925ff0de066SYicong Yang { 926ff0de066SYicong Yang static const u32 hisi_ptt_trace_availble_format[] = { 927ff0de066SYicong Yang 0, /* 4DW */ 928ff0de066SYicong Yang 1, /* 8DW */ 929ff0de066SYicong Yang }; 930ff0de066SYicong Yang int i; 931ff0de066SYicong Yang 932ff0de066SYicong Yang for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_availble_format); i++) { 933ff0de066SYicong Yang if (val == hisi_ptt_trace_availble_format[i]) 934ff0de066SYicong Yang return 0; 935ff0de066SYicong Yang } 936ff0de066SYicong Yang 937ff0de066SYicong Yang return -EINVAL; 938ff0de066SYicong Yang } 939ff0de066SYicong Yang 940ff0de066SYicong Yang static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config) 941ff0de066SYicong Yang { 942ff0de066SYicong Yang unsigned long val, port_mask = hisi_ptt->port_mask; 943ff0de066SYicong Yang struct hisi_ptt_filter_desc *filter; 944556ef093SYicong Yang int ret = 0; 945ff0de066SYicong Yang 946ff0de066SYicong Yang hisi_ptt->trace_ctrl.is_port = FIELD_GET(HISI_PTT_PMU_FILTER_IS_PORT, config); 947ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, config); 948ff0de066SYicong Yang 949ff0de066SYicong Yang /* 950ff0de066SYicong Yang * Port filters are defined as bit mask. For port filters, check 951ff0de066SYicong Yang * the bits in the @val are within the range of hisi_ptt->port_mask 952ff0de066SYicong Yang * and whether it's empty or not, otherwise user has specified 953ff0de066SYicong Yang * some unsupported root ports. 954ff0de066SYicong Yang * 955ff0de066SYicong Yang * For Requester ID filters, walk the available filter list to see 956ff0de066SYicong Yang * whether we have one matched. 957ff0de066SYicong Yang */ 958556ef093SYicong Yang mutex_lock(&hisi_ptt->filter_lock); 959ff0de066SYicong Yang if (!hisi_ptt->trace_ctrl.is_port) { 960ff0de066SYicong Yang list_for_each_entry(filter, &hisi_ptt->req_filters, list) { 961ff0de066SYicong Yang if (val == hisi_ptt_get_filter_val(filter->devid, filter->is_port)) 962556ef093SYicong Yang goto out; 963ff0de066SYicong Yang } 964ff0de066SYicong Yang } else if (bitmap_subset(&val, &port_mask, BITS_PER_LONG)) { 965556ef093SYicong Yang goto out; 966ff0de066SYicong Yang } 967ff0de066SYicong Yang 968556ef093SYicong Yang ret = -EINVAL; 969556ef093SYicong Yang out: 970556ef093SYicong Yang mutex_unlock(&hisi_ptt->filter_lock); 971556ef093SYicong Yang return ret; 972ff0de066SYicong Yang } 973ff0de066SYicong Yang 974ff0de066SYicong Yang static void hisi_ptt_pmu_init_configs(struct hisi_ptt *hisi_ptt, struct perf_event *event) 975ff0de066SYicong Yang { 976ff0de066SYicong Yang struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; 977ff0de066SYicong Yang u32 val; 978ff0de066SYicong Yang 979ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, event->attr.config); 980ff0de066SYicong Yang hisi_ptt->trace_ctrl.filter = val; 981ff0de066SYicong Yang 982ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_DIRECTION_MASK, event->attr.config); 983ff0de066SYicong Yang ctrl->direction = val; 984ff0de066SYicong Yang 985ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_TYPE_MASK, event->attr.config); 986ff0de066SYicong Yang ctrl->type = val; 987ff0de066SYicong Yang 988ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_FORMAT_MASK, event->attr.config); 989ff0de066SYicong Yang ctrl->format = val; 990ff0de066SYicong Yang } 991ff0de066SYicong Yang 992ff0de066SYicong Yang static int hisi_ptt_pmu_event_init(struct perf_event *event) 993ff0de066SYicong Yang { 994ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); 995ff0de066SYicong Yang int ret; 996ff0de066SYicong Yang u32 val; 997ff0de066SYicong Yang 998ff0de066SYicong Yang if (event->cpu < 0) { 999ff0de066SYicong Yang dev_dbg(event->pmu->dev, "Per-task mode not supported\n"); 1000ff0de066SYicong Yang return -EOPNOTSUPP; 1001ff0de066SYicong Yang } 1002ff0de066SYicong Yang 1003ff0de066SYicong Yang if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type) 1004ff0de066SYicong Yang return -ENOENT; 1005ff0de066SYicong Yang 1006ff0de066SYicong Yang ret = hisi_ptt_trace_valid_filter(hisi_ptt, event->attr.config); 1007ff0de066SYicong Yang if (ret < 0) 1008ff0de066SYicong Yang return ret; 1009ff0de066SYicong Yang 1010ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_DIRECTION_MASK, event->attr.config); 1011ff0de066SYicong Yang ret = hisi_ptt_trace_valid_direction(val); 1012ff0de066SYicong Yang if (ret < 0) 1013ff0de066SYicong Yang return ret; 1014ff0de066SYicong Yang 1015ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_TYPE_MASK, event->attr.config); 1016ff0de066SYicong Yang ret = hisi_ptt_trace_valid_type(val); 1017ff0de066SYicong Yang if (ret < 0) 1018ff0de066SYicong Yang return ret; 1019ff0de066SYicong Yang 1020ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_FORMAT_MASK, event->attr.config); 1021ff0de066SYicong Yang return hisi_ptt_trace_valid_format(val); 1022ff0de066SYicong Yang } 1023ff0de066SYicong Yang 1024ff0de066SYicong Yang static void *hisi_ptt_pmu_setup_aux(struct perf_event *event, void **pages, 1025ff0de066SYicong Yang int nr_pages, bool overwrite) 1026ff0de066SYicong Yang { 1027ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf; 1028ff0de066SYicong Yang struct page **pagelist; 1029ff0de066SYicong Yang int i; 1030ff0de066SYicong Yang 1031ff0de066SYicong Yang if (overwrite) { 1032ff0de066SYicong Yang dev_warn(event->pmu->dev, "Overwrite mode is not supported\n"); 1033ff0de066SYicong Yang return NULL; 1034ff0de066SYicong Yang } 1035ff0de066SYicong Yang 1036ff0de066SYicong Yang /* If the pages size less than buffers, we cannot start trace */ 1037ff0de066SYicong Yang if (nr_pages < HISI_PTT_TRACE_TOTAL_BUF_SIZE / PAGE_SIZE) 1038ff0de066SYicong Yang return NULL; 1039ff0de066SYicong Yang 1040ff0de066SYicong Yang buf = kzalloc(sizeof(*buf), GFP_KERNEL); 1041ff0de066SYicong Yang if (!buf) 1042ff0de066SYicong Yang return NULL; 1043ff0de066SYicong Yang 1044ff0de066SYicong Yang pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL); 1045ff0de066SYicong Yang if (!pagelist) 1046ff0de066SYicong Yang goto err; 1047ff0de066SYicong Yang 1048ff0de066SYicong Yang for (i = 0; i < nr_pages; i++) 1049ff0de066SYicong Yang pagelist[i] = virt_to_page(pages[i]); 1050ff0de066SYicong Yang 1051ff0de066SYicong Yang buf->base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL); 1052ff0de066SYicong Yang if (!buf->base) { 1053ff0de066SYicong Yang kfree(pagelist); 1054ff0de066SYicong Yang goto err; 1055ff0de066SYicong Yang } 1056ff0de066SYicong Yang 1057ff0de066SYicong Yang buf->nr_pages = nr_pages; 1058ff0de066SYicong Yang buf->length = nr_pages * PAGE_SIZE; 1059ff0de066SYicong Yang buf->pos = 0; 1060ff0de066SYicong Yang 1061ff0de066SYicong Yang kfree(pagelist); 1062ff0de066SYicong Yang return buf; 1063ff0de066SYicong Yang err: 1064ff0de066SYicong Yang kfree(buf); 1065ff0de066SYicong Yang return NULL; 1066ff0de066SYicong Yang } 1067ff0de066SYicong Yang 1068ff0de066SYicong Yang static void hisi_ptt_pmu_free_aux(void *aux) 1069ff0de066SYicong Yang { 1070ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf = aux; 1071ff0de066SYicong Yang 1072ff0de066SYicong Yang vunmap(buf->base); 1073ff0de066SYicong Yang kfree(buf); 1074ff0de066SYicong Yang } 1075ff0de066SYicong Yang 1076ff0de066SYicong Yang static void hisi_ptt_pmu_start(struct perf_event *event, int flags) 1077ff0de066SYicong Yang { 1078ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); 1079ff0de066SYicong Yang struct perf_output_handle *handle = &hisi_ptt->trace_ctrl.handle; 1080ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw; 1081ff0de066SYicong Yang struct device *dev = event->pmu->dev; 1082ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf; 1083ff0de066SYicong Yang int cpu = event->cpu; 1084ff0de066SYicong Yang int ret; 1085ff0de066SYicong Yang 1086ff0de066SYicong Yang hwc->state = 0; 1087ff0de066SYicong Yang 1088ff0de066SYicong Yang /* Serialize the perf process if user specified several CPUs */ 1089ff0de066SYicong Yang spin_lock(&hisi_ptt->pmu_lock); 1090ff0de066SYicong Yang if (hisi_ptt->trace_ctrl.started) { 1091ff0de066SYicong Yang dev_dbg(dev, "trace has already started\n"); 1092ff0de066SYicong Yang goto stop; 1093ff0de066SYicong Yang } 1094ff0de066SYicong Yang 1095ff0de066SYicong Yang /* 1096ff0de066SYicong Yang * Handle the interrupt on the same cpu which starts the trace to avoid 1097ff0de066SYicong Yang * context mismatch. Otherwise we'll trigger the WARN from the perf 1098ff0de066SYicong Yang * core in event_function_local(). If CPU passed is offline we'll fail 1099ff0de066SYicong Yang * here, just log it since we can do nothing here. 1100ff0de066SYicong Yang */ 11016c50384eSYicong Yang ret = irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(cpu)); 1102ff0de066SYicong Yang if (ret) 1103ff0de066SYicong Yang dev_warn(dev, "failed to set the affinity of trace interrupt\n"); 1104ff0de066SYicong Yang 1105ff0de066SYicong Yang hisi_ptt->trace_ctrl.on_cpu = cpu; 1106ff0de066SYicong Yang 1107ff0de066SYicong Yang buf = perf_aux_output_begin(handle, event); 1108ff0de066SYicong Yang if (!buf) { 1109ff0de066SYicong Yang dev_dbg(dev, "aux output begin failed\n"); 1110ff0de066SYicong Yang goto stop; 1111ff0de066SYicong Yang } 1112ff0de066SYicong Yang 1113ff0de066SYicong Yang buf->pos = handle->head % buf->length; 1114ff0de066SYicong Yang 1115ff0de066SYicong Yang hisi_ptt_pmu_init_configs(hisi_ptt, event); 1116ff0de066SYicong Yang 1117ff0de066SYicong Yang ret = hisi_ptt_trace_start(hisi_ptt); 1118ff0de066SYicong Yang if (ret) { 1119ff0de066SYicong Yang dev_dbg(dev, "trace start failed, ret = %d\n", ret); 1120ff0de066SYicong Yang perf_aux_output_end(handle, 0); 1121ff0de066SYicong Yang goto stop; 1122ff0de066SYicong Yang } 1123ff0de066SYicong Yang 1124ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock); 1125ff0de066SYicong Yang return; 1126ff0de066SYicong Yang stop: 1127ff0de066SYicong Yang event->hw.state |= PERF_HES_STOPPED; 1128ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock); 1129ff0de066SYicong Yang } 1130ff0de066SYicong Yang 1131ff0de066SYicong Yang static void hisi_ptt_pmu_stop(struct perf_event *event, int flags) 1132ff0de066SYicong Yang { 1133ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); 1134ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw; 1135ff0de066SYicong Yang 1136ff0de066SYicong Yang if (hwc->state & PERF_HES_STOPPED) 1137ff0de066SYicong Yang return; 1138ff0de066SYicong Yang 1139ff0de066SYicong Yang spin_lock(&hisi_ptt->pmu_lock); 1140ff0de066SYicong Yang if (hisi_ptt->trace_ctrl.started) { 1141ff0de066SYicong Yang hisi_ptt_trace_end(hisi_ptt); 1142ff0de066SYicong Yang 1143ff0de066SYicong Yang if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) 1144ff0de066SYicong Yang dev_warn(event->pmu->dev, "Device is still busy\n"); 1145ff0de066SYicong Yang 1146ff0de066SYicong Yang hisi_ptt_update_aux(hisi_ptt, hisi_ptt->trace_ctrl.buf_index, true); 1147ff0de066SYicong Yang } 1148ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock); 1149ff0de066SYicong Yang 1150ff0de066SYicong Yang hwc->state |= PERF_HES_STOPPED; 1151ff0de066SYicong Yang perf_event_update_userpage(event); 1152ff0de066SYicong Yang hwc->state |= PERF_HES_UPTODATE; 1153ff0de066SYicong Yang } 1154ff0de066SYicong Yang 1155ff0de066SYicong Yang static int hisi_ptt_pmu_add(struct perf_event *event, int flags) 1156ff0de066SYicong Yang { 1157ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); 1158ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw; 1159ff0de066SYicong Yang int cpu = event->cpu; 1160ff0de066SYicong Yang 1161ff0de066SYicong Yang /* Only allow the cpus on the device's node to add the event */ 1162ff0de066SYicong Yang if (!cpumask_test_cpu(cpu, cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)))) 1163ff0de066SYicong Yang return 0; 1164ff0de066SYicong Yang 1165ff0de066SYicong Yang hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 1166ff0de066SYicong Yang 1167ff0de066SYicong Yang if (flags & PERF_EF_START) { 1168ff0de066SYicong Yang hisi_ptt_pmu_start(event, PERF_EF_RELOAD); 1169ff0de066SYicong Yang if (hwc->state & PERF_HES_STOPPED) 1170ff0de066SYicong Yang return -EINVAL; 1171ff0de066SYicong Yang } 1172ff0de066SYicong Yang 1173ff0de066SYicong Yang return 0; 1174ff0de066SYicong Yang } 1175ff0de066SYicong Yang 1176ff0de066SYicong Yang static void hisi_ptt_pmu_del(struct perf_event *event, int flags) 1177ff0de066SYicong Yang { 1178ff0de066SYicong Yang hisi_ptt_pmu_stop(event, PERF_EF_UPDATE); 1179ff0de066SYicong Yang } 1180ff0de066SYicong Yang 1181*359d3fbcSJunhao He static void hisi_ptt_pmu_read(struct perf_event *event) 1182*359d3fbcSJunhao He { 1183*359d3fbcSJunhao He } 1184*359d3fbcSJunhao He 1185ff0de066SYicong Yang static void hisi_ptt_remove_cpuhp_instance(void *hotplug_node) 1186ff0de066SYicong Yang { 1187ff0de066SYicong Yang cpuhp_state_remove_instance_nocalls(hisi_ptt_pmu_online, hotplug_node); 1188ff0de066SYicong Yang } 1189ff0de066SYicong Yang 1190ff0de066SYicong Yang static void hisi_ptt_unregister_pmu(void *pmu) 1191ff0de066SYicong Yang { 1192ff0de066SYicong Yang perf_pmu_unregister(pmu); 1193ff0de066SYicong Yang } 1194ff0de066SYicong Yang 1195ff0de066SYicong Yang static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt) 1196ff0de066SYicong Yang { 1197ff0de066SYicong Yang u16 core_id, sicl_id; 1198ff0de066SYicong Yang char *pmu_name; 1199ff0de066SYicong Yang u32 reg; 1200ff0de066SYicong Yang int ret; 1201ff0de066SYicong Yang 1202ff0de066SYicong Yang ret = cpuhp_state_add_instance_nocalls(hisi_ptt_pmu_online, 1203ff0de066SYicong Yang &hisi_ptt->hotplug_node); 1204ff0de066SYicong Yang if (ret) 1205ff0de066SYicong Yang return ret; 1206ff0de066SYicong Yang 1207ff0de066SYicong Yang ret = devm_add_action_or_reset(&hisi_ptt->pdev->dev, 1208ff0de066SYicong Yang hisi_ptt_remove_cpuhp_instance, 1209ff0de066SYicong Yang &hisi_ptt->hotplug_node); 1210ff0de066SYicong Yang if (ret) 1211ff0de066SYicong Yang return ret; 1212ff0de066SYicong Yang 12135ca57b03SYicong Yang mutex_init(&hisi_ptt->tune_lock); 1214ff0de066SYicong Yang spin_lock_init(&hisi_ptt->pmu_lock); 1215ff0de066SYicong Yang 1216ff0de066SYicong Yang hisi_ptt->hisi_ptt_pmu = (struct pmu) { 1217ff0de066SYicong Yang .module = THIS_MODULE, 121845c90292SYicong Yang .capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_NO_EXCLUDE, 1219ff0de066SYicong Yang .task_ctx_nr = perf_sw_context, 1220ff0de066SYicong Yang .attr_groups = hisi_ptt_pmu_groups, 1221ff0de066SYicong Yang .event_init = hisi_ptt_pmu_event_init, 1222ff0de066SYicong Yang .setup_aux = hisi_ptt_pmu_setup_aux, 1223ff0de066SYicong Yang .free_aux = hisi_ptt_pmu_free_aux, 1224ff0de066SYicong Yang .start = hisi_ptt_pmu_start, 1225ff0de066SYicong Yang .stop = hisi_ptt_pmu_stop, 1226ff0de066SYicong Yang .add = hisi_ptt_pmu_add, 1227ff0de066SYicong Yang .del = hisi_ptt_pmu_del, 1228*359d3fbcSJunhao He .read = hisi_ptt_pmu_read, 1229ff0de066SYicong Yang }; 1230ff0de066SYicong Yang 1231ff0de066SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_LOCATION); 1232ff0de066SYicong Yang core_id = FIELD_GET(HISI_PTT_CORE_ID, reg); 1233ff0de066SYicong Yang sicl_id = FIELD_GET(HISI_PTT_SICL_ID, reg); 1234ff0de066SYicong Yang 1235ff0de066SYicong Yang pmu_name = devm_kasprintf(&hisi_ptt->pdev->dev, GFP_KERNEL, "hisi_ptt%u_%u", 1236ff0de066SYicong Yang sicl_id, core_id); 1237ff0de066SYicong Yang if (!pmu_name) 1238ff0de066SYicong Yang return -ENOMEM; 1239ff0de066SYicong Yang 1240ff0de066SYicong Yang ret = perf_pmu_register(&hisi_ptt->hisi_ptt_pmu, pmu_name, -1); 1241ff0de066SYicong Yang if (ret) 1242ff0de066SYicong Yang return ret; 1243ff0de066SYicong Yang 1244ff0de066SYicong Yang return devm_add_action_or_reset(&hisi_ptt->pdev->dev, 1245ff0de066SYicong Yang hisi_ptt_unregister_pmu, 1246ff0de066SYicong Yang &hisi_ptt->hisi_ptt_pmu); 1247ff0de066SYicong Yang } 1248ff0de066SYicong Yang 1249556ef093SYicong Yang static void hisi_ptt_unregister_filter_update_notifier(void *data) 1250556ef093SYicong Yang { 1251556ef093SYicong Yang struct hisi_ptt *hisi_ptt = data; 1252556ef093SYicong Yang 1253556ef093SYicong Yang bus_unregister_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb); 1254556ef093SYicong Yang 1255556ef093SYicong Yang /* Cancel any work that has been queued */ 1256556ef093SYicong Yang cancel_delayed_work_sync(&hisi_ptt->work); 1257556ef093SYicong Yang } 1258556ef093SYicong Yang 1259556ef093SYicong Yang /* Register the bus notifier for dynamically updating the filter list */ 1260556ef093SYicong Yang static int hisi_ptt_register_filter_update_notifier(struct hisi_ptt *hisi_ptt) 1261556ef093SYicong Yang { 1262556ef093SYicong Yang int ret; 1263556ef093SYicong Yang 1264556ef093SYicong Yang hisi_ptt->hisi_ptt_nb.notifier_call = hisi_ptt_notifier_call; 1265556ef093SYicong Yang ret = bus_register_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb); 1266556ef093SYicong Yang if (ret) 1267556ef093SYicong Yang return ret; 1268556ef093SYicong Yang 1269556ef093SYicong Yang return devm_add_action_or_reset(&hisi_ptt->pdev->dev, 1270556ef093SYicong Yang hisi_ptt_unregister_filter_update_notifier, 1271556ef093SYicong Yang hisi_ptt); 1272556ef093SYicong Yang } 1273556ef093SYicong Yang 1274ff0de066SYicong Yang /* 1275ff0de066SYicong Yang * The DMA of PTT trace can only use direct mappings due to some 1276ff0de066SYicong Yang * hardware restriction. Check whether there is no IOMMU or the 1277ff0de066SYicong Yang * policy of the IOMMU domain is passthrough, otherwise the trace 1278ff0de066SYicong Yang * cannot work. 1279ff0de066SYicong Yang * 1280ff0de066SYicong Yang * The PTT device is supposed to behind an ARM SMMUv3, which 1281ff0de066SYicong Yang * should have passthrough the device by a quirk. 1282ff0de066SYicong Yang */ 1283ff0de066SYicong Yang static int hisi_ptt_check_iommu_mapping(struct pci_dev *pdev) 1284ff0de066SYicong Yang { 1285ff0de066SYicong Yang struct iommu_domain *iommu_domain; 1286ff0de066SYicong Yang 1287ff0de066SYicong Yang iommu_domain = iommu_get_domain_for_dev(&pdev->dev); 1288ff0de066SYicong Yang if (!iommu_domain || iommu_domain->type == IOMMU_DOMAIN_IDENTITY) 1289ff0de066SYicong Yang return 0; 1290ff0de066SYicong Yang 1291ff0de066SYicong Yang return -EOPNOTSUPP; 1292ff0de066SYicong Yang } 1293ff0de066SYicong Yang 1294ff0de066SYicong Yang static int hisi_ptt_probe(struct pci_dev *pdev, 1295ff0de066SYicong Yang const struct pci_device_id *id) 1296ff0de066SYicong Yang { 1297ff0de066SYicong Yang struct hisi_ptt *hisi_ptt; 1298ff0de066SYicong Yang int ret; 1299ff0de066SYicong Yang 1300ff0de066SYicong Yang ret = hisi_ptt_check_iommu_mapping(pdev); 1301ff0de066SYicong Yang if (ret) { 1302ff0de066SYicong Yang pci_err(pdev, "requires direct DMA mappings\n"); 1303ff0de066SYicong Yang return ret; 1304ff0de066SYicong Yang } 1305ff0de066SYicong Yang 1306ff0de066SYicong Yang hisi_ptt = devm_kzalloc(&pdev->dev, sizeof(*hisi_ptt), GFP_KERNEL); 1307ff0de066SYicong Yang if (!hisi_ptt) 1308ff0de066SYicong Yang return -ENOMEM; 1309ff0de066SYicong Yang 1310ff0de066SYicong Yang hisi_ptt->pdev = pdev; 1311ff0de066SYicong Yang pci_set_drvdata(pdev, hisi_ptt); 1312ff0de066SYicong Yang 1313ff0de066SYicong Yang ret = pcim_enable_device(pdev); 1314ff0de066SYicong Yang if (ret) { 1315ff0de066SYicong Yang pci_err(pdev, "failed to enable device, ret = %d\n", ret); 1316ff0de066SYicong Yang return ret; 1317ff0de066SYicong Yang } 1318ff0de066SYicong Yang 1319ff0de066SYicong Yang ret = pcim_iomap_regions(pdev, BIT(2), DRV_NAME); 1320ff0de066SYicong Yang if (ret) { 1321ff0de066SYicong Yang pci_err(pdev, "failed to remap io memory, ret = %d\n", ret); 1322ff0de066SYicong Yang return ret; 1323ff0de066SYicong Yang } 1324ff0de066SYicong Yang 1325ff0de066SYicong Yang hisi_ptt->iobase = pcim_iomap_table(pdev)[2]; 1326ff0de066SYicong Yang 1327ff0de066SYicong Yang ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); 1328ff0de066SYicong Yang if (ret) { 1329ff0de066SYicong Yang pci_err(pdev, "failed to set 64 bit dma mask, ret = %d\n", ret); 1330ff0de066SYicong Yang return ret; 1331ff0de066SYicong Yang } 1332ff0de066SYicong Yang 1333ff0de066SYicong Yang pci_set_master(pdev); 1334ff0de066SYicong Yang 1335ff0de066SYicong Yang ret = hisi_ptt_register_irq(hisi_ptt); 1336ff0de066SYicong Yang if (ret) 1337ff0de066SYicong Yang return ret; 1338ff0de066SYicong Yang 1339ff0de066SYicong Yang ret = hisi_ptt_init_ctrls(hisi_ptt); 1340ff0de066SYicong Yang if (ret) { 1341ff0de066SYicong Yang pci_err(pdev, "failed to init controls, ret = %d\n", ret); 1342ff0de066SYicong Yang return ret; 1343ff0de066SYicong Yang } 1344ff0de066SYicong Yang 1345556ef093SYicong Yang ret = hisi_ptt_register_filter_update_notifier(hisi_ptt); 1346556ef093SYicong Yang if (ret) 1347556ef093SYicong Yang pci_warn(pdev, "failed to register filter update notifier, ret = %d", ret); 1348556ef093SYicong Yang 1349ff0de066SYicong Yang ret = hisi_ptt_register_pmu(hisi_ptt); 1350ff0de066SYicong Yang if (ret) { 1351ff0de066SYicong Yang pci_err(pdev, "failed to register PMU device, ret = %d", ret); 1352ff0de066SYicong Yang return ret; 1353ff0de066SYicong Yang } 1354ff0de066SYicong Yang 13556373c463SYicong Yang ret = hisi_ptt_init_filter_attributes(hisi_ptt); 13566373c463SYicong Yang if (ret) { 13576373c463SYicong Yang pci_err(pdev, "failed to init sysfs filter attributes, ret = %d", ret); 13586373c463SYicong Yang return ret; 13596373c463SYicong Yang } 13606373c463SYicong Yang 1361ff0de066SYicong Yang return 0; 1362ff0de066SYicong Yang } 1363ff0de066SYicong Yang 1364ff0de066SYicong Yang static const struct pci_device_id hisi_ptt_id_tbl[] = { 1365ff0de066SYicong Yang { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa12e) }, 1366ff0de066SYicong Yang { } 1367ff0de066SYicong Yang }; 1368ff0de066SYicong Yang MODULE_DEVICE_TABLE(pci, hisi_ptt_id_tbl); 1369ff0de066SYicong Yang 1370ff0de066SYicong Yang static struct pci_driver hisi_ptt_driver = { 1371ff0de066SYicong Yang .name = DRV_NAME, 1372ff0de066SYicong Yang .id_table = hisi_ptt_id_tbl, 1373ff0de066SYicong Yang .probe = hisi_ptt_probe, 1374ff0de066SYicong Yang }; 1375ff0de066SYicong Yang 1376ff0de066SYicong Yang static int hisi_ptt_cpu_teardown(unsigned int cpu, struct hlist_node *node) 1377ff0de066SYicong Yang { 1378ff0de066SYicong Yang struct hisi_ptt *hisi_ptt; 1379ff0de066SYicong Yang struct device *dev; 1380ff0de066SYicong Yang int target, src; 1381ff0de066SYicong Yang 1382ff0de066SYicong Yang hisi_ptt = hlist_entry_safe(node, struct hisi_ptt, hotplug_node); 1383ff0de066SYicong Yang src = hisi_ptt->trace_ctrl.on_cpu; 1384ff0de066SYicong Yang dev = hisi_ptt->hisi_ptt_pmu.dev; 1385ff0de066SYicong Yang 1386ff0de066SYicong Yang if (!hisi_ptt->trace_ctrl.started || src != cpu) 1387ff0de066SYicong Yang return 0; 1388ff0de066SYicong Yang 1389ff0de066SYicong Yang target = cpumask_any_but(cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)), cpu); 1390ff0de066SYicong Yang if (target >= nr_cpu_ids) { 1391ff0de066SYicong Yang dev_err(dev, "no available cpu for perf context migration\n"); 1392ff0de066SYicong Yang return 0; 1393ff0de066SYicong Yang } 1394ff0de066SYicong Yang 1395ff0de066SYicong Yang perf_pmu_migrate_context(&hisi_ptt->hisi_ptt_pmu, src, target); 1396ff0de066SYicong Yang 1397ff0de066SYicong Yang /* 1398ff0de066SYicong Yang * Also make sure the interrupt bind to the migrated CPU as well. Warn 1399ff0de066SYicong Yang * the user on failure here. 1400ff0de066SYicong Yang */ 14016c50384eSYicong Yang if (irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(target))) 1402ff0de066SYicong Yang dev_warn(dev, "failed to set the affinity of trace interrupt\n"); 1403ff0de066SYicong Yang 1404ff0de066SYicong Yang hisi_ptt->trace_ctrl.on_cpu = target; 1405ff0de066SYicong Yang return 0; 1406ff0de066SYicong Yang } 1407ff0de066SYicong Yang 1408ff0de066SYicong Yang static int __init hisi_ptt_init(void) 1409ff0de066SYicong Yang { 1410ff0de066SYicong Yang int ret; 1411ff0de066SYicong Yang 1412ff0de066SYicong Yang ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DRV_NAME, NULL, 1413ff0de066SYicong Yang hisi_ptt_cpu_teardown); 1414ff0de066SYicong Yang if (ret < 0) 1415ff0de066SYicong Yang return ret; 1416ff0de066SYicong Yang hisi_ptt_pmu_online = ret; 1417ff0de066SYicong Yang 1418ff0de066SYicong Yang ret = pci_register_driver(&hisi_ptt_driver); 1419ff0de066SYicong Yang if (ret) 1420ff0de066SYicong Yang cpuhp_remove_multi_state(hisi_ptt_pmu_online); 1421ff0de066SYicong Yang 1422ff0de066SYicong Yang return ret; 1423ff0de066SYicong Yang } 1424ff0de066SYicong Yang module_init(hisi_ptt_init); 1425ff0de066SYicong Yang 1426ff0de066SYicong Yang static void __exit hisi_ptt_exit(void) 1427ff0de066SYicong Yang { 1428ff0de066SYicong Yang pci_unregister_driver(&hisi_ptt_driver); 1429ff0de066SYicong Yang cpuhp_remove_multi_state(hisi_ptt_pmu_online); 1430ff0de066SYicong Yang } 1431ff0de066SYicong Yang module_exit(hisi_ptt_exit); 1432ff0de066SYicong Yang 1433ff0de066SYicong Yang MODULE_LICENSE("GPL"); 1434ff0de066SYicong Yang MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>"); 1435ff0de066SYicong Yang MODULE_DESCRIPTION("Driver for HiSilicon PCIe tune and trace device"); 1436