xref: /openbmc/linux/drivers/hwtracing/ptt/hisi_ptt.c (revision ca5e2204)
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