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