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
hisi_ptt_wait_tuning_finish(struct hisi_ptt * hisi_ptt)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
hisi_ptt_tune_attr_show(struct device * dev,struct device_attribute * attr,char * buf)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
hisi_ptt_tune_attr_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
hisi_ptt_get_filter_val(u16 devid,bool is_port)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
hisi_ptt_wait_trace_hw_idle(struct hisi_ptt * hisi_ptt)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
hisi_ptt_wait_dma_reset_done(struct hisi_ptt * hisi_ptt)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
hisi_ptt_trace_end(struct hisi_ptt * hisi_ptt)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
hisi_ptt_trace_start(struct hisi_ptt * hisi_ptt)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
hisi_ptt_update_aux(struct hisi_ptt * hisi_ptt,int index,bool stop)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
hisi_ptt_isr(int irq,void * context)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
hisi_ptt_irq_free_vectors(void * pdev)324ff0de066SYicong Yang static void hisi_ptt_irq_free_vectors(void *pdev)
325ff0de066SYicong Yang {
326ff0de066SYicong Yang pci_free_irq_vectors(pdev);
327ff0de066SYicong Yang }
328ff0de066SYicong Yang
hisi_ptt_register_irq(struct hisi_ptt * hisi_ptt)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);
3455e67b92bSYicong Yang ret = devm_request_irq(&pdev->dev, hisi_ptt->trace_irq, hisi_ptt_isr,
3465e67b92bSYicong Yang IRQF_NOBALANCING | IRQF_NO_THREAD, DRV_NAME,
3475e67b92bSYicong Yang 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
hisi_ptt_del_free_filter(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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 *
hisi_ptt_alloc_add_filter(struct hisi_ptt * hisi_ptt,u16 devid,bool is_port)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
hisi_ptt_filter_show(struct device * dev,struct device_attribute * attr,char * buf)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
hisi_ptt_create_rp_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_remove_rp_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_create_req_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_remove_req_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_create_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_remove_filter_attr(struct hisi_ptt * hisi_ptt,struct hisi_ptt_filter_desc * filter)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
hisi_ptt_remove_all_filter_attributes(void * data)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
hisi_ptt_init_filter_attributes(struct hisi_ptt * hisi_ptt)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
hisi_ptt_update_filters(struct work_struct * work)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 */
hisi_ptt_notifier_call(struct notifier_block * nb,unsigned long action,void * data)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
hisi_ptt_init_filters(struct pci_dev * pdev,void * data)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
hisi_ptt_release_filters(void * data)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
hisi_ptt_config_trace_buf(struct hisi_ptt * hisi_ptt)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
hisi_ptt_init_ctrls(struct hisi_ptt * hisi_ptt)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
cpumask_show(struct device * dev,struct device_attribute * attr,char * buf)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
hisi_ptt_filter_multiselect_show(struct device * dev,struct device_attribute * attr,char * buf)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
hisi_ptt_trace_valid_direction(u32 val)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
hisi_ptt_trace_valid_type(u32 val)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
hisi_ptt_trace_valid_format(u32 val)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
hisi_ptt_trace_valid_filter(struct hisi_ptt * hisi_ptt,u64 config)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
hisi_ptt_pmu_init_configs(struct hisi_ptt * hisi_ptt,struct perf_event * event)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
hisi_ptt_pmu_event_init(struct perf_event * event)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
998*ca5e2204SYang Jihong if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type)
999*ca5e2204SYang Jihong return -ENOENT;
1000*ca5e2204SYang Jihong
1001ff0de066SYicong Yang if (event->cpu < 0) {
1002ff0de066SYicong Yang dev_dbg(event->pmu->dev, "Per-task mode not supported\n");
1003ff0de066SYicong Yang return -EOPNOTSUPP;
1004ff0de066SYicong Yang }
1005ff0de066SYicong Yang
1006bade0c1eSYicong Yang if (event->attach_state & PERF_ATTACH_TASK)
1007bade0c1eSYicong Yang return -EOPNOTSUPP;
1008bade0c1eSYicong Yang
1009ff0de066SYicong Yang ret = hisi_ptt_trace_valid_filter(hisi_ptt, event->attr.config);
1010ff0de066SYicong Yang if (ret < 0)
1011ff0de066SYicong Yang return ret;
1012ff0de066SYicong Yang
1013ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_DIRECTION_MASK, event->attr.config);
1014ff0de066SYicong Yang ret = hisi_ptt_trace_valid_direction(val);
1015ff0de066SYicong Yang if (ret < 0)
1016ff0de066SYicong Yang return ret;
1017ff0de066SYicong Yang
1018ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_TYPE_MASK, event->attr.config);
1019ff0de066SYicong Yang ret = hisi_ptt_trace_valid_type(val);
1020ff0de066SYicong Yang if (ret < 0)
1021ff0de066SYicong Yang return ret;
1022ff0de066SYicong Yang
1023ff0de066SYicong Yang val = FIELD_GET(HISI_PTT_PMU_FORMAT_MASK, event->attr.config);
1024ff0de066SYicong Yang return hisi_ptt_trace_valid_format(val);
1025ff0de066SYicong Yang }
1026ff0de066SYicong Yang
hisi_ptt_pmu_setup_aux(struct perf_event * event,void ** pages,int nr_pages,bool overwrite)1027ff0de066SYicong Yang static void *hisi_ptt_pmu_setup_aux(struct perf_event *event, void **pages,
1028ff0de066SYicong Yang int nr_pages, bool overwrite)
1029ff0de066SYicong Yang {
1030ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf;
1031ff0de066SYicong Yang struct page **pagelist;
1032ff0de066SYicong Yang int i;
1033ff0de066SYicong Yang
1034ff0de066SYicong Yang if (overwrite) {
1035ff0de066SYicong Yang dev_warn(event->pmu->dev, "Overwrite mode is not supported\n");
1036ff0de066SYicong Yang return NULL;
1037ff0de066SYicong Yang }
1038ff0de066SYicong Yang
1039ff0de066SYicong Yang /* If the pages size less than buffers, we cannot start trace */
1040ff0de066SYicong Yang if (nr_pages < HISI_PTT_TRACE_TOTAL_BUF_SIZE / PAGE_SIZE)
1041ff0de066SYicong Yang return NULL;
1042ff0de066SYicong Yang
1043ff0de066SYicong Yang buf = kzalloc(sizeof(*buf), GFP_KERNEL);
1044ff0de066SYicong Yang if (!buf)
1045ff0de066SYicong Yang return NULL;
1046ff0de066SYicong Yang
1047ff0de066SYicong Yang pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL);
1048ff0de066SYicong Yang if (!pagelist)
1049ff0de066SYicong Yang goto err;
1050ff0de066SYicong Yang
1051ff0de066SYicong Yang for (i = 0; i < nr_pages; i++)
1052ff0de066SYicong Yang pagelist[i] = virt_to_page(pages[i]);
1053ff0de066SYicong Yang
1054ff0de066SYicong Yang buf->base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL);
1055ff0de066SYicong Yang if (!buf->base) {
1056ff0de066SYicong Yang kfree(pagelist);
1057ff0de066SYicong Yang goto err;
1058ff0de066SYicong Yang }
1059ff0de066SYicong Yang
1060ff0de066SYicong Yang buf->nr_pages = nr_pages;
1061ff0de066SYicong Yang buf->length = nr_pages * PAGE_SIZE;
1062ff0de066SYicong Yang buf->pos = 0;
1063ff0de066SYicong Yang
1064ff0de066SYicong Yang kfree(pagelist);
1065ff0de066SYicong Yang return buf;
1066ff0de066SYicong Yang err:
1067ff0de066SYicong Yang kfree(buf);
1068ff0de066SYicong Yang return NULL;
1069ff0de066SYicong Yang }
1070ff0de066SYicong Yang
hisi_ptt_pmu_free_aux(void * aux)1071ff0de066SYicong Yang static void hisi_ptt_pmu_free_aux(void *aux)
1072ff0de066SYicong Yang {
1073ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf = aux;
1074ff0de066SYicong Yang
1075ff0de066SYicong Yang vunmap(buf->base);
1076ff0de066SYicong Yang kfree(buf);
1077ff0de066SYicong Yang }
1078ff0de066SYicong Yang
hisi_ptt_pmu_start(struct perf_event * event,int flags)1079ff0de066SYicong Yang static void hisi_ptt_pmu_start(struct perf_event *event, int flags)
1080ff0de066SYicong Yang {
1081ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu);
1082ff0de066SYicong Yang struct perf_output_handle *handle = &hisi_ptt->trace_ctrl.handle;
1083ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw;
1084ff0de066SYicong Yang struct device *dev = event->pmu->dev;
1085ff0de066SYicong Yang struct hisi_ptt_pmu_buf *buf;
1086ff0de066SYicong Yang int cpu = event->cpu;
1087ff0de066SYicong Yang int ret;
1088ff0de066SYicong Yang
1089ff0de066SYicong Yang hwc->state = 0;
1090ff0de066SYicong Yang
1091ff0de066SYicong Yang /* Serialize the perf process if user specified several CPUs */
1092ff0de066SYicong Yang spin_lock(&hisi_ptt->pmu_lock);
1093ff0de066SYicong Yang if (hisi_ptt->trace_ctrl.started) {
1094ff0de066SYicong Yang dev_dbg(dev, "trace has already started\n");
1095ff0de066SYicong Yang goto stop;
1096ff0de066SYicong Yang }
1097ff0de066SYicong Yang
1098ff0de066SYicong Yang /*
1099ff0de066SYicong Yang * Handle the interrupt on the same cpu which starts the trace to avoid
1100ff0de066SYicong Yang * context mismatch. Otherwise we'll trigger the WARN from the perf
1101ff0de066SYicong Yang * core in event_function_local(). If CPU passed is offline we'll fail
1102ff0de066SYicong Yang * here, just log it since we can do nothing here.
1103ff0de066SYicong Yang */
11046c50384eSYicong Yang ret = irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(cpu));
1105ff0de066SYicong Yang if (ret)
1106ff0de066SYicong Yang dev_warn(dev, "failed to set the affinity of trace interrupt\n");
1107ff0de066SYicong Yang
1108ff0de066SYicong Yang hisi_ptt->trace_ctrl.on_cpu = cpu;
1109ff0de066SYicong Yang
1110ff0de066SYicong Yang buf = perf_aux_output_begin(handle, event);
1111ff0de066SYicong Yang if (!buf) {
1112ff0de066SYicong Yang dev_dbg(dev, "aux output begin failed\n");
1113ff0de066SYicong Yang goto stop;
1114ff0de066SYicong Yang }
1115ff0de066SYicong Yang
1116ff0de066SYicong Yang buf->pos = handle->head % buf->length;
1117ff0de066SYicong Yang
1118ff0de066SYicong Yang hisi_ptt_pmu_init_configs(hisi_ptt, event);
1119ff0de066SYicong Yang
1120ff0de066SYicong Yang ret = hisi_ptt_trace_start(hisi_ptt);
1121ff0de066SYicong Yang if (ret) {
1122ff0de066SYicong Yang dev_dbg(dev, "trace start failed, ret = %d\n", ret);
1123ff0de066SYicong Yang perf_aux_output_end(handle, 0);
1124ff0de066SYicong Yang goto stop;
1125ff0de066SYicong Yang }
1126ff0de066SYicong Yang
1127ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock);
1128ff0de066SYicong Yang return;
1129ff0de066SYicong Yang stop:
1130ff0de066SYicong Yang event->hw.state |= PERF_HES_STOPPED;
1131ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock);
1132ff0de066SYicong Yang }
1133ff0de066SYicong Yang
hisi_ptt_pmu_stop(struct perf_event * event,int flags)1134ff0de066SYicong Yang static void hisi_ptt_pmu_stop(struct perf_event *event, int flags)
1135ff0de066SYicong Yang {
1136ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu);
1137ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw;
1138ff0de066SYicong Yang
1139ff0de066SYicong Yang if (hwc->state & PERF_HES_STOPPED)
1140ff0de066SYicong Yang return;
1141ff0de066SYicong Yang
1142ff0de066SYicong Yang spin_lock(&hisi_ptt->pmu_lock);
1143ff0de066SYicong Yang if (hisi_ptt->trace_ctrl.started) {
1144ff0de066SYicong Yang hisi_ptt_trace_end(hisi_ptt);
1145ff0de066SYicong Yang
1146ff0de066SYicong Yang if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt))
1147ff0de066SYicong Yang dev_warn(event->pmu->dev, "Device is still busy\n");
1148ff0de066SYicong Yang
1149ff0de066SYicong Yang hisi_ptt_update_aux(hisi_ptt, hisi_ptt->trace_ctrl.buf_index, true);
1150ff0de066SYicong Yang }
1151ff0de066SYicong Yang spin_unlock(&hisi_ptt->pmu_lock);
1152ff0de066SYicong Yang
1153ff0de066SYicong Yang hwc->state |= PERF_HES_STOPPED;
1154ff0de066SYicong Yang perf_event_update_userpage(event);
1155ff0de066SYicong Yang hwc->state |= PERF_HES_UPTODATE;
1156ff0de066SYicong Yang }
1157ff0de066SYicong Yang
hisi_ptt_pmu_add(struct perf_event * event,int flags)1158ff0de066SYicong Yang static int hisi_ptt_pmu_add(struct perf_event *event, int flags)
1159ff0de066SYicong Yang {
1160ff0de066SYicong Yang struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu);
1161ff0de066SYicong Yang struct hw_perf_event *hwc = &event->hw;
1162ff0de066SYicong Yang int cpu = event->cpu;
1163ff0de066SYicong Yang
1164ff0de066SYicong Yang /* Only allow the cpus on the device's node to add the event */
1165ff0de066SYicong Yang if (!cpumask_test_cpu(cpu, cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev))))
1166ff0de066SYicong Yang return 0;
1167ff0de066SYicong Yang
1168ff0de066SYicong Yang hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
1169ff0de066SYicong Yang
1170ff0de066SYicong Yang if (flags & PERF_EF_START) {
1171ff0de066SYicong Yang hisi_ptt_pmu_start(event, PERF_EF_RELOAD);
1172ff0de066SYicong Yang if (hwc->state & PERF_HES_STOPPED)
1173ff0de066SYicong Yang return -EINVAL;
1174ff0de066SYicong Yang }
1175ff0de066SYicong Yang
1176ff0de066SYicong Yang return 0;
1177ff0de066SYicong Yang }
1178ff0de066SYicong Yang
hisi_ptt_pmu_del(struct perf_event * event,int flags)1179ff0de066SYicong Yang static void hisi_ptt_pmu_del(struct perf_event *event, int flags)
1180ff0de066SYicong Yang {
1181ff0de066SYicong Yang hisi_ptt_pmu_stop(event, PERF_EF_UPDATE);
1182ff0de066SYicong Yang }
1183ff0de066SYicong Yang
hisi_ptt_pmu_read(struct perf_event * event)1184359d3fbcSJunhao He static void hisi_ptt_pmu_read(struct perf_event *event)
1185359d3fbcSJunhao He {
1186359d3fbcSJunhao He }
1187359d3fbcSJunhao He
hisi_ptt_remove_cpuhp_instance(void * hotplug_node)1188ff0de066SYicong Yang static void hisi_ptt_remove_cpuhp_instance(void *hotplug_node)
1189ff0de066SYicong Yang {
1190ff0de066SYicong Yang cpuhp_state_remove_instance_nocalls(hisi_ptt_pmu_online, hotplug_node);
1191ff0de066SYicong Yang }
1192ff0de066SYicong Yang
hisi_ptt_unregister_pmu(void * pmu)1193ff0de066SYicong Yang static void hisi_ptt_unregister_pmu(void *pmu)
1194ff0de066SYicong Yang {
1195ff0de066SYicong Yang perf_pmu_unregister(pmu);
1196ff0de066SYicong Yang }
1197ff0de066SYicong Yang
hisi_ptt_register_pmu(struct hisi_ptt * hisi_ptt)1198ff0de066SYicong Yang static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
1199ff0de066SYicong Yang {
1200ff0de066SYicong Yang u16 core_id, sicl_id;
1201ff0de066SYicong Yang char *pmu_name;
1202ff0de066SYicong Yang u32 reg;
1203ff0de066SYicong Yang int ret;
1204ff0de066SYicong Yang
1205ff0de066SYicong Yang ret = cpuhp_state_add_instance_nocalls(hisi_ptt_pmu_online,
1206ff0de066SYicong Yang &hisi_ptt->hotplug_node);
1207ff0de066SYicong Yang if (ret)
1208ff0de066SYicong Yang return ret;
1209ff0de066SYicong Yang
1210ff0de066SYicong Yang ret = devm_add_action_or_reset(&hisi_ptt->pdev->dev,
1211ff0de066SYicong Yang hisi_ptt_remove_cpuhp_instance,
1212ff0de066SYicong Yang &hisi_ptt->hotplug_node);
1213ff0de066SYicong Yang if (ret)
1214ff0de066SYicong Yang return ret;
1215ff0de066SYicong Yang
12165ca57b03SYicong Yang mutex_init(&hisi_ptt->tune_lock);
1217ff0de066SYicong Yang spin_lock_init(&hisi_ptt->pmu_lock);
1218ff0de066SYicong Yang
1219ff0de066SYicong Yang hisi_ptt->hisi_ptt_pmu = (struct pmu) {
1220ff0de066SYicong Yang .module = THIS_MODULE,
122145c90292SYicong Yang .capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_NO_EXCLUDE,
1222ff0de066SYicong Yang .task_ctx_nr = perf_sw_context,
1223ff0de066SYicong Yang .attr_groups = hisi_ptt_pmu_groups,
1224ff0de066SYicong Yang .event_init = hisi_ptt_pmu_event_init,
1225ff0de066SYicong Yang .setup_aux = hisi_ptt_pmu_setup_aux,
1226ff0de066SYicong Yang .free_aux = hisi_ptt_pmu_free_aux,
1227ff0de066SYicong Yang .start = hisi_ptt_pmu_start,
1228ff0de066SYicong Yang .stop = hisi_ptt_pmu_stop,
1229ff0de066SYicong Yang .add = hisi_ptt_pmu_add,
1230ff0de066SYicong Yang .del = hisi_ptt_pmu_del,
1231359d3fbcSJunhao He .read = hisi_ptt_pmu_read,
1232ff0de066SYicong Yang };
1233ff0de066SYicong Yang
1234ff0de066SYicong Yang reg = readl(hisi_ptt->iobase + HISI_PTT_LOCATION);
1235ff0de066SYicong Yang core_id = FIELD_GET(HISI_PTT_CORE_ID, reg);
1236ff0de066SYicong Yang sicl_id = FIELD_GET(HISI_PTT_SICL_ID, reg);
1237ff0de066SYicong Yang
1238ff0de066SYicong Yang pmu_name = devm_kasprintf(&hisi_ptt->pdev->dev, GFP_KERNEL, "hisi_ptt%u_%u",
1239ff0de066SYicong Yang sicl_id, core_id);
1240ff0de066SYicong Yang if (!pmu_name)
1241ff0de066SYicong Yang return -ENOMEM;
1242ff0de066SYicong Yang
1243ff0de066SYicong Yang ret = perf_pmu_register(&hisi_ptt->hisi_ptt_pmu, pmu_name, -1);
1244ff0de066SYicong Yang if (ret)
1245ff0de066SYicong Yang return ret;
1246ff0de066SYicong Yang
1247ff0de066SYicong Yang return devm_add_action_or_reset(&hisi_ptt->pdev->dev,
1248ff0de066SYicong Yang hisi_ptt_unregister_pmu,
1249ff0de066SYicong Yang &hisi_ptt->hisi_ptt_pmu);
1250ff0de066SYicong Yang }
1251ff0de066SYicong Yang
hisi_ptt_unregister_filter_update_notifier(void * data)1252556ef093SYicong Yang static void hisi_ptt_unregister_filter_update_notifier(void *data)
1253556ef093SYicong Yang {
1254556ef093SYicong Yang struct hisi_ptt *hisi_ptt = data;
1255556ef093SYicong Yang
1256556ef093SYicong Yang bus_unregister_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
1257556ef093SYicong Yang
1258556ef093SYicong Yang /* Cancel any work that has been queued */
1259556ef093SYicong Yang cancel_delayed_work_sync(&hisi_ptt->work);
1260556ef093SYicong Yang }
1261556ef093SYicong Yang
1262556ef093SYicong Yang /* Register the bus notifier for dynamically updating the filter list */
hisi_ptt_register_filter_update_notifier(struct hisi_ptt * hisi_ptt)1263556ef093SYicong Yang static int hisi_ptt_register_filter_update_notifier(struct hisi_ptt *hisi_ptt)
1264556ef093SYicong Yang {
1265556ef093SYicong Yang int ret;
1266556ef093SYicong Yang
1267556ef093SYicong Yang hisi_ptt->hisi_ptt_nb.notifier_call = hisi_ptt_notifier_call;
1268556ef093SYicong Yang ret = bus_register_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
1269556ef093SYicong Yang if (ret)
1270556ef093SYicong Yang return ret;
1271556ef093SYicong Yang
1272556ef093SYicong Yang return devm_add_action_or_reset(&hisi_ptt->pdev->dev,
1273556ef093SYicong Yang hisi_ptt_unregister_filter_update_notifier,
1274556ef093SYicong Yang hisi_ptt);
1275556ef093SYicong Yang }
1276556ef093SYicong Yang
1277ff0de066SYicong Yang /*
1278ff0de066SYicong Yang * The DMA of PTT trace can only use direct mappings due to some
1279ff0de066SYicong Yang * hardware restriction. Check whether there is no IOMMU or the
1280ff0de066SYicong Yang * policy of the IOMMU domain is passthrough, otherwise the trace
1281ff0de066SYicong Yang * cannot work.
1282ff0de066SYicong Yang *
1283ff0de066SYicong Yang * The PTT device is supposed to behind an ARM SMMUv3, which
1284ff0de066SYicong Yang * should have passthrough the device by a quirk.
1285ff0de066SYicong Yang */
hisi_ptt_check_iommu_mapping(struct pci_dev * pdev)1286ff0de066SYicong Yang static int hisi_ptt_check_iommu_mapping(struct pci_dev *pdev)
1287ff0de066SYicong Yang {
1288ff0de066SYicong Yang struct iommu_domain *iommu_domain;
1289ff0de066SYicong Yang
1290ff0de066SYicong Yang iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
1291ff0de066SYicong Yang if (!iommu_domain || iommu_domain->type == IOMMU_DOMAIN_IDENTITY)
1292ff0de066SYicong Yang return 0;
1293ff0de066SYicong Yang
1294ff0de066SYicong Yang return -EOPNOTSUPP;
1295ff0de066SYicong Yang }
1296ff0de066SYicong Yang
hisi_ptt_probe(struct pci_dev * pdev,const struct pci_device_id * id)1297ff0de066SYicong Yang static int hisi_ptt_probe(struct pci_dev *pdev,
1298ff0de066SYicong Yang const struct pci_device_id *id)
1299ff0de066SYicong Yang {
1300ff0de066SYicong Yang struct hisi_ptt *hisi_ptt;
1301ff0de066SYicong Yang int ret;
1302ff0de066SYicong Yang
1303ff0de066SYicong Yang ret = hisi_ptt_check_iommu_mapping(pdev);
1304ff0de066SYicong Yang if (ret) {
1305ff0de066SYicong Yang pci_err(pdev, "requires direct DMA mappings\n");
1306ff0de066SYicong Yang return ret;
1307ff0de066SYicong Yang }
1308ff0de066SYicong Yang
1309ff0de066SYicong Yang hisi_ptt = devm_kzalloc(&pdev->dev, sizeof(*hisi_ptt), GFP_KERNEL);
1310ff0de066SYicong Yang if (!hisi_ptt)
1311ff0de066SYicong Yang return -ENOMEM;
1312ff0de066SYicong Yang
1313ff0de066SYicong Yang hisi_ptt->pdev = pdev;
1314ff0de066SYicong Yang pci_set_drvdata(pdev, hisi_ptt);
1315ff0de066SYicong Yang
1316ff0de066SYicong Yang ret = pcim_enable_device(pdev);
1317ff0de066SYicong Yang if (ret) {
1318ff0de066SYicong Yang pci_err(pdev, "failed to enable device, ret = %d\n", ret);
1319ff0de066SYicong Yang return ret;
1320ff0de066SYicong Yang }
1321ff0de066SYicong Yang
1322ff0de066SYicong Yang ret = pcim_iomap_regions(pdev, BIT(2), DRV_NAME);
1323ff0de066SYicong Yang if (ret) {
1324ff0de066SYicong Yang pci_err(pdev, "failed to remap io memory, ret = %d\n", ret);
1325ff0de066SYicong Yang return ret;
1326ff0de066SYicong Yang }
1327ff0de066SYicong Yang
1328ff0de066SYicong Yang hisi_ptt->iobase = pcim_iomap_table(pdev)[2];
1329ff0de066SYicong Yang
1330ff0de066SYicong Yang ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
1331ff0de066SYicong Yang if (ret) {
1332ff0de066SYicong Yang pci_err(pdev, "failed to set 64 bit dma mask, ret = %d\n", ret);
1333ff0de066SYicong Yang return ret;
1334ff0de066SYicong Yang }
1335ff0de066SYicong Yang
1336ff0de066SYicong Yang pci_set_master(pdev);
1337ff0de066SYicong Yang
1338ff0de066SYicong Yang ret = hisi_ptt_register_irq(hisi_ptt);
1339ff0de066SYicong Yang if (ret)
1340ff0de066SYicong Yang return ret;
1341ff0de066SYicong Yang
1342ff0de066SYicong Yang ret = hisi_ptt_init_ctrls(hisi_ptt);
1343ff0de066SYicong Yang if (ret) {
1344ff0de066SYicong Yang pci_err(pdev, "failed to init controls, ret = %d\n", ret);
1345ff0de066SYicong Yang return ret;
1346ff0de066SYicong Yang }
1347ff0de066SYicong Yang
1348556ef093SYicong Yang ret = hisi_ptt_register_filter_update_notifier(hisi_ptt);
1349556ef093SYicong Yang if (ret)
1350556ef093SYicong Yang pci_warn(pdev, "failed to register filter update notifier, ret = %d", ret);
1351556ef093SYicong Yang
1352ff0de066SYicong Yang ret = hisi_ptt_register_pmu(hisi_ptt);
1353ff0de066SYicong Yang if (ret) {
1354ff0de066SYicong Yang pci_err(pdev, "failed to register PMU device, ret = %d", ret);
1355ff0de066SYicong Yang return ret;
1356ff0de066SYicong Yang }
1357ff0de066SYicong Yang
13586373c463SYicong Yang ret = hisi_ptt_init_filter_attributes(hisi_ptt);
13596373c463SYicong Yang if (ret) {
13606373c463SYicong Yang pci_err(pdev, "failed to init sysfs filter attributes, ret = %d", ret);
13616373c463SYicong Yang return ret;
13626373c463SYicong Yang }
13636373c463SYicong Yang
1364ff0de066SYicong Yang return 0;
1365ff0de066SYicong Yang }
1366ff0de066SYicong Yang
1367ff0de066SYicong Yang static const struct pci_device_id hisi_ptt_id_tbl[] = {
1368ff0de066SYicong Yang { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa12e) },
1369ff0de066SYicong Yang { }
1370ff0de066SYicong Yang };
1371ff0de066SYicong Yang MODULE_DEVICE_TABLE(pci, hisi_ptt_id_tbl);
1372ff0de066SYicong Yang
1373ff0de066SYicong Yang static struct pci_driver hisi_ptt_driver = {
1374ff0de066SYicong Yang .name = DRV_NAME,
1375ff0de066SYicong Yang .id_table = hisi_ptt_id_tbl,
1376ff0de066SYicong Yang .probe = hisi_ptt_probe,
1377ff0de066SYicong Yang };
1378ff0de066SYicong Yang
hisi_ptt_cpu_teardown(unsigned int cpu,struct hlist_node * node)1379ff0de066SYicong Yang static int hisi_ptt_cpu_teardown(unsigned int cpu, struct hlist_node *node)
1380ff0de066SYicong Yang {
1381ff0de066SYicong Yang struct hisi_ptt *hisi_ptt;
1382ff0de066SYicong Yang struct device *dev;
1383ff0de066SYicong Yang int target, src;
1384ff0de066SYicong Yang
1385ff0de066SYicong Yang hisi_ptt = hlist_entry_safe(node, struct hisi_ptt, hotplug_node);
1386ff0de066SYicong Yang src = hisi_ptt->trace_ctrl.on_cpu;
1387ff0de066SYicong Yang dev = hisi_ptt->hisi_ptt_pmu.dev;
1388ff0de066SYicong Yang
1389ff0de066SYicong Yang if (!hisi_ptt->trace_ctrl.started || src != cpu)
1390ff0de066SYicong Yang return 0;
1391ff0de066SYicong Yang
1392ff0de066SYicong Yang target = cpumask_any_but(cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)), cpu);
1393ff0de066SYicong Yang if (target >= nr_cpu_ids) {
1394ff0de066SYicong Yang dev_err(dev, "no available cpu for perf context migration\n");
1395ff0de066SYicong Yang return 0;
1396ff0de066SYicong Yang }
1397ff0de066SYicong Yang
1398ff0de066SYicong Yang perf_pmu_migrate_context(&hisi_ptt->hisi_ptt_pmu, src, target);
1399ff0de066SYicong Yang
1400ff0de066SYicong Yang /*
1401ff0de066SYicong Yang * Also make sure the interrupt bind to the migrated CPU as well. Warn
1402ff0de066SYicong Yang * the user on failure here.
1403ff0de066SYicong Yang */
14046c50384eSYicong Yang if (irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(target)))
1405ff0de066SYicong Yang dev_warn(dev, "failed to set the affinity of trace interrupt\n");
1406ff0de066SYicong Yang
1407ff0de066SYicong Yang hisi_ptt->trace_ctrl.on_cpu = target;
1408ff0de066SYicong Yang return 0;
1409ff0de066SYicong Yang }
1410ff0de066SYicong Yang
hisi_ptt_init(void)1411ff0de066SYicong Yang static int __init hisi_ptt_init(void)
1412ff0de066SYicong Yang {
1413ff0de066SYicong Yang int ret;
1414ff0de066SYicong Yang
1415ff0de066SYicong Yang ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DRV_NAME, NULL,
1416ff0de066SYicong Yang hisi_ptt_cpu_teardown);
1417ff0de066SYicong Yang if (ret < 0)
1418ff0de066SYicong Yang return ret;
1419ff0de066SYicong Yang hisi_ptt_pmu_online = ret;
1420ff0de066SYicong Yang
1421ff0de066SYicong Yang ret = pci_register_driver(&hisi_ptt_driver);
1422ff0de066SYicong Yang if (ret)
1423ff0de066SYicong Yang cpuhp_remove_multi_state(hisi_ptt_pmu_online);
1424ff0de066SYicong Yang
1425ff0de066SYicong Yang return ret;
1426ff0de066SYicong Yang }
1427ff0de066SYicong Yang module_init(hisi_ptt_init);
1428ff0de066SYicong Yang
hisi_ptt_exit(void)1429ff0de066SYicong Yang static void __exit hisi_ptt_exit(void)
1430ff0de066SYicong Yang {
1431ff0de066SYicong Yang pci_unregister_driver(&hisi_ptt_driver);
1432ff0de066SYicong Yang cpuhp_remove_multi_state(hisi_ptt_pmu_online);
1433ff0de066SYicong Yang }
1434ff0de066SYicong Yang module_exit(hisi_ptt_exit);
1435ff0de066SYicong Yang
1436ff0de066SYicong Yang MODULE_LICENSE("GPL");
1437ff0de066SYicong Yang MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
1438ff0de066SYicong Yang MODULE_DESCRIPTION("Driver for HiSilicon PCIe tune and trace device");
1439