mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Adds PCIE-TGT PMU support in Tegra410 SOC. This PMU is instanced in each root complex in the SOC and it captures traffic originating from any source towards PCIE BAR and CXL HDM range. The traffic can be filtered based on the destination root port or target address range. Reviewed-by: Ilkka Koskinen <ilkka@os.amperecomputing.com> Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com> Signed-off-by: Will Deacon <will@kernel.org>
1075 lines
30 KiB
C
1075 lines
30 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
/* Support for NVIDIA specific attributes. */
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/property.h>
|
|
#include <linux/topology.h>
|
|
|
|
#include "arm_cspmu.h"
|
|
|
|
#define NV_PCIE_PORT_COUNT 10ULL
|
|
#define NV_PCIE_FILTER_ID_MASK GENMASK_ULL(NV_PCIE_PORT_COUNT - 1, 0)
|
|
|
|
#define NV_NVL_C2C_PORT_COUNT 2ULL
|
|
#define NV_NVL_C2C_FILTER_ID_MASK GENMASK_ULL(NV_NVL_C2C_PORT_COUNT - 1, 0)
|
|
|
|
#define NV_CNVL_PORT_COUNT 4ULL
|
|
#define NV_CNVL_FILTER_ID_MASK GENMASK_ULL(NV_CNVL_PORT_COUNT - 1, 0)
|
|
|
|
#define NV_UCF_SRC_COUNT 3ULL
|
|
#define NV_UCF_DST_COUNT 4ULL
|
|
#define NV_UCF_FILTER_ID_MASK GENMASK_ULL(11, 0)
|
|
#define NV_UCF_FILTER_SRC GENMASK_ULL(2, 0)
|
|
#define NV_UCF_FILTER_DST GENMASK_ULL(11, 8)
|
|
#define NV_UCF_FILTER_DEFAULT (NV_UCF_FILTER_SRC | NV_UCF_FILTER_DST)
|
|
|
|
#define NV_PCIE_V2_PORT_COUNT 8ULL
|
|
#define NV_PCIE_V2_FILTER_ID_MASK GENMASK_ULL(24, 0)
|
|
#define NV_PCIE_V2_FILTER_PORT GENMASK_ULL(NV_PCIE_V2_PORT_COUNT - 1, 0)
|
|
#define NV_PCIE_V2_FILTER_BDF_VAL GENMASK_ULL(23, NV_PCIE_V2_PORT_COUNT)
|
|
#define NV_PCIE_V2_FILTER_BDF_EN BIT(24)
|
|
#define NV_PCIE_V2_FILTER_BDF_VAL_EN GENMASK_ULL(24, NV_PCIE_V2_PORT_COUNT)
|
|
#define NV_PCIE_V2_FILTER_DEFAULT NV_PCIE_V2_FILTER_PORT
|
|
|
|
#define NV_PCIE_V2_DST_COUNT 5ULL
|
|
#define NV_PCIE_V2_FILTER2_ID_MASK GENMASK_ULL(4, 0)
|
|
#define NV_PCIE_V2_FILTER2_DST GENMASK_ULL(NV_PCIE_V2_DST_COUNT - 1, 0)
|
|
#define NV_PCIE_V2_FILTER2_DEFAULT NV_PCIE_V2_FILTER2_DST
|
|
|
|
#define NV_PCIE_TGT_PORT_COUNT 8ULL
|
|
#define NV_PCIE_TGT_EV_TYPE_CC 0x4
|
|
#define NV_PCIE_TGT_EV_TYPE_COUNT 3ULL
|
|
#define NV_PCIE_TGT_EV_TYPE_MASK GENMASK_ULL(NV_PCIE_TGT_EV_TYPE_COUNT - 1, 0)
|
|
#define NV_PCIE_TGT_FILTER2_MASK GENMASK_ULL(NV_PCIE_TGT_PORT_COUNT, 0)
|
|
#define NV_PCIE_TGT_FILTER2_PORT GENMASK_ULL(NV_PCIE_TGT_PORT_COUNT - 1, 0)
|
|
#define NV_PCIE_TGT_FILTER2_ADDR_EN BIT(NV_PCIE_TGT_PORT_COUNT)
|
|
#define NV_PCIE_TGT_FILTER2_ADDR GENMASK_ULL(15, NV_PCIE_TGT_PORT_COUNT)
|
|
#define NV_PCIE_TGT_FILTER2_DEFAULT NV_PCIE_TGT_FILTER2_PORT
|
|
|
|
#define NV_PCIE_TGT_ADDR_COUNT 8ULL
|
|
#define NV_PCIE_TGT_ADDR_STRIDE 20
|
|
#define NV_PCIE_TGT_ADDR_CTRL 0xD38
|
|
#define NV_PCIE_TGT_ADDR_BASE_LO 0xD3C
|
|
#define NV_PCIE_TGT_ADDR_BASE_HI 0xD40
|
|
#define NV_PCIE_TGT_ADDR_MASK_LO 0xD44
|
|
#define NV_PCIE_TGT_ADDR_MASK_HI 0xD48
|
|
|
|
#define NV_GENERIC_FILTER_ID_MASK GENMASK_ULL(31, 0)
|
|
|
|
#define NV_PRODID_MASK (PMIIDR_PRODUCTID | PMIIDR_VARIANT | PMIIDR_REVISION)
|
|
|
|
#define NV_FORMAT_NAME_GENERIC 0
|
|
|
|
#define to_nv_cspmu_ctx(cspmu) ((struct nv_cspmu_ctx *)(cspmu->impl.ctx))
|
|
|
|
#define NV_CSPMU_EVENT_ATTR_4_INNER(_pref, _num, _suff, _config) \
|
|
ARM_CSPMU_EVENT_ATTR(_pref##_num##_suff, _config)
|
|
|
|
#define NV_CSPMU_EVENT_ATTR_4(_pref, _suff, _config) \
|
|
NV_CSPMU_EVENT_ATTR_4_INNER(_pref, _0_, _suff, _config), \
|
|
NV_CSPMU_EVENT_ATTR_4_INNER(_pref, _1_, _suff, _config + 1), \
|
|
NV_CSPMU_EVENT_ATTR_4_INNER(_pref, _2_, _suff, _config + 2), \
|
|
NV_CSPMU_EVENT_ATTR_4_INNER(_pref, _3_, _suff, _config + 3)
|
|
|
|
struct nv_cspmu_ctx {
|
|
const char *name;
|
|
|
|
struct attribute **event_attr;
|
|
struct attribute **format_attr;
|
|
|
|
u32 filter_mask;
|
|
u32 filter_default_val;
|
|
u32 filter2_mask;
|
|
u32 filter2_default_val;
|
|
|
|
u32 (*get_filter)(const struct perf_event *event);
|
|
u32 (*get_filter2)(const struct perf_event *event);
|
|
|
|
void *data;
|
|
|
|
int (*init_data)(struct arm_cspmu *cspmu);
|
|
};
|
|
|
|
static struct attribute *scf_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(bus_cycles, 0x1d),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(scf_cache_allocate, 0xF0),
|
|
ARM_CSPMU_EVENT_ATTR(scf_cache_refill, 0xF1),
|
|
ARM_CSPMU_EVENT_ATTR(scf_cache, 0xF2),
|
|
ARM_CSPMU_EVENT_ATTR(scf_cache_wb, 0xF3),
|
|
|
|
NV_CSPMU_EVENT_ATTR_4(socket, rd_data, 0x101),
|
|
NV_CSPMU_EVENT_ATTR_4(socket, wb_data, 0x109),
|
|
|
|
NV_CSPMU_EVENT_ATTR_4(socket, rd_outstanding, 0x115),
|
|
|
|
NV_CSPMU_EVENT_ATTR_4(socket, rd_access, 0x12d),
|
|
NV_CSPMU_EVENT_ATTR_4(socket, wb_access, 0x135),
|
|
NV_CSPMU_EVENT_ATTR_4(socket, wr_access, 0x139),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(gmem_rd_data, 0x16d),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_rd_access, 0x16e),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_rd_outstanding, 0x16f),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_wb_data, 0x173),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_wb_access, 0x174),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_wr_data, 0x179),
|
|
ARM_CSPMU_EVENT_ATTR(gmem_wr_access, 0x17b),
|
|
|
|
NV_CSPMU_EVENT_ATTR_4(socket, wr_data, 0x17c),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(gmem_wr_total_bytes, 0x1a0),
|
|
ARM_CSPMU_EVENT_ATTR(remote_socket_wr_total_bytes, 0x1a1),
|
|
ARM_CSPMU_EVENT_ATTR(remote_socket_rd_data, 0x1a2),
|
|
ARM_CSPMU_EVENT_ATTR(remote_socket_rd_outstanding, 0x1a3),
|
|
ARM_CSPMU_EVENT_ATTR(remote_socket_rd_access, 0x1a4),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(cmem_rd_data, 0x1a5),
|
|
ARM_CSPMU_EVENT_ATTR(cmem_rd_access, 0x1a6),
|
|
ARM_CSPMU_EVENT_ATTR(cmem_rd_outstanding, 0x1a7),
|
|
ARM_CSPMU_EVENT_ATTR(cmem_wb_data, 0x1ab),
|
|
ARM_CSPMU_EVENT_ATTR(cmem_wb_access, 0x1ac),
|
|
ARM_CSPMU_EVENT_ATTR(cmem_wr_data, 0x1b1),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(cmem_wr_access, 0x1ca),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(cmem_wr_total_bytes, 0x1db),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *mcf_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(rd_bytes_loc, 0x0),
|
|
ARM_CSPMU_EVENT_ATTR(rd_bytes_rem, 0x1),
|
|
ARM_CSPMU_EVENT_ATTR(wr_bytes_loc, 0x2),
|
|
ARM_CSPMU_EVENT_ATTR(wr_bytes_rem, 0x3),
|
|
ARM_CSPMU_EVENT_ATTR(total_bytes_loc, 0x4),
|
|
ARM_CSPMU_EVENT_ATTR(total_bytes_rem, 0x5),
|
|
ARM_CSPMU_EVENT_ATTR(rd_req_loc, 0x6),
|
|
ARM_CSPMU_EVENT_ATTR(rd_req_rem, 0x7),
|
|
ARM_CSPMU_EVENT_ATTR(wr_req_loc, 0x8),
|
|
ARM_CSPMU_EVENT_ATTR(wr_req_rem, 0x9),
|
|
ARM_CSPMU_EVENT_ATTR(total_req_loc, 0xa),
|
|
ARM_CSPMU_EVENT_ATTR(total_req_rem, 0xb),
|
|
ARM_CSPMU_EVENT_ATTR(rd_cum_outs_loc, 0xc),
|
|
ARM_CSPMU_EVENT_ATTR(rd_cum_outs_rem, 0xd),
|
|
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *ucf_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(bus_cycles, 0x1D),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(slc_allocate, 0xF0),
|
|
ARM_CSPMU_EVENT_ATTR(slc_wb, 0xF3),
|
|
ARM_CSPMU_EVENT_ATTR(slc_refill_rd, 0x109),
|
|
ARM_CSPMU_EVENT_ATTR(slc_refill_wr, 0x10A),
|
|
ARM_CSPMU_EVENT_ATTR(slc_hit_rd, 0x119),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(slc_access_dataless, 0x183),
|
|
ARM_CSPMU_EVENT_ATTR(slc_access_atomic, 0x184),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(slc_access_rd, 0x111),
|
|
ARM_CSPMU_EVENT_ATTR(slc_access_wr, 0x112),
|
|
ARM_CSPMU_EVENT_ATTR(slc_bytes_rd, 0x113),
|
|
ARM_CSPMU_EVENT_ATTR(slc_bytes_wr, 0x114),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(mem_access_rd, 0x121),
|
|
ARM_CSPMU_EVENT_ATTR(mem_access_wr, 0x122),
|
|
ARM_CSPMU_EVENT_ATTR(mem_bytes_rd, 0x123),
|
|
ARM_CSPMU_EVENT_ATTR(mem_bytes_wr, 0x124),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(local_snoop, 0x180),
|
|
ARM_CSPMU_EVENT_ATTR(ext_snp_access, 0x181),
|
|
ARM_CSPMU_EVENT_ATTR(ext_snp_evict, 0x182),
|
|
|
|
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pcie_v2_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(rd_bytes, 0x0),
|
|
ARM_CSPMU_EVENT_ATTR(wr_bytes, 0x1),
|
|
ARM_CSPMU_EVENT_ATTR(rd_req, 0x2),
|
|
ARM_CSPMU_EVENT_ATTR(wr_req, 0x3),
|
|
ARM_CSPMU_EVENT_ATTR(rd_cum_outs, 0x4),
|
|
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pcie_tgt_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(rd_bytes, 0x0),
|
|
ARM_CSPMU_EVENT_ATTR(wr_bytes, 0x1),
|
|
ARM_CSPMU_EVENT_ATTR(rd_req, 0x2),
|
|
ARM_CSPMU_EVENT_ATTR(wr_req, 0x3),
|
|
ARM_CSPMU_EVENT_ATTR(cycles, NV_PCIE_TGT_EV_TYPE_CC),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *generic_pmu_event_attrs[] = {
|
|
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *scf_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *pcie_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_ATTR(root_port, "config1:0-9"),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *nvlink_c2c_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_ATTR(port, "config1:0-1"),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *cnvlink_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_ATTR(rem_socket, "config1:0-3"),
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *ucf_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_ATTR(src_loc_noncpu, "config1:0"),
|
|
ARM_CSPMU_FORMAT_ATTR(src_loc_cpu, "config1:1"),
|
|
ARM_CSPMU_FORMAT_ATTR(src_rem, "config1:2"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_cmem, "config1:8"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_gmem, "config1:9"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_other, "config1:10"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_rem, "config1:11"),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pcie_v2_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_ATTR(src_rp_mask, "config1:0-7"),
|
|
ARM_CSPMU_FORMAT_ATTR(src_bdf, "config1:8-23"),
|
|
ARM_CSPMU_FORMAT_ATTR(src_bdf_en, "config1:24"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_cmem, "config2:0"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_gmem, "config2:1"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_pcie_p2p, "config2:2"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_loc_pcie_cxl, "config2:3"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_rem, "config2:4"),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pcie_tgt_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_ATTR(event, "config:0-2"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_rp_mask, "config:3-10"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_addr_en, "config:11"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_addr_base, "config1:0-63"),
|
|
ARM_CSPMU_FORMAT_ATTR(dst_addr_mask, "config2:0-63"),
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *generic_pmu_format_attrs[] = {
|
|
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
|
ARM_CSPMU_FORMAT_FILTER_ATTR,
|
|
ARM_CSPMU_FORMAT_FILTER2_ATTR,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute **
|
|
nv_cspmu_get_event_attrs(const struct arm_cspmu *cspmu)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
|
|
|
|
return ctx->event_attr;
|
|
}
|
|
|
|
static struct attribute **
|
|
nv_cspmu_get_format_attrs(const struct arm_cspmu *cspmu)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
|
|
|
|
return ctx->format_attr;
|
|
}
|
|
|
|
static const char *
|
|
nv_cspmu_get_name(const struct arm_cspmu *cspmu)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
|
|
|
|
return ctx->name;
|
|
}
|
|
|
|
#if defined(CONFIG_ACPI) && defined(CONFIG_ARM64)
|
|
static int nv_cspmu_get_inst_id(const struct arm_cspmu *cspmu, u32 *id)
|
|
{
|
|
struct fwnode_handle *fwnode;
|
|
struct acpi_device *adev;
|
|
int ret;
|
|
|
|
adev = arm_cspmu_acpi_dev_get(cspmu);
|
|
if (!adev)
|
|
return -ENODEV;
|
|
|
|
fwnode = acpi_fwnode_handle(adev);
|
|
ret = fwnode_property_read_u32(fwnode, "instance_id", id);
|
|
if (ret)
|
|
dev_err(cspmu->dev, "Failed to get instance ID\n");
|
|
|
|
acpi_dev_put(adev);
|
|
return ret;
|
|
}
|
|
#else
|
|
static int nv_cspmu_get_inst_id(const struct arm_cspmu *cspmu, u32 *id)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
static u32 nv_cspmu_event_filter(const struct perf_event *event)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx =
|
|
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
|
|
|
const u32 filter_val = event->attr.config1 & ctx->filter_mask;
|
|
|
|
if (filter_val == 0)
|
|
return ctx->filter_default_val;
|
|
|
|
return filter_val;
|
|
}
|
|
|
|
static u32 nv_cspmu_event_filter2(const struct perf_event *event)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx =
|
|
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
|
|
|
const u32 filter_val = event->attr.config2 & ctx->filter2_mask;
|
|
|
|
if (filter_val == 0)
|
|
return ctx->filter2_default_val;
|
|
|
|
return filter_val;
|
|
}
|
|
|
|
static void nv_cspmu_set_ev_filter(struct arm_cspmu *cspmu,
|
|
const struct perf_event *event)
|
|
{
|
|
u32 filter, offset;
|
|
const struct nv_cspmu_ctx *ctx =
|
|
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
|
offset = 4 * event->hw.idx;
|
|
|
|
if (ctx->get_filter) {
|
|
filter = ctx->get_filter(event);
|
|
writel(filter, cspmu->base0 + PMEVFILTR + offset);
|
|
}
|
|
|
|
if (ctx->get_filter2) {
|
|
filter = ctx->get_filter2(event);
|
|
writel(filter, cspmu->base0 + PMEVFILT2R + offset);
|
|
}
|
|
}
|
|
|
|
static void nv_cspmu_reset_ev_filter(struct arm_cspmu *cspmu,
|
|
const struct perf_event *event)
|
|
{
|
|
const struct nv_cspmu_ctx *ctx =
|
|
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
|
const u32 offset = 4 * event->hw.idx;
|
|
|
|
if (ctx->get_filter)
|
|
writel(0, cspmu->base0 + PMEVFILTR + offset);
|
|
|
|
if (ctx->get_filter2)
|
|
writel(0, cspmu->base0 + PMEVFILT2R + offset);
|
|
}
|
|
|
|
static void nv_cspmu_set_cc_filter(struct arm_cspmu *cspmu,
|
|
const struct perf_event *event)
|
|
{
|
|
u32 filter = nv_cspmu_event_filter(event);
|
|
|
|
writel(filter, cspmu->base0 + PMCCFILTR);
|
|
}
|
|
|
|
static u32 ucf_pmu_event_filter(const struct perf_event *event)
|
|
{
|
|
u32 ret, filter, src, dst;
|
|
|
|
filter = nv_cspmu_event_filter(event);
|
|
|
|
/* Monitor all sources if none is selected. */
|
|
src = FIELD_GET(NV_UCF_FILTER_SRC, filter);
|
|
if (src == 0)
|
|
src = GENMASK_ULL(NV_UCF_SRC_COUNT - 1, 0);
|
|
|
|
/* Monitor all destinations if none is selected. */
|
|
dst = FIELD_GET(NV_UCF_FILTER_DST, filter);
|
|
if (dst == 0)
|
|
dst = GENMASK_ULL(NV_UCF_DST_COUNT - 1, 0);
|
|
|
|
ret = FIELD_PREP(NV_UCF_FILTER_SRC, src);
|
|
ret |= FIELD_PREP(NV_UCF_FILTER_DST, dst);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static u32 pcie_v2_pmu_bdf_val_en(u32 filter)
|
|
{
|
|
const u32 bdf_en = FIELD_GET(NV_PCIE_V2_FILTER_BDF_EN, filter);
|
|
|
|
/* Returns both BDF value and enable bit if BDF filtering is enabled. */
|
|
if (bdf_en)
|
|
return FIELD_GET(NV_PCIE_V2_FILTER_BDF_VAL_EN, filter);
|
|
|
|
/* Ignore the BDF value if BDF filter is not enabled. */
|
|
return 0;
|
|
}
|
|
|
|
static u32 pcie_v2_pmu_event_filter(const struct perf_event *event)
|
|
{
|
|
u32 filter, lead_filter, lead_bdf;
|
|
struct perf_event *leader;
|
|
const struct nv_cspmu_ctx *ctx =
|
|
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
|
|
|
filter = event->attr.config1 & ctx->filter_mask;
|
|
if (filter != 0)
|
|
return filter;
|
|
|
|
leader = event->group_leader;
|
|
|
|
/* Use leader's filter value if its BDF filtering is enabled. */
|
|
if (event != leader) {
|
|
lead_filter = pcie_v2_pmu_event_filter(leader);
|
|
lead_bdf = pcie_v2_pmu_bdf_val_en(lead_filter);
|
|
if (lead_bdf != 0)
|
|
return lead_filter;
|
|
}
|
|
|
|
/* Otherwise, return default filter value. */
|
|
return ctx->filter_default_val;
|
|
}
|
|
|
|
static int pcie_v2_pmu_validate_event(struct arm_cspmu *cspmu,
|
|
struct perf_event *new_ev)
|
|
{
|
|
/*
|
|
* Make sure the events are using same BDF filter since the PCIE-SRC PMU
|
|
* only supports one common BDF filter setting for all of the counters.
|
|
*/
|
|
|
|
int idx;
|
|
u32 new_filter, new_rp, new_bdf, new_lead_filter, new_lead_bdf;
|
|
struct perf_event *new_leader;
|
|
|
|
if (cspmu->impl.ops.is_cycle_counter_event(new_ev))
|
|
return 0;
|
|
|
|
new_leader = new_ev->group_leader;
|
|
|
|
new_filter = pcie_v2_pmu_event_filter(new_ev);
|
|
new_lead_filter = pcie_v2_pmu_event_filter(new_leader);
|
|
|
|
new_bdf = pcie_v2_pmu_bdf_val_en(new_filter);
|
|
new_lead_bdf = pcie_v2_pmu_bdf_val_en(new_lead_filter);
|
|
|
|
new_rp = FIELD_GET(NV_PCIE_V2_FILTER_PORT, new_filter);
|
|
|
|
if (new_rp != 0 && new_bdf != 0) {
|
|
dev_err(cspmu->dev,
|
|
"RP and BDF filtering are mutually exclusive\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (new_bdf != new_lead_bdf) {
|
|
dev_err(cspmu->dev,
|
|
"sibling and leader BDF value should be equal\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Compare BDF filter on existing events. */
|
|
idx = find_first_bit(cspmu->hw_events.used_ctrs,
|
|
cspmu->cycle_counter_logical_idx);
|
|
|
|
if (idx != cspmu->cycle_counter_logical_idx) {
|
|
struct perf_event *leader = cspmu->hw_events.events[idx]->group_leader;
|
|
|
|
const u32 lead_filter = pcie_v2_pmu_event_filter(leader);
|
|
const u32 lead_bdf = pcie_v2_pmu_bdf_val_en(lead_filter);
|
|
|
|
if (new_lead_bdf != lead_bdf) {
|
|
dev_err(cspmu->dev, "only one BDF value is supported\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct pcie_tgt_addr_filter {
|
|
u32 refcount;
|
|
u64 base;
|
|
u64 mask;
|
|
};
|
|
|
|
struct pcie_tgt_data {
|
|
struct pcie_tgt_addr_filter addr_filter[NV_PCIE_TGT_ADDR_COUNT];
|
|
void __iomem *addr_filter_reg;
|
|
};
|
|
|
|
#if defined(CONFIG_ACPI) && defined(CONFIG_ARM64)
|
|
static int pcie_tgt_init_data(struct arm_cspmu *cspmu)
|
|
{
|
|
int ret;
|
|
struct acpi_device *adev;
|
|
struct pcie_tgt_data *data;
|
|
struct list_head resource_list;
|
|
struct resource_entry *rentry;
|
|
struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
|
|
struct device *dev = cspmu->dev;
|
|
|
|
data = devm_kzalloc(dev, sizeof(struct pcie_tgt_data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
adev = arm_cspmu_acpi_dev_get(cspmu);
|
|
if (!adev) {
|
|
dev_err(dev, "failed to get associated PCIE-TGT device\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&resource_list);
|
|
ret = acpi_dev_get_memory_resources(adev, &resource_list);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to get PCIE-TGT device memory resources\n");
|
|
acpi_dev_put(adev);
|
|
return ret;
|
|
}
|
|
|
|
rentry = list_first_entry_or_null(
|
|
&resource_list, struct resource_entry, node);
|
|
if (rentry) {
|
|
data->addr_filter_reg = devm_ioremap_resource(dev, rentry->res);
|
|
ret = 0;
|
|
}
|
|
|
|
if (IS_ERR(data->addr_filter_reg)) {
|
|
dev_err(dev, "failed to get address filter resource\n");
|
|
ret = PTR_ERR(data->addr_filter_reg);
|
|
}
|
|
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
acpi_dev_put(adev);
|
|
|
|
ctx->data = data;
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
static int pcie_tgt_init_data(struct arm_cspmu *cspmu)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
|
|
static struct pcie_tgt_data *pcie_tgt_get_data(struct arm_cspmu *cspmu)
|
|
{
|
|
struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
|
|
|
|
return ctx->data;
|
|
}
|
|
|
|
/* Find the first available address filter slot. */
|
|
static int pcie_tgt_find_addr_idx(struct arm_cspmu *cspmu, u64 base, u64 mask,
|
|
bool is_reset)
|
|
{
|
|
int i;
|
|
struct pcie_tgt_data *data = pcie_tgt_get_data(cspmu);
|
|
|
|
for (i = 0; i < NV_PCIE_TGT_ADDR_COUNT; i++) {
|
|
if (!is_reset && data->addr_filter[i].refcount == 0)
|
|
return i;
|
|
|
|
if (data->addr_filter[i].base == base &&
|
|
data->addr_filter[i].mask == mask)
|
|
return i;
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static u32 pcie_tgt_pmu_event_filter(const struct perf_event *event)
|
|
{
|
|
u32 filter;
|
|
|
|
filter = (event->attr.config >> NV_PCIE_TGT_EV_TYPE_COUNT) &
|
|
NV_PCIE_TGT_FILTER2_MASK;
|
|
|
|
return filter;
|
|
}
|
|
|
|
static bool pcie_tgt_pmu_addr_en(const struct perf_event *event)
|
|
{
|
|
u32 filter = pcie_tgt_pmu_event_filter(event);
|
|
|
|
return FIELD_GET(NV_PCIE_TGT_FILTER2_ADDR_EN, filter) != 0;
|
|
}
|
|
|
|
static u32 pcie_tgt_pmu_port_filter(const struct perf_event *event)
|
|
{
|
|
u32 filter = pcie_tgt_pmu_event_filter(event);
|
|
|
|
return FIELD_GET(NV_PCIE_TGT_FILTER2_PORT, filter);
|
|
}
|
|
|
|
static u64 pcie_tgt_pmu_dst_addr_base(const struct perf_event *event)
|
|
{
|
|
return event->attr.config1;
|
|
}
|
|
|
|
static u64 pcie_tgt_pmu_dst_addr_mask(const struct perf_event *event)
|
|
{
|
|
return event->attr.config2;
|
|
}
|
|
|
|
static int pcie_tgt_pmu_validate_event(struct arm_cspmu *cspmu,
|
|
struct perf_event *new_ev)
|
|
{
|
|
u64 base, mask;
|
|
int idx;
|
|
|
|
if (!pcie_tgt_pmu_addr_en(new_ev))
|
|
return 0;
|
|
|
|
/* Make sure there is a slot available for the address filter. */
|
|
base = pcie_tgt_pmu_dst_addr_base(new_ev);
|
|
mask = pcie_tgt_pmu_dst_addr_mask(new_ev);
|
|
idx = pcie_tgt_find_addr_idx(cspmu, base, mask, false);
|
|
if (idx < 0)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pcie_tgt_pmu_config_addr_filter(struct arm_cspmu *cspmu,
|
|
bool en, u64 base, u64 mask, int idx)
|
|
{
|
|
struct pcie_tgt_data *data;
|
|
struct pcie_tgt_addr_filter *filter;
|
|
void __iomem *filter_reg;
|
|
|
|
data = pcie_tgt_get_data(cspmu);
|
|
filter = &data->addr_filter[idx];
|
|
filter_reg = data->addr_filter_reg + (idx * NV_PCIE_TGT_ADDR_STRIDE);
|
|
|
|
if (en) {
|
|
filter->refcount++;
|
|
if (filter->refcount == 1) {
|
|
filter->base = base;
|
|
filter->mask = mask;
|
|
|
|
writel(lower_32_bits(base), filter_reg + NV_PCIE_TGT_ADDR_BASE_LO);
|
|
writel(upper_32_bits(base), filter_reg + NV_PCIE_TGT_ADDR_BASE_HI);
|
|
writel(lower_32_bits(mask), filter_reg + NV_PCIE_TGT_ADDR_MASK_LO);
|
|
writel(upper_32_bits(mask), filter_reg + NV_PCIE_TGT_ADDR_MASK_HI);
|
|
writel(1, filter_reg + NV_PCIE_TGT_ADDR_CTRL);
|
|
}
|
|
} else {
|
|
filter->refcount--;
|
|
if (filter->refcount == 0) {
|
|
writel(0, filter_reg + NV_PCIE_TGT_ADDR_CTRL);
|
|
writel(0, filter_reg + NV_PCIE_TGT_ADDR_BASE_LO);
|
|
writel(0, filter_reg + NV_PCIE_TGT_ADDR_BASE_HI);
|
|
writel(0, filter_reg + NV_PCIE_TGT_ADDR_MASK_LO);
|
|
writel(0, filter_reg + NV_PCIE_TGT_ADDR_MASK_HI);
|
|
|
|
filter->base = 0;
|
|
filter->mask = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pcie_tgt_pmu_set_ev_filter(struct arm_cspmu *cspmu,
|
|
const struct perf_event *event)
|
|
{
|
|
bool addr_filter_en;
|
|
int idx;
|
|
u32 filter2_val, filter2_offset, port_filter;
|
|
u64 base, mask;
|
|
|
|
filter2_val = 0;
|
|
filter2_offset = PMEVFILT2R + (4 * event->hw.idx);
|
|
|
|
addr_filter_en = pcie_tgt_pmu_addr_en(event);
|
|
if (addr_filter_en) {
|
|
base = pcie_tgt_pmu_dst_addr_base(event);
|
|
mask = pcie_tgt_pmu_dst_addr_mask(event);
|
|
idx = pcie_tgt_find_addr_idx(cspmu, base, mask, false);
|
|
|
|
if (idx < 0) {
|
|
dev_err(cspmu->dev,
|
|
"Unable to find a slot for address filtering\n");
|
|
writel(0, cspmu->base0 + filter2_offset);
|
|
return;
|
|
}
|
|
|
|
/* Configure address range filter registers.*/
|
|
pcie_tgt_pmu_config_addr_filter(cspmu, true, base, mask, idx);
|
|
|
|
/* Config the counter to use the selected address filter slot. */
|
|
filter2_val |= FIELD_PREP(NV_PCIE_TGT_FILTER2_ADDR, 1U << idx);
|
|
}
|
|
|
|
port_filter = pcie_tgt_pmu_port_filter(event);
|
|
|
|
/* Monitor all ports if no filter is selected. */
|
|
if (!addr_filter_en && port_filter == 0)
|
|
port_filter = NV_PCIE_TGT_FILTER2_PORT;
|
|
|
|
filter2_val |= FIELD_PREP(NV_PCIE_TGT_FILTER2_PORT, port_filter);
|
|
|
|
writel(filter2_val, cspmu->base0 + filter2_offset);
|
|
}
|
|
|
|
static void pcie_tgt_pmu_reset_ev_filter(struct arm_cspmu *cspmu,
|
|
const struct perf_event *event)
|
|
{
|
|
bool addr_filter_en;
|
|
u64 base, mask;
|
|
int idx;
|
|
|
|
addr_filter_en = pcie_tgt_pmu_addr_en(event);
|
|
if (!addr_filter_en)
|
|
return;
|
|
|
|
base = pcie_tgt_pmu_dst_addr_base(event);
|
|
mask = pcie_tgt_pmu_dst_addr_mask(event);
|
|
idx = pcie_tgt_find_addr_idx(cspmu, base, mask, true);
|
|
|
|
if (idx < 0) {
|
|
dev_err(cspmu->dev,
|
|
"Unable to find the address filter slot to reset\n");
|
|
return;
|
|
}
|
|
|
|
pcie_tgt_pmu_config_addr_filter(cspmu, false, base, mask, idx);
|
|
}
|
|
|
|
static u32 pcie_tgt_pmu_event_type(const struct perf_event *event)
|
|
{
|
|
return event->attr.config & NV_PCIE_TGT_EV_TYPE_MASK;
|
|
}
|
|
|
|
static bool pcie_tgt_pmu_is_cycle_counter_event(const struct perf_event *event)
|
|
{
|
|
u32 event_type = pcie_tgt_pmu_event_type(event);
|
|
|
|
return event_type == NV_PCIE_TGT_EV_TYPE_CC;
|
|
}
|
|
|
|
enum nv_cspmu_name_fmt {
|
|
NAME_FMT_GENERIC,
|
|
NAME_FMT_SOCKET,
|
|
NAME_FMT_SOCKET_INST,
|
|
};
|
|
|
|
struct nv_cspmu_match {
|
|
u32 prodid;
|
|
u32 prodid_mask;
|
|
const char *name_pattern;
|
|
enum nv_cspmu_name_fmt name_fmt;
|
|
struct nv_cspmu_ctx template_ctx;
|
|
struct arm_cspmu_impl_ops ops;
|
|
};
|
|
|
|
static const struct nv_cspmu_match nv_cspmu_match[] = {
|
|
{
|
|
.prodid = 0x10300000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_pcie_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = mcf_pmu_event_attrs,
|
|
.format_attr = pcie_pmu_format_attrs,
|
|
.filter_mask = NV_PCIE_FILTER_ID_MASK,
|
|
.filter_default_val = NV_PCIE_FILTER_ID_MASK,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = NULL,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x10400000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_nvlink_c2c1_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = mcf_pmu_event_attrs,
|
|
.format_attr = nvlink_c2c_pmu_format_attrs,
|
|
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
|
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = NULL,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x10500000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_nvlink_c2c0_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = mcf_pmu_event_attrs,
|
|
.format_attr = nvlink_c2c_pmu_format_attrs,
|
|
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
|
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = NULL,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x10600000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_cnvlink_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = mcf_pmu_event_attrs,
|
|
.format_attr = cnvlink_pmu_format_attrs,
|
|
.filter_mask = NV_CNVL_FILTER_ID_MASK,
|
|
.filter_default_val = NV_CNVL_FILTER_ID_MASK,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = NULL,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x2CF00000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_scf_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = scf_pmu_event_attrs,
|
|
.format_attr = scf_pmu_format_attrs,
|
|
.filter_mask = 0x0,
|
|
.filter_default_val = 0x0,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = NULL,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x2CF20000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_ucf_pmu_%u",
|
|
.name_fmt = NAME_FMT_SOCKET,
|
|
.template_ctx = {
|
|
.event_attr = ucf_pmu_event_attrs,
|
|
.format_attr = ucf_pmu_format_attrs,
|
|
.filter_mask = NV_UCF_FILTER_ID_MASK,
|
|
.filter_default_val = NV_UCF_FILTER_DEFAULT,
|
|
.filter2_mask = 0x0,
|
|
.filter2_default_val = 0x0,
|
|
.get_filter = ucf_pmu_event_filter,
|
|
},
|
|
},
|
|
{
|
|
.prodid = 0x10301000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_pcie_pmu_%u_rc_%u",
|
|
.name_fmt = NAME_FMT_SOCKET_INST,
|
|
.template_ctx = {
|
|
.event_attr = pcie_v2_pmu_event_attrs,
|
|
.format_attr = pcie_v2_pmu_format_attrs,
|
|
.filter_mask = NV_PCIE_V2_FILTER_ID_MASK,
|
|
.filter_default_val = NV_PCIE_V2_FILTER_DEFAULT,
|
|
.filter2_mask = NV_PCIE_V2_FILTER2_ID_MASK,
|
|
.filter2_default_val = NV_PCIE_V2_FILTER2_DEFAULT,
|
|
.get_filter = pcie_v2_pmu_event_filter,
|
|
.get_filter2 = nv_cspmu_event_filter2,
|
|
},
|
|
.ops = {
|
|
.validate_event = pcie_v2_pmu_validate_event,
|
|
.reset_ev_filter = nv_cspmu_reset_ev_filter,
|
|
}
|
|
},
|
|
{
|
|
.prodid = 0x10700000,
|
|
.prodid_mask = NV_PRODID_MASK,
|
|
.name_pattern = "nvidia_pcie_tgt_pmu_%u_rc_%u",
|
|
.name_fmt = NAME_FMT_SOCKET_INST,
|
|
.template_ctx = {
|
|
.event_attr = pcie_tgt_pmu_event_attrs,
|
|
.format_attr = pcie_tgt_pmu_format_attrs,
|
|
.filter_mask = 0x0,
|
|
.filter_default_val = 0x0,
|
|
.filter2_mask = NV_PCIE_TGT_FILTER2_MASK,
|
|
.filter2_default_val = NV_PCIE_TGT_FILTER2_DEFAULT,
|
|
.init_data = pcie_tgt_init_data
|
|
},
|
|
.ops = {
|
|
.is_cycle_counter_event = pcie_tgt_pmu_is_cycle_counter_event,
|
|
.event_type = pcie_tgt_pmu_event_type,
|
|
.validate_event = pcie_tgt_pmu_validate_event,
|
|
.set_ev_filter = pcie_tgt_pmu_set_ev_filter,
|
|
.reset_ev_filter = pcie_tgt_pmu_reset_ev_filter,
|
|
}
|
|
},
|
|
{
|
|
.prodid = 0,
|
|
.prodid_mask = 0,
|
|
.name_pattern = "nvidia_uncore_pmu_%u",
|
|
.name_fmt = NAME_FMT_GENERIC,
|
|
.template_ctx = {
|
|
.event_attr = generic_pmu_event_attrs,
|
|
.format_attr = generic_pmu_format_attrs,
|
|
.filter_mask = NV_GENERIC_FILTER_ID_MASK,
|
|
.filter_default_val = NV_GENERIC_FILTER_ID_MASK,
|
|
.filter2_mask = NV_GENERIC_FILTER_ID_MASK,
|
|
.filter2_default_val = NV_GENERIC_FILTER_ID_MASK,
|
|
.get_filter = nv_cspmu_event_filter,
|
|
.get_filter2 = nv_cspmu_event_filter2,
|
|
.data = NULL,
|
|
.init_data = NULL
|
|
},
|
|
},
|
|
};
|
|
|
|
static char *nv_cspmu_format_name(const struct arm_cspmu *cspmu,
|
|
const struct nv_cspmu_match *match)
|
|
{
|
|
char *name = NULL;
|
|
struct device *dev = cspmu->dev;
|
|
|
|
static atomic_t pmu_generic_idx = {0};
|
|
|
|
switch (match->name_fmt) {
|
|
case NAME_FMT_SOCKET: {
|
|
const int cpu = cpumask_first(&cspmu->associated_cpus);
|
|
const int socket = cpu_to_node(cpu);
|
|
|
|
name = devm_kasprintf(dev, GFP_KERNEL, match->name_pattern,
|
|
socket);
|
|
break;
|
|
}
|
|
case NAME_FMT_SOCKET_INST: {
|
|
const int cpu = cpumask_first(&cspmu->associated_cpus);
|
|
const int socket = cpu_to_node(cpu);
|
|
u32 inst_id;
|
|
|
|
if (!nv_cspmu_get_inst_id(cspmu, &inst_id))
|
|
name = devm_kasprintf(dev, GFP_KERNEL,
|
|
match->name_pattern, socket, inst_id);
|
|
break;
|
|
}
|
|
case NAME_FMT_GENERIC:
|
|
name = devm_kasprintf(dev, GFP_KERNEL, match->name_pattern,
|
|
atomic_fetch_inc(&pmu_generic_idx));
|
|
break;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
#define SET_OP(name, impl, match, default_op) \
|
|
do { \
|
|
if (match->ops.name) \
|
|
impl->name = match->ops.name; \
|
|
else if (default_op != NULL) \
|
|
impl->name = default_op; \
|
|
} while (false)
|
|
|
|
static int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
|
|
{
|
|
struct nv_cspmu_ctx *ctx;
|
|
struct device *dev = cspmu->dev;
|
|
struct arm_cspmu_impl_ops *impl_ops = &cspmu->impl.ops;
|
|
const struct nv_cspmu_match *match = nv_cspmu_match;
|
|
|
|
ctx = devm_kzalloc(dev, sizeof(struct nv_cspmu_ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
|
|
/* Find matching PMU. */
|
|
for (; match->prodid; match++) {
|
|
const u32 prodid_mask = match->prodid_mask;
|
|
|
|
if ((match->prodid & prodid_mask) ==
|
|
(cspmu->impl.pmiidr & prodid_mask))
|
|
break;
|
|
}
|
|
|
|
/* Initialize the context with the matched template. */
|
|
memcpy(ctx, &match->template_ctx, sizeof(struct nv_cspmu_ctx));
|
|
ctx->name = nv_cspmu_format_name(cspmu, match);
|
|
|
|
cspmu->impl.ctx = ctx;
|
|
|
|
/* NVIDIA specific callbacks. */
|
|
SET_OP(validate_event, impl_ops, match, NULL);
|
|
SET_OP(event_type, impl_ops, match, NULL);
|
|
SET_OP(is_cycle_counter_event, impl_ops, match, NULL);
|
|
SET_OP(set_cc_filter, impl_ops, match, nv_cspmu_set_cc_filter);
|
|
SET_OP(set_ev_filter, impl_ops, match, nv_cspmu_set_ev_filter);
|
|
SET_OP(reset_ev_filter, impl_ops, match, NULL);
|
|
SET_OP(get_event_attrs, impl_ops, match, nv_cspmu_get_event_attrs);
|
|
SET_OP(get_format_attrs, impl_ops, match, nv_cspmu_get_format_attrs);
|
|
SET_OP(get_name, impl_ops, match, nv_cspmu_get_name);
|
|
|
|
if (ctx->init_data)
|
|
return ctx->init_data(cspmu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Match all NVIDIA Coresight PMU devices */
|
|
static const struct arm_cspmu_impl_match nv_cspmu_param = {
|
|
.pmiidr_val = ARM_CSPMU_IMPL_ID_NVIDIA,
|
|
.module = THIS_MODULE,
|
|
.impl_init_ops = nv_cspmu_init_ops
|
|
};
|
|
|
|
static int __init nvidia_cspmu_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = arm_cspmu_impl_register(&nv_cspmu_param);
|
|
if (ret)
|
|
pr_err("nvidia_cspmu backend registration error: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit nvidia_cspmu_exit(void)
|
|
{
|
|
arm_cspmu_impl_unregister(&nv_cspmu_param);
|
|
}
|
|
|
|
module_init(nvidia_cspmu_init);
|
|
module_exit(nvidia_cspmu_exit);
|
|
|
|
MODULE_DESCRIPTION("NVIDIA Coresight Architecture Performance Monitor Driver");
|
|
MODULE_LICENSE("GPL v2");
|