162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This driver adds support for PCIe PMU RCiEP device. Related
462306a36Sopenharmony_ci * perf events are bandwidth, latency etc.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2021 HiSilicon Limited
762306a36Sopenharmony_ci * Author: Qi Liu <liuqi115@huawei.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/bitmap.h>
1162306a36Sopenharmony_ci#include <linux/bug.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/perf_event.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DRV_NAME "hisi_pcie_pmu"
2362306a36Sopenharmony_ci/* Define registers */
2462306a36Sopenharmony_ci#define HISI_PCIE_GLOBAL_CTRL		0x00
2562306a36Sopenharmony_ci#define HISI_PCIE_EVENT_CTRL		0x010
2662306a36Sopenharmony_ci#define HISI_PCIE_CNT			0x090
2762306a36Sopenharmony_ci#define HISI_PCIE_EXT_CNT		0x110
2862306a36Sopenharmony_ci#define HISI_PCIE_INT_STAT		0x150
2962306a36Sopenharmony_ci#define HISI_PCIE_INT_MASK		0x154
3062306a36Sopenharmony_ci#define HISI_PCIE_REG_BDF		0xfe0
3162306a36Sopenharmony_ci#define HISI_PCIE_REG_VERSION		0xfe4
3262306a36Sopenharmony_ci#define HISI_PCIE_REG_INFO		0xfe8
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Define command in HISI_PCIE_GLOBAL_CTRL */
3562306a36Sopenharmony_ci#define HISI_PCIE_GLOBAL_EN		0x01
3662306a36Sopenharmony_ci#define HISI_PCIE_GLOBAL_NONE		0
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Define command in HISI_PCIE_EVENT_CTRL */
3962306a36Sopenharmony_ci#define HISI_PCIE_EVENT_EN		BIT_ULL(20)
4062306a36Sopenharmony_ci#define HISI_PCIE_RESET_CNT		BIT_ULL(22)
4162306a36Sopenharmony_ci#define HISI_PCIE_INIT_SET		BIT_ULL(34)
4262306a36Sopenharmony_ci#define HISI_PCIE_THR_EN		BIT_ULL(26)
4362306a36Sopenharmony_ci#define HISI_PCIE_TARGET_EN		BIT_ULL(32)
4462306a36Sopenharmony_ci#define HISI_PCIE_TRIG_EN		BIT_ULL(52)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Define offsets in HISI_PCIE_EVENT_CTRL */
4762306a36Sopenharmony_ci#define HISI_PCIE_EVENT_M		GENMASK_ULL(15, 0)
4862306a36Sopenharmony_ci#define HISI_PCIE_THR_MODE_M		GENMASK_ULL(27, 27)
4962306a36Sopenharmony_ci#define HISI_PCIE_THR_M			GENMASK_ULL(31, 28)
5062306a36Sopenharmony_ci#define HISI_PCIE_LEN_M			GENMASK_ULL(35, 34)
5162306a36Sopenharmony_ci#define HISI_PCIE_TARGET_M		GENMASK_ULL(52, 36)
5262306a36Sopenharmony_ci#define HISI_PCIE_TRIG_MODE_M		GENMASK_ULL(53, 53)
5362306a36Sopenharmony_ci#define HISI_PCIE_TRIG_M		GENMASK_ULL(59, 56)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Default config of TLP length mode, will count both TLP headers and payloads */
5662306a36Sopenharmony_ci#define HISI_PCIE_LEN_M_DEFAULT		3ULL
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define HISI_PCIE_MAX_COUNTERS		8
5962306a36Sopenharmony_ci#define HISI_PCIE_REG_STEP		8
6062306a36Sopenharmony_ci#define HISI_PCIE_THR_MAX_VAL		10
6162306a36Sopenharmony_ci#define HISI_PCIE_TRIG_MAX_VAL		10
6262306a36Sopenharmony_ci#define HISI_PCIE_MAX_PERIOD		(GENMASK_ULL(63, 0))
6362306a36Sopenharmony_ci#define HISI_PCIE_INIT_VAL		BIT_ULL(63)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct hisi_pcie_pmu {
6662306a36Sopenharmony_ci	struct perf_event *hw_events[HISI_PCIE_MAX_COUNTERS];
6762306a36Sopenharmony_ci	struct hlist_node node;
6862306a36Sopenharmony_ci	struct pci_dev *pdev;
6962306a36Sopenharmony_ci	struct pmu pmu;
7062306a36Sopenharmony_ci	void __iomem *base;
7162306a36Sopenharmony_ci	int irq;
7262306a36Sopenharmony_ci	u32 identifier;
7362306a36Sopenharmony_ci	/* Minimum and maximum BDF of root ports monitored by PMU */
7462306a36Sopenharmony_ci	u16 bdf_min;
7562306a36Sopenharmony_ci	u16 bdf_max;
7662306a36Sopenharmony_ci	int on_cpu;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct hisi_pcie_reg_pair {
8062306a36Sopenharmony_ci	u16 lo;
8162306a36Sopenharmony_ci	u16 hi;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define to_pcie_pmu(p)  (container_of((p), struct hisi_pcie_pmu, pmu))
8562306a36Sopenharmony_ci#define GET_PCI_DEVFN(bdf)  ((bdf) & 0xff)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define HISI_PCIE_PMU_FILTER_ATTR(_name, _config, _hi, _lo)		  \
8862306a36Sopenharmony_ci	static u64 hisi_pcie_get_##_name(struct perf_event *event)	  \
8962306a36Sopenharmony_ci	{								  \
9062306a36Sopenharmony_ci		return FIELD_GET(GENMASK(_hi, _lo), event->attr._config); \
9162306a36Sopenharmony_ci	}								  \
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(event, config, 16, 0);
9462306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(thr_len, config1, 3, 0);
9562306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(thr_mode, config1, 4, 4);
9662306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(trig_len, config1, 8, 5);
9762306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(trig_mode, config1, 9, 9);
9862306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(len_mode, config1, 11, 10);
9962306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(port, config2, 15, 0);
10062306a36Sopenharmony_ciHISI_PCIE_PMU_FILTER_ATTR(bdf, config2, 31, 16);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic ssize_t hisi_pcie_format_sysfs_show(struct device *dev, struct device_attribute *attr,
10362306a36Sopenharmony_ci					   char *buf)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct dev_ext_attribute *eattr;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	eattr = container_of(attr, struct dev_ext_attribute, attr);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", (char *)eattr->var);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic ssize_t hisi_pcie_event_sysfs_show(struct device *dev, struct device_attribute *attr,
11362306a36Sopenharmony_ci					  char *buf)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct perf_pmu_events_attr *pmu_attr =
11662306a36Sopenharmony_ci		container_of(attr, struct perf_pmu_events_attr, attr);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return sysfs_emit(buf, "config=0x%llx\n", pmu_attr->id);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define HISI_PCIE_PMU_FORMAT_ATTR(_name, _format)                              \
12262306a36Sopenharmony_ci	(&((struct dev_ext_attribute[]){                                       \
12362306a36Sopenharmony_ci		{ .attr = __ATTR(_name, 0444, hisi_pcie_format_sysfs_show,     \
12462306a36Sopenharmony_ci				 NULL),                                        \
12562306a36Sopenharmony_ci		  .var = (void *)_format }                                     \
12662306a36Sopenharmony_ci	})[0].attr.attr)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define HISI_PCIE_PMU_EVENT_ATTR(_name, _id)			\
12962306a36Sopenharmony_ci	PMU_EVENT_ATTR_ID(_name, hisi_pcie_event_sysfs_show, _id)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu));
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic ssize_t identifier_show(struct device *dev, struct device_attribute *attr, char *buf)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev));
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return sysfs_emit(buf, "%#x\n", pcie_pmu->identifier);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(identifier);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic ssize_t bus_show(struct device *dev, struct device_attribute *attr, char *buf)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return sysfs_emit(buf, "%#04x\n", PCI_BUS_NUM(pcie_pmu->bdf_min));
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(bus);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic struct hisi_pcie_reg_pair
15662306a36Sopenharmony_cihisi_pcie_parse_reg_value(struct hisi_pcie_pmu *pcie_pmu, u32 reg_off)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u32 val = readl_relaxed(pcie_pmu->base + reg_off);
15962306a36Sopenharmony_ci	struct hisi_pcie_reg_pair regs = {
16062306a36Sopenharmony_ci		.lo = val,
16162306a36Sopenharmony_ci		.hi = val >> 16,
16262306a36Sopenharmony_ci	};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return regs;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * Hardware counter and ext_counter work together for bandwidth, latency, bus
16962306a36Sopenharmony_ci * utilization and buffer occupancy events. For example, RX memory write latency
17062306a36Sopenharmony_ci * events(index = 0x0010), counter counts total delay cycles and ext_counter
17162306a36Sopenharmony_ci * counts RX memory write PCIe packets number.
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * As we don't want PMU driver to process these two data, "delay cycles" can
17462306a36Sopenharmony_ci * be treated as an independent event(index = 0x0010), "RX memory write packets
17562306a36Sopenharmony_ci * number" as another(index = 0x10010). BIT 16 is used to distinguish and 0-15
17662306a36Sopenharmony_ci * bits are "real" event index, which can be used to set HISI_PCIE_EVENT_CTRL.
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_ci#define EXT_COUNTER_IS_USED(idx)		((idx) & BIT(16))
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic u32 hisi_pcie_get_real_event(struct perf_event *event)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return hisi_pcie_get_event(event) & GENMASK(15, 0);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic u32 hisi_pcie_pmu_get_offset(u32 offset, u32 idx)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	return offset + HISI_PCIE_REG_STEP * idx;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic u32 hisi_pcie_pmu_readl(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset,
19162306a36Sopenharmony_ci			       u32 idx)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	u32 offset = hisi_pcie_pmu_get_offset(reg_offset, idx);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return readl_relaxed(pcie_pmu->base + offset);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void hisi_pcie_pmu_writel(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset, u32 idx, u32 val)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u32 offset = hisi_pcie_pmu_get_offset(reg_offset, idx);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	writel_relaxed(val, pcie_pmu->base + offset);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic u64 hisi_pcie_pmu_readq(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset, u32 idx)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	u32 offset = hisi_pcie_pmu_get_offset(reg_offset, idx);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return readq_relaxed(pcie_pmu->base + offset);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void hisi_pcie_pmu_writeq(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset, u32 idx, u64 val)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	u32 offset = hisi_pcie_pmu_get_offset(reg_offset, idx);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	writeq_relaxed(val, pcie_pmu->base + offset);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void hisi_pcie_pmu_config_filter(struct perf_event *event)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
22262306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
22362306a36Sopenharmony_ci	u64 port, trig_len, thr_len, len_mode;
22462306a36Sopenharmony_ci	u64 reg = HISI_PCIE_INIT_SET;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Config HISI_PCIE_EVENT_CTRL according to event. */
22762306a36Sopenharmony_ci	reg |= FIELD_PREP(HISI_PCIE_EVENT_M, hisi_pcie_get_real_event(event));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Config HISI_PCIE_EVENT_CTRL according to root port or EP device. */
23062306a36Sopenharmony_ci	port = hisi_pcie_get_port(event);
23162306a36Sopenharmony_ci	if (port)
23262306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_TARGET_M, port);
23362306a36Sopenharmony_ci	else
23462306a36Sopenharmony_ci		reg |= HISI_PCIE_TARGET_EN |
23562306a36Sopenharmony_ci		       FIELD_PREP(HISI_PCIE_TARGET_M, hisi_pcie_get_bdf(event));
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Config HISI_PCIE_EVENT_CTRL according to trigger condition. */
23862306a36Sopenharmony_ci	trig_len = hisi_pcie_get_trig_len(event);
23962306a36Sopenharmony_ci	if (trig_len) {
24062306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_TRIG_M, trig_len);
24162306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_TRIG_MODE_M, hisi_pcie_get_trig_mode(event));
24262306a36Sopenharmony_ci		reg |= HISI_PCIE_TRIG_EN;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Config HISI_PCIE_EVENT_CTRL according to threshold condition. */
24662306a36Sopenharmony_ci	thr_len = hisi_pcie_get_thr_len(event);
24762306a36Sopenharmony_ci	if (thr_len) {
24862306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_THR_M, thr_len);
24962306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_THR_MODE_M, hisi_pcie_get_thr_mode(event));
25062306a36Sopenharmony_ci		reg |= HISI_PCIE_THR_EN;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	len_mode = hisi_pcie_get_len_mode(event);
25462306a36Sopenharmony_ci	if (len_mode)
25562306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_LEN_M, len_mode);
25662306a36Sopenharmony_ci	else
25762306a36Sopenharmony_ci		reg |= FIELD_PREP(HISI_PCIE_LEN_M, HISI_PCIE_LEN_M_DEFAULT);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, hwc->idx, reg);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void hisi_pcie_pmu_clear_filter(struct perf_event *event)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
26562306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, hwc->idx, HISI_PCIE_INIT_SET);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic bool hisi_pcie_pmu_valid_requester_id(struct hisi_pcie_pmu *pcie_pmu, u32 bdf)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct pci_dev *root_port, *pdev;
27362306a36Sopenharmony_ci	u16 rp_bdf;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pcie_pmu->pdev->bus), PCI_BUS_NUM(bdf),
27662306a36Sopenharmony_ci					   GET_PCI_DEVFN(bdf));
27762306a36Sopenharmony_ci	if (!pdev)
27862306a36Sopenharmony_ci		return false;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	root_port = pcie_find_root_port(pdev);
28162306a36Sopenharmony_ci	if (!root_port) {
28262306a36Sopenharmony_ci		pci_dev_put(pdev);
28362306a36Sopenharmony_ci		return false;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	pci_dev_put(pdev);
28762306a36Sopenharmony_ci	rp_bdf = pci_dev_id(root_port);
28862306a36Sopenharmony_ci	return rp_bdf >= pcie_pmu->bdf_min && rp_bdf <= pcie_pmu->bdf_max;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic bool hisi_pcie_pmu_valid_filter(struct perf_event *event,
29262306a36Sopenharmony_ci				       struct hisi_pcie_pmu *pcie_pmu)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	u32 requester_id = hisi_pcie_get_bdf(event);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (hisi_pcie_get_thr_len(event) > HISI_PCIE_THR_MAX_VAL)
29762306a36Sopenharmony_ci		return false;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (hisi_pcie_get_trig_len(event) > HISI_PCIE_TRIG_MAX_VAL)
30062306a36Sopenharmony_ci		return false;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (requester_id) {
30362306a36Sopenharmony_ci		if (!hisi_pcie_pmu_valid_requester_id(pcie_pmu, requester_id))
30462306a36Sopenharmony_ci			return false;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return true;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic bool hisi_pcie_pmu_cmp_event(struct perf_event *target,
31162306a36Sopenharmony_ci					struct perf_event *event)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	return hisi_pcie_get_real_event(target) == hisi_pcie_get_real_event(event);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic bool hisi_pcie_pmu_validate_event_group(struct perf_event *event)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct perf_event *sibling, *leader = event->group_leader;
31962306a36Sopenharmony_ci	struct perf_event *event_group[HISI_PCIE_MAX_COUNTERS];
32062306a36Sopenharmony_ci	int counters = 1;
32162306a36Sopenharmony_ci	int num;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	event_group[0] = leader;
32462306a36Sopenharmony_ci	if (!is_software_event(leader)) {
32562306a36Sopenharmony_ci		if (leader->pmu != event->pmu)
32662306a36Sopenharmony_ci			return false;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (leader != event && !hisi_pcie_pmu_cmp_event(leader, event))
32962306a36Sopenharmony_ci			event_group[counters++] = event;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	for_each_sibling_event(sibling, event->group_leader) {
33362306a36Sopenharmony_ci		if (is_software_event(sibling))
33462306a36Sopenharmony_ci			continue;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (sibling->pmu != event->pmu)
33762306a36Sopenharmony_ci			return false;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		for (num = 0; num < counters; num++) {
34062306a36Sopenharmony_ci			if (hisi_pcie_pmu_cmp_event(event_group[num], sibling))
34162306a36Sopenharmony_ci				break;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (num == counters)
34562306a36Sopenharmony_ci			event_group[counters++] = sibling;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return counters <= HISI_PCIE_MAX_COUNTERS;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int hisi_pcie_pmu_event_init(struct perf_event *event)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
35462306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Check the type first before going on, otherwise it's not our event */
35762306a36Sopenharmony_ci	if (event->attr.type != event->pmu->type)
35862306a36Sopenharmony_ci		return -ENOENT;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	event->cpu = pcie_pmu->on_cpu;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (EXT_COUNTER_IS_USED(hisi_pcie_get_event(event)))
36362306a36Sopenharmony_ci		hwc->event_base = HISI_PCIE_EXT_CNT;
36462306a36Sopenharmony_ci	else
36562306a36Sopenharmony_ci		hwc->event_base = HISI_PCIE_CNT;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* Sampling is not supported. */
36862306a36Sopenharmony_ci	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
36962306a36Sopenharmony_ci		return -EOPNOTSUPP;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (!hisi_pcie_pmu_valid_filter(event, pcie_pmu))
37262306a36Sopenharmony_ci		return -EINVAL;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!hisi_pcie_pmu_validate_event_group(event))
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic u64 hisi_pcie_pmu_read_counter(struct perf_event *event)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
38362306a36Sopenharmony_ci	u32 idx = event->hw.idx;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return hisi_pcie_pmu_readq(pcie_pmu, event->hw.event_base, idx);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int hisi_pcie_pmu_find_related_event(struct hisi_pcie_pmu *pcie_pmu,
38962306a36Sopenharmony_ci					    struct perf_event *event)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct perf_event *sibling;
39262306a36Sopenharmony_ci	int idx;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	for (idx = 0; idx < HISI_PCIE_MAX_COUNTERS; idx++) {
39562306a36Sopenharmony_ci		sibling = pcie_pmu->hw_events[idx];
39662306a36Sopenharmony_ci		if (!sibling)
39762306a36Sopenharmony_ci			continue;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		if (!hisi_pcie_pmu_cmp_event(sibling, event))
40062306a36Sopenharmony_ci			continue;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		/* Related events must be used in group */
40362306a36Sopenharmony_ci		if (sibling->group_leader == event->group_leader)
40462306a36Sopenharmony_ci			return idx;
40562306a36Sopenharmony_ci		else
40662306a36Sopenharmony_ci			return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return idx;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int hisi_pcie_pmu_get_event_idx(struct hisi_pcie_pmu *pcie_pmu)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	int idx;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	for (idx = 0; idx < HISI_PCIE_MAX_COUNTERS; idx++) {
41762306a36Sopenharmony_ci		if (!pcie_pmu->hw_events[idx])
41862306a36Sopenharmony_ci			return idx;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return -EINVAL;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void hisi_pcie_pmu_event_update(struct perf_event *event)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
42762306a36Sopenharmony_ci	u64 new_cnt, prev_cnt, delta;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	do {
43062306a36Sopenharmony_ci		prev_cnt = local64_read(&hwc->prev_count);
43162306a36Sopenharmony_ci		new_cnt = hisi_pcie_pmu_read_counter(event);
43262306a36Sopenharmony_ci	} while (local64_cmpxchg(&hwc->prev_count, prev_cnt,
43362306a36Sopenharmony_ci				 new_cnt) != prev_cnt);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	delta = (new_cnt - prev_cnt) & HISI_PCIE_MAX_PERIOD;
43662306a36Sopenharmony_ci	local64_add(delta, &event->count);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void hisi_pcie_pmu_read(struct perf_event *event)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	hisi_pcie_pmu_event_update(event);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void hisi_pcie_pmu_set_period(struct perf_event *event)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
44762306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
44862306a36Sopenharmony_ci	int idx = hwc->idx;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	local64_set(&hwc->prev_count, HISI_PCIE_INIT_VAL);
45162306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_CNT, idx, HISI_PCIE_INIT_VAL);
45262306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EXT_CNT, idx, HISI_PCIE_INIT_VAL);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void hisi_pcie_pmu_enable_counter(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	u32 idx = hwc->idx;
45862306a36Sopenharmony_ci	u64 val;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	val = hisi_pcie_pmu_readq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx);
46162306a36Sopenharmony_ci	val |= HISI_PCIE_EVENT_EN;
46262306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx, val);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void hisi_pcie_pmu_disable_counter(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	u32 idx = hwc->idx;
46862306a36Sopenharmony_ci	u64 val;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	val = hisi_pcie_pmu_readq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx);
47162306a36Sopenharmony_ci	val &= ~HISI_PCIE_EVENT_EN;
47262306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx, val);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void hisi_pcie_pmu_enable_int(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	u32 idx = hwc->idx;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	hisi_pcie_pmu_writel(pcie_pmu, HISI_PCIE_INT_MASK, idx, 0);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic void hisi_pcie_pmu_disable_int(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	u32 idx = hwc->idx;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	hisi_pcie_pmu_writel(pcie_pmu, HISI_PCIE_INT_MASK, idx, 1);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void hisi_pcie_pmu_reset_counter(struct hisi_pcie_pmu *pcie_pmu, int idx)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx, HISI_PCIE_RESET_CNT);
49262306a36Sopenharmony_ci	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, idx, HISI_PCIE_INIT_SET);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic void hisi_pcie_pmu_start(struct perf_event *event, int flags)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
49862306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
49962306a36Sopenharmony_ci	int idx = hwc->idx;
50062306a36Sopenharmony_ci	u64 prev_cnt;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
50662306a36Sopenharmony_ci	hwc->state = 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	hisi_pcie_pmu_config_filter(event);
50962306a36Sopenharmony_ci	hisi_pcie_pmu_enable_counter(pcie_pmu, hwc);
51062306a36Sopenharmony_ci	hisi_pcie_pmu_enable_int(pcie_pmu, hwc);
51162306a36Sopenharmony_ci	hisi_pcie_pmu_set_period(event);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (flags & PERF_EF_RELOAD) {
51462306a36Sopenharmony_ci		prev_cnt = local64_read(&hwc->prev_count);
51562306a36Sopenharmony_ci		hisi_pcie_pmu_writeq(pcie_pmu, hwc->event_base, idx, prev_cnt);
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	perf_event_update_userpage(event);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void hisi_pcie_pmu_stop(struct perf_event *event, int flags)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
52462306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	hisi_pcie_pmu_event_update(event);
52762306a36Sopenharmony_ci	hisi_pcie_pmu_disable_int(pcie_pmu, hwc);
52862306a36Sopenharmony_ci	hisi_pcie_pmu_disable_counter(pcie_pmu, hwc);
52962306a36Sopenharmony_ci	hisi_pcie_pmu_clear_filter(event);
53062306a36Sopenharmony_ci	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
53162306a36Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (hwc->state & PERF_HES_UPTODATE)
53462306a36Sopenharmony_ci		return;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	hwc->state |= PERF_HES_UPTODATE;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int hisi_pcie_pmu_add(struct perf_event *event, int flags)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
54262306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
54362306a36Sopenharmony_ci	int idx;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Check all working events to find a related event. */
54862306a36Sopenharmony_ci	idx = hisi_pcie_pmu_find_related_event(pcie_pmu, event);
54962306a36Sopenharmony_ci	if (idx < 0)
55062306a36Sopenharmony_ci		return idx;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Current event shares an enabled counter with the related event */
55362306a36Sopenharmony_ci	if (idx < HISI_PCIE_MAX_COUNTERS) {
55462306a36Sopenharmony_ci		hwc->idx = idx;
55562306a36Sopenharmony_ci		goto start_count;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	idx = hisi_pcie_pmu_get_event_idx(pcie_pmu);
55962306a36Sopenharmony_ci	if (idx < 0)
56062306a36Sopenharmony_ci		return idx;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	hwc->idx = idx;
56362306a36Sopenharmony_ci	pcie_pmu->hw_events[idx] = event;
56462306a36Sopenharmony_ci	/* Reset Counter to avoid previous statistic interference. */
56562306a36Sopenharmony_ci	hisi_pcie_pmu_reset_counter(pcie_pmu, idx);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistart_count:
56862306a36Sopenharmony_ci	if (flags & PERF_EF_START)
56962306a36Sopenharmony_ci		hisi_pcie_pmu_start(event, PERF_EF_RELOAD);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic void hisi_pcie_pmu_del(struct perf_event *event, int flags)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
57762306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	hisi_pcie_pmu_stop(event, PERF_EF_UPDATE);
58062306a36Sopenharmony_ci	pcie_pmu->hw_events[hwc->idx] = NULL;
58162306a36Sopenharmony_ci	perf_event_update_userpage(event);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void hisi_pcie_pmu_enable(struct pmu *pmu)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(pmu);
58762306a36Sopenharmony_ci	int num;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	for (num = 0; num < HISI_PCIE_MAX_COUNTERS; num++) {
59062306a36Sopenharmony_ci		if (pcie_pmu->hw_events[num])
59162306a36Sopenharmony_ci			break;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (num == HISI_PCIE_MAX_COUNTERS)
59562306a36Sopenharmony_ci		return;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	writel(HISI_PCIE_GLOBAL_EN, pcie_pmu->base + HISI_PCIE_GLOBAL_CTRL);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic void hisi_pcie_pmu_disable(struct pmu *pmu)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(pmu);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	writel(HISI_PCIE_GLOBAL_NONE, pcie_pmu->base + HISI_PCIE_GLOBAL_CTRL);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic irqreturn_t hisi_pcie_pmu_irq(int irq, void *data)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = data;
61062306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
61162306a36Sopenharmony_ci	struct perf_event *event;
61262306a36Sopenharmony_ci	u32 overflown;
61362306a36Sopenharmony_ci	int idx;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	for (idx = 0; idx < HISI_PCIE_MAX_COUNTERS; idx++) {
61662306a36Sopenharmony_ci		overflown = hisi_pcie_pmu_readl(pcie_pmu, HISI_PCIE_INT_STAT, idx);
61762306a36Sopenharmony_ci		if (!overflown)
61862306a36Sopenharmony_ci			continue;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		/* Clear status of interrupt. */
62162306a36Sopenharmony_ci		hisi_pcie_pmu_writel(pcie_pmu, HISI_PCIE_INT_STAT, idx, 1);
62262306a36Sopenharmony_ci		event = pcie_pmu->hw_events[idx];
62362306a36Sopenharmony_ci		if (!event)
62462306a36Sopenharmony_ci			continue;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		hisi_pcie_pmu_event_update(event);
62762306a36Sopenharmony_ci		hisi_pcie_pmu_set_period(event);
62862306a36Sopenharmony_ci		ret = IRQ_HANDLED;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return ret;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic int hisi_pcie_pmu_irq_register(struct pci_dev *pdev, struct hisi_pcie_pmu *pcie_pmu)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	int irq, ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
63962306a36Sopenharmony_ci	if (ret < 0) {
64062306a36Sopenharmony_ci		pci_err(pdev, "Failed to enable MSI vectors: %d\n", ret);
64162306a36Sopenharmony_ci		return ret;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	irq = pci_irq_vector(pdev, 0);
64562306a36Sopenharmony_ci	ret = request_irq(irq, hisi_pcie_pmu_irq, IRQF_NOBALANCING | IRQF_NO_THREAD, DRV_NAME,
64662306a36Sopenharmony_ci			  pcie_pmu);
64762306a36Sopenharmony_ci	if (ret) {
64862306a36Sopenharmony_ci		pci_err(pdev, "Failed to register IRQ: %d\n", ret);
64962306a36Sopenharmony_ci		pci_free_irq_vectors(pdev);
65062306a36Sopenharmony_ci		return ret;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	pcie_pmu->irq = irq;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic void hisi_pcie_pmu_irq_unregister(struct pci_dev *pdev, struct hisi_pcie_pmu *pcie_pmu)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	free_irq(pcie_pmu->irq, pcie_pmu);
66162306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic int hisi_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = hlist_entry_safe(node, struct hisi_pcie_pmu, node);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (pcie_pmu->on_cpu == -1) {
66962306a36Sopenharmony_ci		pcie_pmu->on_cpu = cpumask_local_spread(0, dev_to_node(&pcie_pmu->pdev->dev));
67062306a36Sopenharmony_ci		WARN_ON(irq_set_affinity(pcie_pmu->irq, cpumask_of(pcie_pmu->on_cpu)));
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return 0;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic int hisi_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = hlist_entry_safe(node, struct hisi_pcie_pmu, node);
67962306a36Sopenharmony_ci	unsigned int target;
68062306a36Sopenharmony_ci	cpumask_t mask;
68162306a36Sopenharmony_ci	int numa_node;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Nothing to do if this CPU doesn't own the PMU */
68462306a36Sopenharmony_ci	if (pcie_pmu->on_cpu != cpu)
68562306a36Sopenharmony_ci		return 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	pcie_pmu->on_cpu = -1;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* Choose a local CPU from all online cpus. */
69062306a36Sopenharmony_ci	numa_node = dev_to_node(&pcie_pmu->pdev->dev);
69162306a36Sopenharmony_ci	if (cpumask_and(&mask, cpumask_of_node(numa_node), cpu_online_mask) &&
69262306a36Sopenharmony_ci	    cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
69362306a36Sopenharmony_ci		target = cpumask_any(&mask);
69462306a36Sopenharmony_ci	else
69562306a36Sopenharmony_ci		target = cpumask_any_but(cpu_online_mask, cpu);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (target >= nr_cpu_ids) {
69862306a36Sopenharmony_ci		pci_err(pcie_pmu->pdev, "There is no CPU to set\n");
69962306a36Sopenharmony_ci		return 0;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target);
70362306a36Sopenharmony_ci	/* Use this CPU for event counting */
70462306a36Sopenharmony_ci	pcie_pmu->on_cpu = target;
70562306a36Sopenharmony_ci	WARN_ON(irq_set_affinity(pcie_pmu->irq, cpumask_of(target)));
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return 0;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic struct attribute *hisi_pcie_pmu_events_attr[] = {
71162306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mwr_latency, 0x0010),
71262306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mwr_cnt, 0x10010),
71362306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_latency, 0x0210),
71462306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_cnt, 0x10210),
71562306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_latency, 0x0011),
71662306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_cnt, 0x10011),
71762306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_flux, 0x0804),
71862306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_time, 0x10804),
71962306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_flux, 0x0405),
72062306a36Sopenharmony_ci	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_time, 0x10405),
72162306a36Sopenharmony_ci	NULL
72262306a36Sopenharmony_ci};
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic struct attribute_group hisi_pcie_pmu_events_group = {
72562306a36Sopenharmony_ci	.name = "events",
72662306a36Sopenharmony_ci	.attrs = hisi_pcie_pmu_events_attr,
72762306a36Sopenharmony_ci};
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic struct attribute *hisi_pcie_pmu_format_attr[] = {
73062306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(event, "config:0-16"),
73162306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(thr_len, "config1:0-3"),
73262306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(thr_mode, "config1:4"),
73362306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(trig_len, "config1:5-8"),
73462306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(trig_mode, "config1:9"),
73562306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(len_mode, "config1:10-11"),
73662306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(port, "config2:0-15"),
73762306a36Sopenharmony_ci	HISI_PCIE_PMU_FORMAT_ATTR(bdf, "config2:16-31"),
73862306a36Sopenharmony_ci	NULL
73962306a36Sopenharmony_ci};
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic const struct attribute_group hisi_pcie_pmu_format_group = {
74262306a36Sopenharmony_ci	.name = "format",
74362306a36Sopenharmony_ci	.attrs = hisi_pcie_pmu_format_attr,
74462306a36Sopenharmony_ci};
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic struct attribute *hisi_pcie_pmu_bus_attrs[] = {
74762306a36Sopenharmony_ci	&dev_attr_bus.attr,
74862306a36Sopenharmony_ci	NULL
74962306a36Sopenharmony_ci};
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic const struct attribute_group hisi_pcie_pmu_bus_attr_group = {
75262306a36Sopenharmony_ci	.attrs = hisi_pcie_pmu_bus_attrs,
75362306a36Sopenharmony_ci};
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic struct attribute *hisi_pcie_pmu_cpumask_attrs[] = {
75662306a36Sopenharmony_ci	&dev_attr_cpumask.attr,
75762306a36Sopenharmony_ci	NULL
75862306a36Sopenharmony_ci};
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic const struct attribute_group hisi_pcie_pmu_cpumask_attr_group = {
76162306a36Sopenharmony_ci	.attrs = hisi_pcie_pmu_cpumask_attrs,
76262306a36Sopenharmony_ci};
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic struct attribute *hisi_pcie_pmu_identifier_attrs[] = {
76562306a36Sopenharmony_ci	&dev_attr_identifier.attr,
76662306a36Sopenharmony_ci	NULL
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic const struct attribute_group hisi_pcie_pmu_identifier_attr_group = {
77062306a36Sopenharmony_ci	.attrs = hisi_pcie_pmu_identifier_attrs,
77162306a36Sopenharmony_ci};
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic const struct attribute_group *hisi_pcie_pmu_attr_groups[] = {
77462306a36Sopenharmony_ci	&hisi_pcie_pmu_events_group,
77562306a36Sopenharmony_ci	&hisi_pcie_pmu_format_group,
77662306a36Sopenharmony_ci	&hisi_pcie_pmu_bus_attr_group,
77762306a36Sopenharmony_ci	&hisi_pcie_pmu_cpumask_attr_group,
77862306a36Sopenharmony_ci	&hisi_pcie_pmu_identifier_attr_group,
77962306a36Sopenharmony_ci	NULL
78062306a36Sopenharmony_ci};
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int hisi_pcie_alloc_pmu(struct pci_dev *pdev, struct hisi_pcie_pmu *pcie_pmu)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	struct hisi_pcie_reg_pair regs;
78562306a36Sopenharmony_ci	u16 sicl_id, core_id;
78662306a36Sopenharmony_ci	char *name;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	regs = hisi_pcie_parse_reg_value(pcie_pmu, HISI_PCIE_REG_BDF);
78962306a36Sopenharmony_ci	pcie_pmu->bdf_min = regs.lo;
79062306a36Sopenharmony_ci	pcie_pmu->bdf_max = regs.hi;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	regs = hisi_pcie_parse_reg_value(pcie_pmu, HISI_PCIE_REG_INFO);
79362306a36Sopenharmony_ci	sicl_id = regs.hi;
79462306a36Sopenharmony_ci	core_id = regs.lo;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_pcie%u_core%u", sicl_id, core_id);
79762306a36Sopenharmony_ci	if (!name)
79862306a36Sopenharmony_ci		return -ENOMEM;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	pcie_pmu->pdev = pdev;
80162306a36Sopenharmony_ci	pcie_pmu->on_cpu = -1;
80262306a36Sopenharmony_ci	pcie_pmu->identifier = readl(pcie_pmu->base + HISI_PCIE_REG_VERSION);
80362306a36Sopenharmony_ci	pcie_pmu->pmu = (struct pmu) {
80462306a36Sopenharmony_ci		.name		= name,
80562306a36Sopenharmony_ci		.module		= THIS_MODULE,
80662306a36Sopenharmony_ci		.event_init	= hisi_pcie_pmu_event_init,
80762306a36Sopenharmony_ci		.pmu_enable	= hisi_pcie_pmu_enable,
80862306a36Sopenharmony_ci		.pmu_disable	= hisi_pcie_pmu_disable,
80962306a36Sopenharmony_ci		.add		= hisi_pcie_pmu_add,
81062306a36Sopenharmony_ci		.del		= hisi_pcie_pmu_del,
81162306a36Sopenharmony_ci		.start		= hisi_pcie_pmu_start,
81262306a36Sopenharmony_ci		.stop		= hisi_pcie_pmu_stop,
81362306a36Sopenharmony_ci		.read		= hisi_pcie_pmu_read,
81462306a36Sopenharmony_ci		.task_ctx_nr	= perf_invalid_context,
81562306a36Sopenharmony_ci		.attr_groups	= hisi_pcie_pmu_attr_groups,
81662306a36Sopenharmony_ci		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
81762306a36Sopenharmony_ci	};
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int hisi_pcie_init_pmu(struct pci_dev *pdev, struct hisi_pcie_pmu *pcie_pmu)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	int ret;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	pcie_pmu->base = pci_ioremap_bar(pdev, 2);
82762306a36Sopenharmony_ci	if (!pcie_pmu->base) {
82862306a36Sopenharmony_ci		pci_err(pdev, "Ioremap failed for pcie_pmu resource\n");
82962306a36Sopenharmony_ci		return -ENOMEM;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ret = hisi_pcie_alloc_pmu(pdev, pcie_pmu);
83362306a36Sopenharmony_ci	if (ret)
83462306a36Sopenharmony_ci		goto err_iounmap;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	ret = hisi_pcie_pmu_irq_register(pdev, pcie_pmu);
83762306a36Sopenharmony_ci	if (ret)
83862306a36Sopenharmony_ci		goto err_iounmap;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE, &pcie_pmu->node);
84162306a36Sopenharmony_ci	if (ret) {
84262306a36Sopenharmony_ci		pci_err(pdev, "Failed to register hotplug: %d\n", ret);
84362306a36Sopenharmony_ci		goto err_irq_unregister;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ret = perf_pmu_register(&pcie_pmu->pmu, pcie_pmu->pmu.name, -1);
84762306a36Sopenharmony_ci	if (ret) {
84862306a36Sopenharmony_ci		pci_err(pdev, "Failed to register PCIe PMU: %d\n", ret);
84962306a36Sopenharmony_ci		goto err_hotplug_unregister;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	return ret;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cierr_hotplug_unregister:
85562306a36Sopenharmony_ci	cpuhp_state_remove_instance_nocalls(
85662306a36Sopenharmony_ci		CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE, &pcie_pmu->node);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cierr_irq_unregister:
85962306a36Sopenharmony_ci	hisi_pcie_pmu_irq_unregister(pdev, pcie_pmu);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cierr_iounmap:
86262306a36Sopenharmony_ci	iounmap(pcie_pmu->base);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return ret;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic void hisi_pcie_uninit_pmu(struct pci_dev *pdev)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu = pci_get_drvdata(pdev);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	perf_pmu_unregister(&pcie_pmu->pmu);
87262306a36Sopenharmony_ci	cpuhp_state_remove_instance_nocalls(
87362306a36Sopenharmony_ci		CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE, &pcie_pmu->node);
87462306a36Sopenharmony_ci	hisi_pcie_pmu_irq_unregister(pdev, pcie_pmu);
87562306a36Sopenharmony_ci	iounmap(pcie_pmu->base);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int hisi_pcie_init_dev(struct pci_dev *pdev)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	int ret;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
88362306a36Sopenharmony_ci	if (ret) {
88462306a36Sopenharmony_ci		pci_err(pdev, "Failed to enable PCI device: %d\n", ret);
88562306a36Sopenharmony_ci		return ret;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ret = pcim_iomap_regions(pdev, BIT(2), DRV_NAME);
88962306a36Sopenharmony_ci	if (ret < 0) {
89062306a36Sopenharmony_ci		pci_err(pdev, "Failed to request PCI mem regions: %d\n", ret);
89162306a36Sopenharmony_ci		return ret;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	pci_set_master(pdev);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic int hisi_pcie_pmu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct hisi_pcie_pmu *pcie_pmu;
90262306a36Sopenharmony_ci	int ret;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	pcie_pmu = devm_kzalloc(&pdev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
90562306a36Sopenharmony_ci	if (!pcie_pmu)
90662306a36Sopenharmony_ci		return -ENOMEM;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	ret = hisi_pcie_init_dev(pdev);
90962306a36Sopenharmony_ci	if (ret)
91062306a36Sopenharmony_ci		return ret;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	ret = hisi_pcie_init_pmu(pdev, pcie_pmu);
91362306a36Sopenharmony_ci	if (ret)
91462306a36Sopenharmony_ci		return ret;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	pci_set_drvdata(pdev, pcie_pmu);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return ret;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic void hisi_pcie_pmu_remove(struct pci_dev *pdev)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	hisi_pcie_uninit_pmu(pdev);
92462306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic const struct pci_device_id hisi_pcie_pmu_ids[] = {
92862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa12d) },
92962306a36Sopenharmony_ci	{ 0, }
93062306a36Sopenharmony_ci};
93162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, hisi_pcie_pmu_ids);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic struct pci_driver hisi_pcie_pmu_driver = {
93462306a36Sopenharmony_ci	.name = DRV_NAME,
93562306a36Sopenharmony_ci	.id_table = hisi_pcie_pmu_ids,
93662306a36Sopenharmony_ci	.probe = hisi_pcie_pmu_probe,
93762306a36Sopenharmony_ci	.remove = hisi_pcie_pmu_remove,
93862306a36Sopenharmony_ci};
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic int __init hisi_pcie_module_init(void)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	int ret;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE,
94562306a36Sopenharmony_ci				      "AP_PERF_ARM_HISI_PCIE_PMU_ONLINE",
94662306a36Sopenharmony_ci				      hisi_pcie_pmu_online_cpu,
94762306a36Sopenharmony_ci				      hisi_pcie_pmu_offline_cpu);
94862306a36Sopenharmony_ci	if (ret) {
94962306a36Sopenharmony_ci		pr_err("Failed to setup PCIe PMU hotplug: %d\n", ret);
95062306a36Sopenharmony_ci		return ret;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	ret = pci_register_driver(&hisi_pcie_pmu_driver);
95462306a36Sopenharmony_ci	if (ret)
95562306a36Sopenharmony_ci		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	return ret;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_cimodule_init(hisi_pcie_module_init);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void __exit hisi_pcie_module_exit(void)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	pci_unregister_driver(&hisi_pcie_pmu_driver);
96462306a36Sopenharmony_ci	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PCIE_PMU_ONLINE);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_cimodule_exit(hisi_pcie_module_exit);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon PCIe PMU driver");
96962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
97062306a36Sopenharmony_ciMODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");
971