1*2f89b7f7SBesar Wicaksono // SPDX-License-Identifier: GPL-2.0 2*2f89b7f7SBesar Wicaksono /* 3*2f89b7f7SBesar Wicaksono * NVIDIA Tegra410 C2C PMU driver. 4*2f89b7f7SBesar Wicaksono * 5*2f89b7f7SBesar Wicaksono * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 6*2f89b7f7SBesar Wicaksono */ 7*2f89b7f7SBesar Wicaksono 8*2f89b7f7SBesar Wicaksono #include <linux/acpi.h> 9*2f89b7f7SBesar Wicaksono #include <linux/bitops.h> 10*2f89b7f7SBesar Wicaksono #include <linux/cpumask.h> 11*2f89b7f7SBesar Wicaksono #include <linux/device.h> 12*2f89b7f7SBesar Wicaksono #include <linux/interrupt.h> 13*2f89b7f7SBesar Wicaksono #include <linux/io.h> 14*2f89b7f7SBesar Wicaksono #include <linux/module.h> 15*2f89b7f7SBesar Wicaksono #include <linux/perf_event.h> 16*2f89b7f7SBesar Wicaksono #include <linux/platform_device.h> 17*2f89b7f7SBesar Wicaksono #include <linux/property.h> 18*2f89b7f7SBesar Wicaksono 19*2f89b7f7SBesar Wicaksono /* The C2C interface types in Tegra410. */ 20*2f89b7f7SBesar Wicaksono #define C2C_TYPE_NVLINK 0x0 21*2f89b7f7SBesar Wicaksono #define C2C_TYPE_NVCLINK 0x1 22*2f89b7f7SBesar Wicaksono #define C2C_TYPE_NVDLINK 0x2 23*2f89b7f7SBesar Wicaksono #define C2C_TYPE_COUNT 0x3 24*2f89b7f7SBesar Wicaksono 25*2f89b7f7SBesar Wicaksono /* The type of the peer device connected to the C2C interface. */ 26*2f89b7f7SBesar Wicaksono #define C2C_PEER_TYPE_CPU 0x0 27*2f89b7f7SBesar Wicaksono #define C2C_PEER_TYPE_GPU 0x1 28*2f89b7f7SBesar Wicaksono #define C2C_PEER_TYPE_CXLMEM 0x2 29*2f89b7f7SBesar Wicaksono #define C2C_PEER_TYPE_COUNT 0x3 30*2f89b7f7SBesar Wicaksono 31*2f89b7f7SBesar Wicaksono /* The number of peer devices can be connected to the C2C interface. */ 32*2f89b7f7SBesar Wicaksono #define C2C_NR_PEER_CPU 0x1 33*2f89b7f7SBesar Wicaksono #define C2C_NR_PEER_GPU 0x2 34*2f89b7f7SBesar Wicaksono #define C2C_NR_PEER_CXLMEM 0x1 35*2f89b7f7SBesar Wicaksono #define C2C_NR_PEER_MAX 0x2 36*2f89b7f7SBesar Wicaksono 37*2f89b7f7SBesar Wicaksono /* Number of instances on each interface. */ 38*2f89b7f7SBesar Wicaksono #define C2C_NR_INST_NVLINK 14 39*2f89b7f7SBesar Wicaksono #define C2C_NR_INST_NVCLINK 12 40*2f89b7f7SBesar Wicaksono #define C2C_NR_INST_NVDLINK 16 41*2f89b7f7SBesar Wicaksono #define C2C_NR_INST_MAX 16 42*2f89b7f7SBesar Wicaksono 43*2f89b7f7SBesar Wicaksono /* Register offsets. */ 44*2f89b7f7SBesar Wicaksono #define C2C_CTRL 0x864 45*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS 0x868 46*2f89b7f7SBesar Wicaksono #define C2C_CYCLE_CNTR 0x86c 47*2f89b7f7SBesar Wicaksono #define C2C_IN_RD_CUM_OUTS_CNTR 0x874 48*2f89b7f7SBesar Wicaksono #define C2C_IN_RD_REQ_CNTR 0x87c 49*2f89b7f7SBesar Wicaksono #define C2C_IN_WR_CUM_OUTS_CNTR 0x884 50*2f89b7f7SBesar Wicaksono #define C2C_IN_WR_REQ_CNTR 0x88c 51*2f89b7f7SBesar Wicaksono #define C2C_OUT_STATUS 0x890 52*2f89b7f7SBesar Wicaksono #define C2C_OUT_RD_CUM_OUTS_CNTR 0x898 53*2f89b7f7SBesar Wicaksono #define C2C_OUT_RD_REQ_CNTR 0x8a0 54*2f89b7f7SBesar Wicaksono #define C2C_OUT_WR_CUM_OUTS_CNTR 0x8a8 55*2f89b7f7SBesar Wicaksono #define C2C_OUT_WR_REQ_CNTR 0x8b0 56*2f89b7f7SBesar Wicaksono 57*2f89b7f7SBesar Wicaksono /* C2C_IN_STATUS register field. */ 58*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS_CYCLE_OVF BIT(0) 59*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS_IN_RD_CUM_OUTS_OVF BIT(1) 60*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS_IN_RD_REQ_OVF BIT(2) 61*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS_IN_WR_CUM_OUTS_OVF BIT(3) 62*2f89b7f7SBesar Wicaksono #define C2C_IN_STATUS_IN_WR_REQ_OVF BIT(4) 63*2f89b7f7SBesar Wicaksono 64*2f89b7f7SBesar Wicaksono /* C2C_OUT_STATUS register field. */ 65*2f89b7f7SBesar Wicaksono #define C2C_OUT_STATUS_OUT_RD_CUM_OUTS_OVF BIT(0) 66*2f89b7f7SBesar Wicaksono #define C2C_OUT_STATUS_OUT_RD_REQ_OVF BIT(1) 67*2f89b7f7SBesar Wicaksono #define C2C_OUT_STATUS_OUT_WR_CUM_OUTS_OVF BIT(2) 68*2f89b7f7SBesar Wicaksono #define C2C_OUT_STATUS_OUT_WR_REQ_OVF BIT(3) 69*2f89b7f7SBesar Wicaksono 70*2f89b7f7SBesar Wicaksono /* Events. */ 71*2f89b7f7SBesar Wicaksono #define C2C_EVENT_CYCLES 0x0 72*2f89b7f7SBesar Wicaksono #define C2C_EVENT_IN_RD_CUM_OUTS 0x1 73*2f89b7f7SBesar Wicaksono #define C2C_EVENT_IN_RD_REQ 0x2 74*2f89b7f7SBesar Wicaksono #define C2C_EVENT_IN_WR_CUM_OUTS 0x3 75*2f89b7f7SBesar Wicaksono #define C2C_EVENT_IN_WR_REQ 0x4 76*2f89b7f7SBesar Wicaksono #define C2C_EVENT_OUT_RD_CUM_OUTS 0x5 77*2f89b7f7SBesar Wicaksono #define C2C_EVENT_OUT_RD_REQ 0x6 78*2f89b7f7SBesar Wicaksono #define C2C_EVENT_OUT_WR_CUM_OUTS 0x7 79*2f89b7f7SBesar Wicaksono #define C2C_EVENT_OUT_WR_REQ 0x8 80*2f89b7f7SBesar Wicaksono 81*2f89b7f7SBesar Wicaksono #define C2C_NUM_EVENTS 0x9 82*2f89b7f7SBesar Wicaksono #define C2C_MASK_EVENT 0xFF 83*2f89b7f7SBesar Wicaksono #define C2C_MAX_ACTIVE_EVENTS 32 84*2f89b7f7SBesar Wicaksono 85*2f89b7f7SBesar Wicaksono #define C2C_ACTIVE_CPU_MASK 0x0 86*2f89b7f7SBesar Wicaksono #define C2C_ASSOCIATED_CPU_MASK 0x1 87*2f89b7f7SBesar Wicaksono 88*2f89b7f7SBesar Wicaksono /* 89*2f89b7f7SBesar Wicaksono * Maximum poll count for reading counter value using high-low-high sequence. 90*2f89b7f7SBesar Wicaksono */ 91*2f89b7f7SBesar Wicaksono #define HILOHI_MAX_POLL 1000 92*2f89b7f7SBesar Wicaksono 93*2f89b7f7SBesar Wicaksono static unsigned long nv_c2c_pmu_cpuhp_state; 94*2f89b7f7SBesar Wicaksono 95*2f89b7f7SBesar Wicaksono /* PMU descriptor. */ 96*2f89b7f7SBesar Wicaksono 97*2f89b7f7SBesar Wicaksono /* C2C type information. */ 98*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_data { 99*2f89b7f7SBesar Wicaksono unsigned int c2c_type; 100*2f89b7f7SBesar Wicaksono unsigned int nr_inst; 101*2f89b7f7SBesar Wicaksono const char *name_fmt; 102*2f89b7f7SBesar Wicaksono }; 103*2f89b7f7SBesar Wicaksono 104*2f89b7f7SBesar Wicaksono static const struct nv_c2c_pmu_data nv_c2c_pmu_data[] = { 105*2f89b7f7SBesar Wicaksono [C2C_TYPE_NVLINK] = { 106*2f89b7f7SBesar Wicaksono .c2c_type = C2C_TYPE_NVLINK, 107*2f89b7f7SBesar Wicaksono .nr_inst = C2C_NR_INST_NVLINK, 108*2f89b7f7SBesar Wicaksono .name_fmt = "nvidia_nvlink_c2c_pmu_%u", 109*2f89b7f7SBesar Wicaksono }, 110*2f89b7f7SBesar Wicaksono [C2C_TYPE_NVCLINK] = { 111*2f89b7f7SBesar Wicaksono .c2c_type = C2C_TYPE_NVCLINK, 112*2f89b7f7SBesar Wicaksono .nr_inst = C2C_NR_INST_NVCLINK, 113*2f89b7f7SBesar Wicaksono .name_fmt = "nvidia_nvclink_pmu_%u", 114*2f89b7f7SBesar Wicaksono }, 115*2f89b7f7SBesar Wicaksono [C2C_TYPE_NVDLINK] = { 116*2f89b7f7SBesar Wicaksono .c2c_type = C2C_TYPE_NVDLINK, 117*2f89b7f7SBesar Wicaksono .nr_inst = C2C_NR_INST_NVDLINK, 118*2f89b7f7SBesar Wicaksono .name_fmt = "nvidia_nvdlink_pmu_%u", 119*2f89b7f7SBesar Wicaksono }, 120*2f89b7f7SBesar Wicaksono }; 121*2f89b7f7SBesar Wicaksono 122*2f89b7f7SBesar Wicaksono /* Tracks the events assigned to the PMU for a given logical index. */ 123*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events { 124*2f89b7f7SBesar Wicaksono /* The events that are active. */ 125*2f89b7f7SBesar Wicaksono struct perf_event *events[C2C_MAX_ACTIVE_EVENTS]; 126*2f89b7f7SBesar Wicaksono 127*2f89b7f7SBesar Wicaksono /* 128*2f89b7f7SBesar Wicaksono * Each bit indicates a logical counter is being used (or not) for an 129*2f89b7f7SBesar Wicaksono * event. 130*2f89b7f7SBesar Wicaksono */ 131*2f89b7f7SBesar Wicaksono DECLARE_BITMAP(used_ctrs, C2C_MAX_ACTIVE_EVENTS); 132*2f89b7f7SBesar Wicaksono }; 133*2f89b7f7SBesar Wicaksono 134*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu { 135*2f89b7f7SBesar Wicaksono struct pmu pmu; 136*2f89b7f7SBesar Wicaksono struct device *dev; 137*2f89b7f7SBesar Wicaksono struct acpi_device *acpi_dev; 138*2f89b7f7SBesar Wicaksono 139*2f89b7f7SBesar Wicaksono const char *name; 140*2f89b7f7SBesar Wicaksono const char *identifier; 141*2f89b7f7SBesar Wicaksono 142*2f89b7f7SBesar Wicaksono const struct nv_c2c_pmu_data *data; 143*2f89b7f7SBesar Wicaksono unsigned int peer_type; 144*2f89b7f7SBesar Wicaksono unsigned int socket; 145*2f89b7f7SBesar Wicaksono unsigned int nr_peer; 146*2f89b7f7SBesar Wicaksono unsigned long peer_insts[C2C_NR_PEER_MAX][BITS_TO_LONGS(C2C_NR_INST_MAX)]; 147*2f89b7f7SBesar Wicaksono u32 filter_default; 148*2f89b7f7SBesar Wicaksono 149*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events hw_events; 150*2f89b7f7SBesar Wicaksono 151*2f89b7f7SBesar Wicaksono cpumask_t associated_cpus; 152*2f89b7f7SBesar Wicaksono cpumask_t active_cpu; 153*2f89b7f7SBesar Wicaksono 154*2f89b7f7SBesar Wicaksono struct hlist_node cpuhp_node; 155*2f89b7f7SBesar Wicaksono 156*2f89b7f7SBesar Wicaksono const struct attribute_group **attr_groups; 157*2f89b7f7SBesar Wicaksono 158*2f89b7f7SBesar Wicaksono void __iomem *base_broadcast; 159*2f89b7f7SBesar Wicaksono void __iomem *base[C2C_NR_INST_MAX]; 160*2f89b7f7SBesar Wicaksono }; 161*2f89b7f7SBesar Wicaksono 162*2f89b7f7SBesar Wicaksono #define to_c2c_pmu(p) (container_of(p, struct nv_c2c_pmu, pmu)) 163*2f89b7f7SBesar Wicaksono 164*2f89b7f7SBesar Wicaksono /* Get event type from perf_event. */ 165*2f89b7f7SBesar Wicaksono static inline u32 get_event_type(struct perf_event *event) 166*2f89b7f7SBesar Wicaksono { 167*2f89b7f7SBesar Wicaksono return (event->attr.config) & C2C_MASK_EVENT; 168*2f89b7f7SBesar Wicaksono } 169*2f89b7f7SBesar Wicaksono 170*2f89b7f7SBesar Wicaksono static inline u32 get_filter_mask(struct perf_event *event) 171*2f89b7f7SBesar Wicaksono { 172*2f89b7f7SBesar Wicaksono u32 filter; 173*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(event->pmu); 174*2f89b7f7SBesar Wicaksono 175*2f89b7f7SBesar Wicaksono filter = ((u32)event->attr.config1) & c2c_pmu->filter_default; 176*2f89b7f7SBesar Wicaksono if (filter == 0) 177*2f89b7f7SBesar Wicaksono filter = c2c_pmu->filter_default; 178*2f89b7f7SBesar Wicaksono 179*2f89b7f7SBesar Wicaksono return filter; 180*2f89b7f7SBesar Wicaksono } 181*2f89b7f7SBesar Wicaksono 182*2f89b7f7SBesar Wicaksono /* PMU operations. */ 183*2f89b7f7SBesar Wicaksono 184*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_get_event_idx(struct nv_c2c_pmu_hw_events *hw_events, 185*2f89b7f7SBesar Wicaksono struct perf_event *event) 186*2f89b7f7SBesar Wicaksono { 187*2f89b7f7SBesar Wicaksono u32 idx; 188*2f89b7f7SBesar Wicaksono 189*2f89b7f7SBesar Wicaksono idx = find_first_zero_bit(hw_events->used_ctrs, C2C_MAX_ACTIVE_EVENTS); 190*2f89b7f7SBesar Wicaksono if (idx >= C2C_MAX_ACTIVE_EVENTS) 191*2f89b7f7SBesar Wicaksono return -EAGAIN; 192*2f89b7f7SBesar Wicaksono 193*2f89b7f7SBesar Wicaksono set_bit(idx, hw_events->used_ctrs); 194*2f89b7f7SBesar Wicaksono 195*2f89b7f7SBesar Wicaksono return idx; 196*2f89b7f7SBesar Wicaksono } 197*2f89b7f7SBesar Wicaksono 198*2f89b7f7SBesar Wicaksono static bool 199*2f89b7f7SBesar Wicaksono nv_c2c_pmu_validate_event(struct pmu *pmu, 200*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events *hw_events, 201*2f89b7f7SBesar Wicaksono struct perf_event *event) 202*2f89b7f7SBesar Wicaksono { 203*2f89b7f7SBesar Wicaksono if (is_software_event(event)) 204*2f89b7f7SBesar Wicaksono return true; 205*2f89b7f7SBesar Wicaksono 206*2f89b7f7SBesar Wicaksono /* Reject groups spanning multiple HW PMUs. */ 207*2f89b7f7SBesar Wicaksono if (event->pmu != pmu) 208*2f89b7f7SBesar Wicaksono return false; 209*2f89b7f7SBesar Wicaksono 210*2f89b7f7SBesar Wicaksono return nv_c2c_pmu_get_event_idx(hw_events, event) >= 0; 211*2f89b7f7SBesar Wicaksono } 212*2f89b7f7SBesar Wicaksono 213*2f89b7f7SBesar Wicaksono /* 214*2f89b7f7SBesar Wicaksono * Make sure the group of events can be scheduled at once 215*2f89b7f7SBesar Wicaksono * on the PMU. 216*2f89b7f7SBesar Wicaksono */ 217*2f89b7f7SBesar Wicaksono static bool nv_c2c_pmu_validate_group(struct perf_event *event) 218*2f89b7f7SBesar Wicaksono { 219*2f89b7f7SBesar Wicaksono struct perf_event *sibling, *leader = event->group_leader; 220*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events fake_hw_events; 221*2f89b7f7SBesar Wicaksono 222*2f89b7f7SBesar Wicaksono if (event->group_leader == event) 223*2f89b7f7SBesar Wicaksono return true; 224*2f89b7f7SBesar Wicaksono 225*2f89b7f7SBesar Wicaksono memset(&fake_hw_events, 0, sizeof(fake_hw_events)); 226*2f89b7f7SBesar Wicaksono 227*2f89b7f7SBesar Wicaksono if (!nv_c2c_pmu_validate_event(event->pmu, &fake_hw_events, leader)) 228*2f89b7f7SBesar Wicaksono return false; 229*2f89b7f7SBesar Wicaksono 230*2f89b7f7SBesar Wicaksono for_each_sibling_event(sibling, leader) { 231*2f89b7f7SBesar Wicaksono if (!nv_c2c_pmu_validate_event(event->pmu, &fake_hw_events, 232*2f89b7f7SBesar Wicaksono sibling)) 233*2f89b7f7SBesar Wicaksono return false; 234*2f89b7f7SBesar Wicaksono } 235*2f89b7f7SBesar Wicaksono 236*2f89b7f7SBesar Wicaksono return nv_c2c_pmu_validate_event(event->pmu, &fake_hw_events, event); 237*2f89b7f7SBesar Wicaksono } 238*2f89b7f7SBesar Wicaksono 239*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_event_init(struct perf_event *event) 240*2f89b7f7SBesar Wicaksono { 241*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(event->pmu); 242*2f89b7f7SBesar Wicaksono struct hw_perf_event *hwc = &event->hw; 243*2f89b7f7SBesar Wicaksono u32 event_type = get_event_type(event); 244*2f89b7f7SBesar Wicaksono 245*2f89b7f7SBesar Wicaksono if (event->attr.type != event->pmu->type || 246*2f89b7f7SBesar Wicaksono event_type >= C2C_NUM_EVENTS) 247*2f89b7f7SBesar Wicaksono return -ENOENT; 248*2f89b7f7SBesar Wicaksono 249*2f89b7f7SBesar Wicaksono /* 250*2f89b7f7SBesar Wicaksono * Following other "uncore" PMUs, we do not support sampling mode or 251*2f89b7f7SBesar Wicaksono * attach to a task (per-process mode). 252*2f89b7f7SBesar Wicaksono */ 253*2f89b7f7SBesar Wicaksono if (is_sampling_event(event)) { 254*2f89b7f7SBesar Wicaksono dev_dbg(c2c_pmu->pmu.dev, "Can't support sampling events\n"); 255*2f89b7f7SBesar Wicaksono return -EOPNOTSUPP; 256*2f89b7f7SBesar Wicaksono } 257*2f89b7f7SBesar Wicaksono 258*2f89b7f7SBesar Wicaksono if (event->cpu < 0 || event->attach_state & PERF_ATTACH_TASK) { 259*2f89b7f7SBesar Wicaksono dev_dbg(c2c_pmu->pmu.dev, "Can't support per-task counters\n"); 260*2f89b7f7SBesar Wicaksono return -EINVAL; 261*2f89b7f7SBesar Wicaksono } 262*2f89b7f7SBesar Wicaksono 263*2f89b7f7SBesar Wicaksono /* 264*2f89b7f7SBesar Wicaksono * Make sure the CPU assignment is on one of the CPUs associated with 265*2f89b7f7SBesar Wicaksono * this PMU. 266*2f89b7f7SBesar Wicaksono */ 267*2f89b7f7SBesar Wicaksono if (!cpumask_test_cpu(event->cpu, &c2c_pmu->associated_cpus)) { 268*2f89b7f7SBesar Wicaksono dev_dbg(c2c_pmu->pmu.dev, 269*2f89b7f7SBesar Wicaksono "Requested cpu is not associated with the PMU\n"); 270*2f89b7f7SBesar Wicaksono return -EINVAL; 271*2f89b7f7SBesar Wicaksono } 272*2f89b7f7SBesar Wicaksono 273*2f89b7f7SBesar Wicaksono /* Enforce the current active CPU to handle the events in this PMU. */ 274*2f89b7f7SBesar Wicaksono event->cpu = cpumask_first(&c2c_pmu->active_cpu); 275*2f89b7f7SBesar Wicaksono if (event->cpu >= nr_cpu_ids) 276*2f89b7f7SBesar Wicaksono return -EINVAL; 277*2f89b7f7SBesar Wicaksono 278*2f89b7f7SBesar Wicaksono if (!nv_c2c_pmu_validate_group(event)) 279*2f89b7f7SBesar Wicaksono return -EINVAL; 280*2f89b7f7SBesar Wicaksono 281*2f89b7f7SBesar Wicaksono hwc->idx = -1; 282*2f89b7f7SBesar Wicaksono hwc->config = event_type; 283*2f89b7f7SBesar Wicaksono 284*2f89b7f7SBesar Wicaksono return 0; 285*2f89b7f7SBesar Wicaksono } 286*2f89b7f7SBesar Wicaksono 287*2f89b7f7SBesar Wicaksono /* 288*2f89b7f7SBesar Wicaksono * Read 64-bit register as a pair of 32-bit registers using hi-lo-hi sequence. 289*2f89b7f7SBesar Wicaksono */ 290*2f89b7f7SBesar Wicaksono static u64 read_reg64_hilohi(const void __iomem *addr, u32 max_poll_count) 291*2f89b7f7SBesar Wicaksono { 292*2f89b7f7SBesar Wicaksono u32 val_lo, val_hi; 293*2f89b7f7SBesar Wicaksono u64 val; 294*2f89b7f7SBesar Wicaksono 295*2f89b7f7SBesar Wicaksono /* Use high-low-high sequence to avoid tearing */ 296*2f89b7f7SBesar Wicaksono do { 297*2f89b7f7SBesar Wicaksono if (max_poll_count-- == 0) { 298*2f89b7f7SBesar Wicaksono pr_err("NV C2C PMU: timeout hi-low-high sequence\n"); 299*2f89b7f7SBesar Wicaksono return 0; 300*2f89b7f7SBesar Wicaksono } 301*2f89b7f7SBesar Wicaksono 302*2f89b7f7SBesar Wicaksono val_hi = readl(addr + 4); 303*2f89b7f7SBesar Wicaksono val_lo = readl(addr); 304*2f89b7f7SBesar Wicaksono } while (val_hi != readl(addr + 4)); 305*2f89b7f7SBesar Wicaksono 306*2f89b7f7SBesar Wicaksono val = (((u64)val_hi << 32) | val_lo); 307*2f89b7f7SBesar Wicaksono 308*2f89b7f7SBesar Wicaksono return val; 309*2f89b7f7SBesar Wicaksono } 310*2f89b7f7SBesar Wicaksono 311*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_check_status(struct nv_c2c_pmu *c2c_pmu, u32 instance) 312*2f89b7f7SBesar Wicaksono { 313*2f89b7f7SBesar Wicaksono u32 in_status, out_status; 314*2f89b7f7SBesar Wicaksono 315*2f89b7f7SBesar Wicaksono in_status = readl(c2c_pmu->base[instance] + C2C_IN_STATUS); 316*2f89b7f7SBesar Wicaksono out_status = readl(c2c_pmu->base[instance] + C2C_OUT_STATUS); 317*2f89b7f7SBesar Wicaksono 318*2f89b7f7SBesar Wicaksono if (in_status || out_status) 319*2f89b7f7SBesar Wicaksono dev_warn(c2c_pmu->dev, 320*2f89b7f7SBesar Wicaksono "C2C PMU overflow in: 0x%x, out: 0x%x\n", 321*2f89b7f7SBesar Wicaksono in_status, out_status); 322*2f89b7f7SBesar Wicaksono } 323*2f89b7f7SBesar Wicaksono 324*2f89b7f7SBesar Wicaksono static u32 nv_c2c_ctr_offset[C2C_NUM_EVENTS] = { 325*2f89b7f7SBesar Wicaksono [C2C_EVENT_CYCLES] = C2C_CYCLE_CNTR, 326*2f89b7f7SBesar Wicaksono [C2C_EVENT_IN_RD_CUM_OUTS] = C2C_IN_RD_CUM_OUTS_CNTR, 327*2f89b7f7SBesar Wicaksono [C2C_EVENT_IN_RD_REQ] = C2C_IN_RD_REQ_CNTR, 328*2f89b7f7SBesar Wicaksono [C2C_EVENT_IN_WR_CUM_OUTS] = C2C_IN_WR_CUM_OUTS_CNTR, 329*2f89b7f7SBesar Wicaksono [C2C_EVENT_IN_WR_REQ] = C2C_IN_WR_REQ_CNTR, 330*2f89b7f7SBesar Wicaksono [C2C_EVENT_OUT_RD_CUM_OUTS] = C2C_OUT_RD_CUM_OUTS_CNTR, 331*2f89b7f7SBesar Wicaksono [C2C_EVENT_OUT_RD_REQ] = C2C_OUT_RD_REQ_CNTR, 332*2f89b7f7SBesar Wicaksono [C2C_EVENT_OUT_WR_CUM_OUTS] = C2C_OUT_WR_CUM_OUTS_CNTR, 333*2f89b7f7SBesar Wicaksono [C2C_EVENT_OUT_WR_REQ] = C2C_OUT_WR_REQ_CNTR, 334*2f89b7f7SBesar Wicaksono }; 335*2f89b7f7SBesar Wicaksono 336*2f89b7f7SBesar Wicaksono static u64 nv_c2c_pmu_read_counter(struct perf_event *event) 337*2f89b7f7SBesar Wicaksono { 338*2f89b7f7SBesar Wicaksono u32 ctr_id, ctr_offset, filter_mask, filter_idx, inst_idx; 339*2f89b7f7SBesar Wicaksono unsigned long *inst_mask; 340*2f89b7f7SBesar Wicaksono DECLARE_BITMAP(filter_bitmap, C2C_NR_PEER_MAX); 341*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(event->pmu); 342*2f89b7f7SBesar Wicaksono u64 val = 0; 343*2f89b7f7SBesar Wicaksono 344*2f89b7f7SBesar Wicaksono filter_mask = get_filter_mask(event); 345*2f89b7f7SBesar Wicaksono bitmap_from_arr32(filter_bitmap, &filter_mask, c2c_pmu->nr_peer); 346*2f89b7f7SBesar Wicaksono 347*2f89b7f7SBesar Wicaksono ctr_id = event->hw.config; 348*2f89b7f7SBesar Wicaksono ctr_offset = nv_c2c_ctr_offset[ctr_id]; 349*2f89b7f7SBesar Wicaksono 350*2f89b7f7SBesar Wicaksono for_each_set_bit(filter_idx, filter_bitmap, c2c_pmu->nr_peer) { 351*2f89b7f7SBesar Wicaksono inst_mask = c2c_pmu->peer_insts[filter_idx]; 352*2f89b7f7SBesar Wicaksono for_each_set_bit(inst_idx, inst_mask, c2c_pmu->data->nr_inst) { 353*2f89b7f7SBesar Wicaksono nv_c2c_pmu_check_status(c2c_pmu, inst_idx); 354*2f89b7f7SBesar Wicaksono 355*2f89b7f7SBesar Wicaksono /* 356*2f89b7f7SBesar Wicaksono * Each instance share same clock and the driver always 357*2f89b7f7SBesar Wicaksono * enables all instances. So we can use the counts from 358*2f89b7f7SBesar Wicaksono * one instance for cycle counter. 359*2f89b7f7SBesar Wicaksono */ 360*2f89b7f7SBesar Wicaksono if (ctr_id == C2C_EVENT_CYCLES) 361*2f89b7f7SBesar Wicaksono return read_reg64_hilohi( 362*2f89b7f7SBesar Wicaksono c2c_pmu->base[inst_idx] + ctr_offset, 363*2f89b7f7SBesar Wicaksono HILOHI_MAX_POLL); 364*2f89b7f7SBesar Wicaksono 365*2f89b7f7SBesar Wicaksono /* 366*2f89b7f7SBesar Wicaksono * For other events, sum up the counts from all instances. 367*2f89b7f7SBesar Wicaksono */ 368*2f89b7f7SBesar Wicaksono val += read_reg64_hilohi( 369*2f89b7f7SBesar Wicaksono c2c_pmu->base[inst_idx] + ctr_offset, 370*2f89b7f7SBesar Wicaksono HILOHI_MAX_POLL); 371*2f89b7f7SBesar Wicaksono } 372*2f89b7f7SBesar Wicaksono } 373*2f89b7f7SBesar Wicaksono 374*2f89b7f7SBesar Wicaksono return val; 375*2f89b7f7SBesar Wicaksono } 376*2f89b7f7SBesar Wicaksono 377*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_event_update(struct perf_event *event) 378*2f89b7f7SBesar Wicaksono { 379*2f89b7f7SBesar Wicaksono struct hw_perf_event *hwc = &event->hw; 380*2f89b7f7SBesar Wicaksono u64 prev, now; 381*2f89b7f7SBesar Wicaksono 382*2f89b7f7SBesar Wicaksono do { 383*2f89b7f7SBesar Wicaksono prev = local64_read(&hwc->prev_count); 384*2f89b7f7SBesar Wicaksono now = nv_c2c_pmu_read_counter(event); 385*2f89b7f7SBesar Wicaksono } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev); 386*2f89b7f7SBesar Wicaksono 387*2f89b7f7SBesar Wicaksono local64_add(now - prev, &event->count); 388*2f89b7f7SBesar Wicaksono } 389*2f89b7f7SBesar Wicaksono 390*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_start(struct perf_event *event, int pmu_flags) 391*2f89b7f7SBesar Wicaksono { 392*2f89b7f7SBesar Wicaksono event->hw.state = 0; 393*2f89b7f7SBesar Wicaksono } 394*2f89b7f7SBesar Wicaksono 395*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_stop(struct perf_event *event, int pmu_flags) 396*2f89b7f7SBesar Wicaksono { 397*2f89b7f7SBesar Wicaksono event->hw.state |= PERF_HES_STOPPED; 398*2f89b7f7SBesar Wicaksono } 399*2f89b7f7SBesar Wicaksono 400*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_add(struct perf_event *event, int flags) 401*2f89b7f7SBesar Wicaksono { 402*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(event->pmu); 403*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events *hw_events = &c2c_pmu->hw_events; 404*2f89b7f7SBesar Wicaksono struct hw_perf_event *hwc = &event->hw; 405*2f89b7f7SBesar Wicaksono int idx; 406*2f89b7f7SBesar Wicaksono 407*2f89b7f7SBesar Wicaksono if (WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), 408*2f89b7f7SBesar Wicaksono &c2c_pmu->associated_cpus))) 409*2f89b7f7SBesar Wicaksono return -ENOENT; 410*2f89b7f7SBesar Wicaksono 411*2f89b7f7SBesar Wicaksono idx = nv_c2c_pmu_get_event_idx(hw_events, event); 412*2f89b7f7SBesar Wicaksono if (idx < 0) 413*2f89b7f7SBesar Wicaksono return idx; 414*2f89b7f7SBesar Wicaksono 415*2f89b7f7SBesar Wicaksono hw_events->events[idx] = event; 416*2f89b7f7SBesar Wicaksono hwc->idx = idx; 417*2f89b7f7SBesar Wicaksono hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 418*2f89b7f7SBesar Wicaksono 419*2f89b7f7SBesar Wicaksono if (flags & PERF_EF_START) 420*2f89b7f7SBesar Wicaksono nv_c2c_pmu_start(event, PERF_EF_RELOAD); 421*2f89b7f7SBesar Wicaksono 422*2f89b7f7SBesar Wicaksono /* Propagate changes to the userspace mapping. */ 423*2f89b7f7SBesar Wicaksono perf_event_update_userpage(event); 424*2f89b7f7SBesar Wicaksono 425*2f89b7f7SBesar Wicaksono return 0; 426*2f89b7f7SBesar Wicaksono } 427*2f89b7f7SBesar Wicaksono 428*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_del(struct perf_event *event, int flags) 429*2f89b7f7SBesar Wicaksono { 430*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(event->pmu); 431*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu_hw_events *hw_events = &c2c_pmu->hw_events; 432*2f89b7f7SBesar Wicaksono struct hw_perf_event *hwc = &event->hw; 433*2f89b7f7SBesar Wicaksono int idx = hwc->idx; 434*2f89b7f7SBesar Wicaksono 435*2f89b7f7SBesar Wicaksono nv_c2c_pmu_stop(event, PERF_EF_UPDATE); 436*2f89b7f7SBesar Wicaksono 437*2f89b7f7SBesar Wicaksono hw_events->events[idx] = NULL; 438*2f89b7f7SBesar Wicaksono 439*2f89b7f7SBesar Wicaksono clear_bit(idx, hw_events->used_ctrs); 440*2f89b7f7SBesar Wicaksono 441*2f89b7f7SBesar Wicaksono perf_event_update_userpage(event); 442*2f89b7f7SBesar Wicaksono } 443*2f89b7f7SBesar Wicaksono 444*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_read(struct perf_event *event) 445*2f89b7f7SBesar Wicaksono { 446*2f89b7f7SBesar Wicaksono nv_c2c_pmu_event_update(event); 447*2f89b7f7SBesar Wicaksono } 448*2f89b7f7SBesar Wicaksono 449*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_enable(struct pmu *pmu) 450*2f89b7f7SBesar Wicaksono { 451*2f89b7f7SBesar Wicaksono void __iomem *bcast; 452*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(pmu); 453*2f89b7f7SBesar Wicaksono 454*2f89b7f7SBesar Wicaksono /* Check if any filter is enabled. */ 455*2f89b7f7SBesar Wicaksono if (bitmap_empty(c2c_pmu->hw_events.used_ctrs, C2C_MAX_ACTIVE_EVENTS)) 456*2f89b7f7SBesar Wicaksono return; 457*2f89b7f7SBesar Wicaksono 458*2f89b7f7SBesar Wicaksono /* Enable all the counters. */ 459*2f89b7f7SBesar Wicaksono bcast = c2c_pmu->base_broadcast; 460*2f89b7f7SBesar Wicaksono writel(0x1UL, bcast + C2C_CTRL); 461*2f89b7f7SBesar Wicaksono } 462*2f89b7f7SBesar Wicaksono 463*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_disable(struct pmu *pmu) 464*2f89b7f7SBesar Wicaksono { 465*2f89b7f7SBesar Wicaksono unsigned int idx; 466*2f89b7f7SBesar Wicaksono void __iomem *bcast; 467*2f89b7f7SBesar Wicaksono struct perf_event *event; 468*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(pmu); 469*2f89b7f7SBesar Wicaksono 470*2f89b7f7SBesar Wicaksono /* Disable all the counters. */ 471*2f89b7f7SBesar Wicaksono bcast = c2c_pmu->base_broadcast; 472*2f89b7f7SBesar Wicaksono writel(0x0UL, bcast + C2C_CTRL); 473*2f89b7f7SBesar Wicaksono 474*2f89b7f7SBesar Wicaksono /* 475*2f89b7f7SBesar Wicaksono * The counters will start from 0 again on restart. 476*2f89b7f7SBesar Wicaksono * Update the events immediately to avoid losing the counts. 477*2f89b7f7SBesar Wicaksono */ 478*2f89b7f7SBesar Wicaksono for_each_set_bit(idx, c2c_pmu->hw_events.used_ctrs, 479*2f89b7f7SBesar Wicaksono C2C_MAX_ACTIVE_EVENTS) { 480*2f89b7f7SBesar Wicaksono event = c2c_pmu->hw_events.events[idx]; 481*2f89b7f7SBesar Wicaksono 482*2f89b7f7SBesar Wicaksono if (!event) 483*2f89b7f7SBesar Wicaksono continue; 484*2f89b7f7SBesar Wicaksono 485*2f89b7f7SBesar Wicaksono nv_c2c_pmu_event_update(event); 486*2f89b7f7SBesar Wicaksono 487*2f89b7f7SBesar Wicaksono local64_set(&event->hw.prev_count, 0ULL); 488*2f89b7f7SBesar Wicaksono } 489*2f89b7f7SBesar Wicaksono } 490*2f89b7f7SBesar Wicaksono 491*2f89b7f7SBesar Wicaksono /* PMU identifier attribute. */ 492*2f89b7f7SBesar Wicaksono 493*2f89b7f7SBesar Wicaksono static ssize_t nv_c2c_pmu_identifier_show(struct device *dev, 494*2f89b7f7SBesar Wicaksono struct device_attribute *attr, 495*2f89b7f7SBesar Wicaksono char *page) 496*2f89b7f7SBesar Wicaksono { 497*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(dev_get_drvdata(dev)); 498*2f89b7f7SBesar Wicaksono 499*2f89b7f7SBesar Wicaksono return sysfs_emit(page, "%s\n", c2c_pmu->identifier); 500*2f89b7f7SBesar Wicaksono } 501*2f89b7f7SBesar Wicaksono 502*2f89b7f7SBesar Wicaksono static struct device_attribute nv_c2c_pmu_identifier_attr = 503*2f89b7f7SBesar Wicaksono __ATTR(identifier, 0444, nv_c2c_pmu_identifier_show, NULL); 504*2f89b7f7SBesar Wicaksono 505*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_identifier_attrs[] = { 506*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_identifier_attr.attr, 507*2f89b7f7SBesar Wicaksono NULL, 508*2f89b7f7SBesar Wicaksono }; 509*2f89b7f7SBesar Wicaksono 510*2f89b7f7SBesar Wicaksono static struct attribute_group nv_c2c_pmu_identifier_attr_group = { 511*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_identifier_attrs, 512*2f89b7f7SBesar Wicaksono }; 513*2f89b7f7SBesar Wicaksono 514*2f89b7f7SBesar Wicaksono /* Peer attribute. */ 515*2f89b7f7SBesar Wicaksono 516*2f89b7f7SBesar Wicaksono static ssize_t nv_c2c_pmu_peer_show(struct device *dev, 517*2f89b7f7SBesar Wicaksono struct device_attribute *attr, 518*2f89b7f7SBesar Wicaksono char *page) 519*2f89b7f7SBesar Wicaksono { 520*2f89b7f7SBesar Wicaksono const char *peer_type[C2C_PEER_TYPE_COUNT] = { 521*2f89b7f7SBesar Wicaksono [C2C_PEER_TYPE_CPU] = "cpu", 522*2f89b7f7SBesar Wicaksono [C2C_PEER_TYPE_GPU] = "gpu", 523*2f89b7f7SBesar Wicaksono [C2C_PEER_TYPE_CXLMEM] = "cxlmem", 524*2f89b7f7SBesar Wicaksono }; 525*2f89b7f7SBesar Wicaksono 526*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(dev_get_drvdata(dev)); 527*2f89b7f7SBesar Wicaksono return sysfs_emit(page, "nr_%s=%u\n", peer_type[c2c_pmu->peer_type], 528*2f89b7f7SBesar Wicaksono c2c_pmu->nr_peer); 529*2f89b7f7SBesar Wicaksono } 530*2f89b7f7SBesar Wicaksono 531*2f89b7f7SBesar Wicaksono static struct device_attribute nv_c2c_pmu_peer_attr = 532*2f89b7f7SBesar Wicaksono __ATTR(peer, 0444, nv_c2c_pmu_peer_show, NULL); 533*2f89b7f7SBesar Wicaksono 534*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_peer_attrs[] = { 535*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_peer_attr.attr, 536*2f89b7f7SBesar Wicaksono NULL, 537*2f89b7f7SBesar Wicaksono }; 538*2f89b7f7SBesar Wicaksono 539*2f89b7f7SBesar Wicaksono static struct attribute_group nv_c2c_pmu_peer_attr_group = { 540*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_peer_attrs, 541*2f89b7f7SBesar Wicaksono }; 542*2f89b7f7SBesar Wicaksono 543*2f89b7f7SBesar Wicaksono /* Format attributes. */ 544*2f89b7f7SBesar Wicaksono 545*2f89b7f7SBesar Wicaksono #define NV_C2C_PMU_EXT_ATTR(_name, _func, _config) \ 546*2f89b7f7SBesar Wicaksono (&((struct dev_ext_attribute[]){ \ 547*2f89b7f7SBesar Wicaksono { \ 548*2f89b7f7SBesar Wicaksono .attr = __ATTR(_name, 0444, _func, NULL), \ 549*2f89b7f7SBesar Wicaksono .var = (void *)_config \ 550*2f89b7f7SBesar Wicaksono } \ 551*2f89b7f7SBesar Wicaksono })[0].attr.attr) 552*2f89b7f7SBesar Wicaksono 553*2f89b7f7SBesar Wicaksono #define NV_C2C_PMU_FORMAT_ATTR(_name, _config) \ 554*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EXT_ATTR(_name, device_show_string, _config) 555*2f89b7f7SBesar Wicaksono 556*2f89b7f7SBesar Wicaksono #define NV_C2C_PMU_FORMAT_EVENT_ATTR \ 557*2f89b7f7SBesar Wicaksono NV_C2C_PMU_FORMAT_ATTR(event, "config:0-3") 558*2f89b7f7SBesar Wicaksono 559*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_gpu_formats[] = { 560*2f89b7f7SBesar Wicaksono NV_C2C_PMU_FORMAT_EVENT_ATTR, 561*2f89b7f7SBesar Wicaksono NV_C2C_PMU_FORMAT_ATTR(gpu_mask, "config1:0-1"), 562*2f89b7f7SBesar Wicaksono NULL, 563*2f89b7f7SBesar Wicaksono }; 564*2f89b7f7SBesar Wicaksono 565*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_gpu_format_group = { 566*2f89b7f7SBesar Wicaksono .name = "format", 567*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_gpu_formats, 568*2f89b7f7SBesar Wicaksono }; 569*2f89b7f7SBesar Wicaksono 570*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_formats[] = { 571*2f89b7f7SBesar Wicaksono NV_C2C_PMU_FORMAT_EVENT_ATTR, 572*2f89b7f7SBesar Wicaksono NULL, 573*2f89b7f7SBesar Wicaksono }; 574*2f89b7f7SBesar Wicaksono 575*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_format_group = { 576*2f89b7f7SBesar Wicaksono .name = "format", 577*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_formats, 578*2f89b7f7SBesar Wicaksono }; 579*2f89b7f7SBesar Wicaksono 580*2f89b7f7SBesar Wicaksono /* Event attributes. */ 581*2f89b7f7SBesar Wicaksono 582*2f89b7f7SBesar Wicaksono static ssize_t nv_c2c_pmu_sysfs_event_show(struct device *dev, 583*2f89b7f7SBesar Wicaksono struct device_attribute *attr, 584*2f89b7f7SBesar Wicaksono char *buf) 585*2f89b7f7SBesar Wicaksono { 586*2f89b7f7SBesar Wicaksono struct perf_pmu_events_attr *pmu_attr; 587*2f89b7f7SBesar Wicaksono 588*2f89b7f7SBesar Wicaksono pmu_attr = container_of(attr, typeof(*pmu_attr), attr); 589*2f89b7f7SBesar Wicaksono return sysfs_emit(buf, "event=0x%llx\n", pmu_attr->id); 590*2f89b7f7SBesar Wicaksono } 591*2f89b7f7SBesar Wicaksono 592*2f89b7f7SBesar Wicaksono #define NV_C2C_PMU_EVENT_ATTR(_name, _config) \ 593*2f89b7f7SBesar Wicaksono PMU_EVENT_ATTR_ID(_name, nv_c2c_pmu_sysfs_event_show, _config) 594*2f89b7f7SBesar Wicaksono 595*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_gpu_events[] = { 596*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(cycles, C2C_EVENT_CYCLES), 597*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_cum_outs, C2C_EVENT_IN_RD_CUM_OUTS), 598*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_req, C2C_EVENT_IN_RD_REQ), 599*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_wr_cum_outs, C2C_EVENT_IN_WR_CUM_OUTS), 600*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_wr_req, C2C_EVENT_IN_WR_REQ), 601*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_rd_cum_outs, C2C_EVENT_OUT_RD_CUM_OUTS), 602*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_rd_req, C2C_EVENT_OUT_RD_REQ), 603*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_wr_cum_outs, C2C_EVENT_OUT_WR_CUM_OUTS), 604*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_wr_req, C2C_EVENT_OUT_WR_REQ), 605*2f89b7f7SBesar Wicaksono NULL 606*2f89b7f7SBesar Wicaksono }; 607*2f89b7f7SBesar Wicaksono 608*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_gpu_events_group = { 609*2f89b7f7SBesar Wicaksono .name = "events", 610*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_gpu_events, 611*2f89b7f7SBesar Wicaksono }; 612*2f89b7f7SBesar Wicaksono 613*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_cpu_events[] = { 614*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(cycles, C2C_EVENT_CYCLES), 615*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_cum_outs, C2C_EVENT_IN_RD_CUM_OUTS), 616*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_req, C2C_EVENT_IN_RD_REQ), 617*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_rd_cum_outs, C2C_EVENT_OUT_RD_CUM_OUTS), 618*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(out_rd_req, C2C_EVENT_OUT_RD_REQ), 619*2f89b7f7SBesar Wicaksono NULL 620*2f89b7f7SBesar Wicaksono }; 621*2f89b7f7SBesar Wicaksono 622*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_cpu_events_group = { 623*2f89b7f7SBesar Wicaksono .name = "events", 624*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_cpu_events, 625*2f89b7f7SBesar Wicaksono }; 626*2f89b7f7SBesar Wicaksono 627*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_cxlmem_events[] = { 628*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(cycles, C2C_EVENT_CYCLES), 629*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_cum_outs, C2C_EVENT_IN_RD_CUM_OUTS), 630*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EVENT_ATTR(in_rd_req, C2C_EVENT_IN_RD_REQ), 631*2f89b7f7SBesar Wicaksono NULL 632*2f89b7f7SBesar Wicaksono }; 633*2f89b7f7SBesar Wicaksono 634*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_cxlmem_events_group = { 635*2f89b7f7SBesar Wicaksono .name = "events", 636*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_cxlmem_events, 637*2f89b7f7SBesar Wicaksono }; 638*2f89b7f7SBesar Wicaksono 639*2f89b7f7SBesar Wicaksono /* Cpumask attributes. */ 640*2f89b7f7SBesar Wicaksono 641*2f89b7f7SBesar Wicaksono static ssize_t nv_c2c_pmu_cpumask_show(struct device *dev, 642*2f89b7f7SBesar Wicaksono struct device_attribute *attr, char *buf) 643*2f89b7f7SBesar Wicaksono { 644*2f89b7f7SBesar Wicaksono struct pmu *pmu = dev_get_drvdata(dev); 645*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = to_c2c_pmu(pmu); 646*2f89b7f7SBesar Wicaksono struct dev_ext_attribute *eattr = 647*2f89b7f7SBesar Wicaksono container_of(attr, struct dev_ext_attribute, attr); 648*2f89b7f7SBesar Wicaksono unsigned long mask_id = (unsigned long)eattr->var; 649*2f89b7f7SBesar Wicaksono const cpumask_t *cpumask; 650*2f89b7f7SBesar Wicaksono 651*2f89b7f7SBesar Wicaksono switch (mask_id) { 652*2f89b7f7SBesar Wicaksono case C2C_ACTIVE_CPU_MASK: 653*2f89b7f7SBesar Wicaksono cpumask = &c2c_pmu->active_cpu; 654*2f89b7f7SBesar Wicaksono break; 655*2f89b7f7SBesar Wicaksono case C2C_ASSOCIATED_CPU_MASK: 656*2f89b7f7SBesar Wicaksono cpumask = &c2c_pmu->associated_cpus; 657*2f89b7f7SBesar Wicaksono break; 658*2f89b7f7SBesar Wicaksono default: 659*2f89b7f7SBesar Wicaksono return 0; 660*2f89b7f7SBesar Wicaksono } 661*2f89b7f7SBesar Wicaksono return cpumap_print_to_pagebuf(true, buf, cpumask); 662*2f89b7f7SBesar Wicaksono } 663*2f89b7f7SBesar Wicaksono 664*2f89b7f7SBesar Wicaksono #define NV_C2C_PMU_CPUMASK_ATTR(_name, _config) \ 665*2f89b7f7SBesar Wicaksono NV_C2C_PMU_EXT_ATTR(_name, nv_c2c_pmu_cpumask_show, \ 666*2f89b7f7SBesar Wicaksono (unsigned long)_config) 667*2f89b7f7SBesar Wicaksono 668*2f89b7f7SBesar Wicaksono static struct attribute *nv_c2c_pmu_cpumask_attrs[] = { 669*2f89b7f7SBesar Wicaksono NV_C2C_PMU_CPUMASK_ATTR(cpumask, C2C_ACTIVE_CPU_MASK), 670*2f89b7f7SBesar Wicaksono NV_C2C_PMU_CPUMASK_ATTR(associated_cpus, C2C_ASSOCIATED_CPU_MASK), 671*2f89b7f7SBesar Wicaksono NULL, 672*2f89b7f7SBesar Wicaksono }; 673*2f89b7f7SBesar Wicaksono 674*2f89b7f7SBesar Wicaksono static const struct attribute_group nv_c2c_pmu_cpumask_attr_group = { 675*2f89b7f7SBesar Wicaksono .attrs = nv_c2c_pmu_cpumask_attrs, 676*2f89b7f7SBesar Wicaksono }; 677*2f89b7f7SBesar Wicaksono 678*2f89b7f7SBesar Wicaksono /* Attribute groups for C2C PMU connecting SoC and GPU */ 679*2f89b7f7SBesar Wicaksono static const struct attribute_group *nv_c2c_pmu_gpu_attr_groups[] = { 680*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_gpu_format_group, 681*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_gpu_events_group, 682*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_cpumask_attr_group, 683*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_identifier_attr_group, 684*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_peer_attr_group, 685*2f89b7f7SBesar Wicaksono NULL 686*2f89b7f7SBesar Wicaksono }; 687*2f89b7f7SBesar Wicaksono 688*2f89b7f7SBesar Wicaksono /* Attribute groups for C2C PMU connecting multiple SoCs */ 689*2f89b7f7SBesar Wicaksono static const struct attribute_group *nv_c2c_pmu_cpu_attr_groups[] = { 690*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_format_group, 691*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_cpu_events_group, 692*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_cpumask_attr_group, 693*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_identifier_attr_group, 694*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_peer_attr_group, 695*2f89b7f7SBesar Wicaksono NULL 696*2f89b7f7SBesar Wicaksono }; 697*2f89b7f7SBesar Wicaksono 698*2f89b7f7SBesar Wicaksono /* Attribute groups for C2C PMU connecting SoC and CXLMEM */ 699*2f89b7f7SBesar Wicaksono static const struct attribute_group *nv_c2c_pmu_cxlmem_attr_groups[] = { 700*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_format_group, 701*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_cxlmem_events_group, 702*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_cpumask_attr_group, 703*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_identifier_attr_group, 704*2f89b7f7SBesar Wicaksono &nv_c2c_pmu_peer_attr_group, 705*2f89b7f7SBesar Wicaksono NULL 706*2f89b7f7SBesar Wicaksono }; 707*2f89b7f7SBesar Wicaksono 708*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) 709*2f89b7f7SBesar Wicaksono { 710*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = 711*2f89b7f7SBesar Wicaksono hlist_entry_safe(node, struct nv_c2c_pmu, cpuhp_node); 712*2f89b7f7SBesar Wicaksono 713*2f89b7f7SBesar Wicaksono if (!cpumask_test_cpu(cpu, &c2c_pmu->associated_cpus)) 714*2f89b7f7SBesar Wicaksono return 0; 715*2f89b7f7SBesar Wicaksono 716*2f89b7f7SBesar Wicaksono /* If the PMU is already managed, there is nothing to do */ 717*2f89b7f7SBesar Wicaksono if (!cpumask_empty(&c2c_pmu->active_cpu)) 718*2f89b7f7SBesar Wicaksono return 0; 719*2f89b7f7SBesar Wicaksono 720*2f89b7f7SBesar Wicaksono /* Use this CPU for event counting */ 721*2f89b7f7SBesar Wicaksono cpumask_set_cpu(cpu, &c2c_pmu->active_cpu); 722*2f89b7f7SBesar Wicaksono 723*2f89b7f7SBesar Wicaksono return 0; 724*2f89b7f7SBesar Wicaksono } 725*2f89b7f7SBesar Wicaksono 726*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_cpu_teardown(unsigned int cpu, struct hlist_node *node) 727*2f89b7f7SBesar Wicaksono { 728*2f89b7f7SBesar Wicaksono unsigned int dst; 729*2f89b7f7SBesar Wicaksono 730*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = 731*2f89b7f7SBesar Wicaksono hlist_entry_safe(node, struct nv_c2c_pmu, cpuhp_node); 732*2f89b7f7SBesar Wicaksono 733*2f89b7f7SBesar Wicaksono /* Nothing to do if this CPU doesn't own the PMU */ 734*2f89b7f7SBesar Wicaksono if (!cpumask_test_and_clear_cpu(cpu, &c2c_pmu->active_cpu)) 735*2f89b7f7SBesar Wicaksono return 0; 736*2f89b7f7SBesar Wicaksono 737*2f89b7f7SBesar Wicaksono /* Choose a new CPU to migrate ownership of the PMU to */ 738*2f89b7f7SBesar Wicaksono dst = cpumask_any_and_but(&c2c_pmu->associated_cpus, 739*2f89b7f7SBesar Wicaksono cpu_online_mask, cpu); 740*2f89b7f7SBesar Wicaksono if (dst >= nr_cpu_ids) 741*2f89b7f7SBesar Wicaksono return 0; 742*2f89b7f7SBesar Wicaksono 743*2f89b7f7SBesar Wicaksono /* Use this CPU for event counting */ 744*2f89b7f7SBesar Wicaksono perf_pmu_migrate_context(&c2c_pmu->pmu, cpu, dst); 745*2f89b7f7SBesar Wicaksono cpumask_set_cpu(dst, &c2c_pmu->active_cpu); 746*2f89b7f7SBesar Wicaksono 747*2f89b7f7SBesar Wicaksono return 0; 748*2f89b7f7SBesar Wicaksono } 749*2f89b7f7SBesar Wicaksono 750*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_get_cpus(struct nv_c2c_pmu *c2c_pmu) 751*2f89b7f7SBesar Wicaksono { 752*2f89b7f7SBesar Wicaksono int socket = c2c_pmu->socket, cpu; 753*2f89b7f7SBesar Wicaksono 754*2f89b7f7SBesar Wicaksono for_each_possible_cpu(cpu) { 755*2f89b7f7SBesar Wicaksono if (cpu_to_node(cpu) == socket) 756*2f89b7f7SBesar Wicaksono cpumask_set_cpu(cpu, &c2c_pmu->associated_cpus); 757*2f89b7f7SBesar Wicaksono } 758*2f89b7f7SBesar Wicaksono 759*2f89b7f7SBesar Wicaksono if (cpumask_empty(&c2c_pmu->associated_cpus)) { 760*2f89b7f7SBesar Wicaksono dev_dbg(c2c_pmu->dev, 761*2f89b7f7SBesar Wicaksono "No cpu associated with C2C PMU socket-%u\n", socket); 762*2f89b7f7SBesar Wicaksono return -ENODEV; 763*2f89b7f7SBesar Wicaksono } 764*2f89b7f7SBesar Wicaksono 765*2f89b7f7SBesar Wicaksono return 0; 766*2f89b7f7SBesar Wicaksono } 767*2f89b7f7SBesar Wicaksono 768*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_init_socket(struct nv_c2c_pmu *c2c_pmu) 769*2f89b7f7SBesar Wicaksono { 770*2f89b7f7SBesar Wicaksono const char *uid_str; 771*2f89b7f7SBesar Wicaksono int ret, socket; 772*2f89b7f7SBesar Wicaksono 773*2f89b7f7SBesar Wicaksono uid_str = acpi_device_uid(c2c_pmu->acpi_dev); 774*2f89b7f7SBesar Wicaksono if (!uid_str) { 775*2f89b7f7SBesar Wicaksono dev_err(c2c_pmu->dev, "No ACPI device UID\n"); 776*2f89b7f7SBesar Wicaksono return -ENODEV; 777*2f89b7f7SBesar Wicaksono } 778*2f89b7f7SBesar Wicaksono 779*2f89b7f7SBesar Wicaksono ret = kstrtou32(uid_str, 0, &socket); 780*2f89b7f7SBesar Wicaksono if (ret) { 781*2f89b7f7SBesar Wicaksono dev_err(c2c_pmu->dev, "Failed to parse ACPI device UID\n"); 782*2f89b7f7SBesar Wicaksono return ret; 783*2f89b7f7SBesar Wicaksono } 784*2f89b7f7SBesar Wicaksono 785*2f89b7f7SBesar Wicaksono c2c_pmu->socket = socket; 786*2f89b7f7SBesar Wicaksono return 0; 787*2f89b7f7SBesar Wicaksono } 788*2f89b7f7SBesar Wicaksono 789*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_init_id(struct nv_c2c_pmu *c2c_pmu) 790*2f89b7f7SBesar Wicaksono { 791*2f89b7f7SBesar Wicaksono char *name; 792*2f89b7f7SBesar Wicaksono 793*2f89b7f7SBesar Wicaksono name = devm_kasprintf(c2c_pmu->dev, GFP_KERNEL, c2c_pmu->data->name_fmt, 794*2f89b7f7SBesar Wicaksono c2c_pmu->socket); 795*2f89b7f7SBesar Wicaksono if (!name) 796*2f89b7f7SBesar Wicaksono return -ENOMEM; 797*2f89b7f7SBesar Wicaksono 798*2f89b7f7SBesar Wicaksono c2c_pmu->name = name; 799*2f89b7f7SBesar Wicaksono 800*2f89b7f7SBesar Wicaksono c2c_pmu->identifier = acpi_device_hid(c2c_pmu->acpi_dev); 801*2f89b7f7SBesar Wicaksono 802*2f89b7f7SBesar Wicaksono return 0; 803*2f89b7f7SBesar Wicaksono } 804*2f89b7f7SBesar Wicaksono 805*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_init_filter(struct nv_c2c_pmu *c2c_pmu) 806*2f89b7f7SBesar Wicaksono { 807*2f89b7f7SBesar Wicaksono u32 cpu_en = 0; 808*2f89b7f7SBesar Wicaksono struct device *dev = c2c_pmu->dev; 809*2f89b7f7SBesar Wicaksono const struct nv_c2c_pmu_data *data = c2c_pmu->data; 810*2f89b7f7SBesar Wicaksono 811*2f89b7f7SBesar Wicaksono if (data->c2c_type == C2C_TYPE_NVDLINK) { 812*2f89b7f7SBesar Wicaksono c2c_pmu->peer_type = C2C_PEER_TYPE_CXLMEM; 813*2f89b7f7SBesar Wicaksono 814*2f89b7f7SBesar Wicaksono c2c_pmu->peer_insts[0][0] = (1UL << data->nr_inst) - 1; 815*2f89b7f7SBesar Wicaksono 816*2f89b7f7SBesar Wicaksono c2c_pmu->nr_peer = C2C_NR_PEER_CXLMEM; 817*2f89b7f7SBesar Wicaksono c2c_pmu->filter_default = (1 << c2c_pmu->nr_peer) - 1; 818*2f89b7f7SBesar Wicaksono 819*2f89b7f7SBesar Wicaksono c2c_pmu->attr_groups = nv_c2c_pmu_cxlmem_attr_groups; 820*2f89b7f7SBesar Wicaksono 821*2f89b7f7SBesar Wicaksono return 0; 822*2f89b7f7SBesar Wicaksono } 823*2f89b7f7SBesar Wicaksono 824*2f89b7f7SBesar Wicaksono if (device_property_read_u32(dev, "cpu_en_mask", &cpu_en)) 825*2f89b7f7SBesar Wicaksono dev_dbg(dev, "no cpu_en_mask property\n"); 826*2f89b7f7SBesar Wicaksono 827*2f89b7f7SBesar Wicaksono if (cpu_en) { 828*2f89b7f7SBesar Wicaksono c2c_pmu->peer_type = C2C_PEER_TYPE_CPU; 829*2f89b7f7SBesar Wicaksono 830*2f89b7f7SBesar Wicaksono /* Fill peer_insts bitmap with instances connected to peer CPU. */ 831*2f89b7f7SBesar Wicaksono bitmap_from_arr32(c2c_pmu->peer_insts[0], &cpu_en, data->nr_inst); 832*2f89b7f7SBesar Wicaksono 833*2f89b7f7SBesar Wicaksono c2c_pmu->nr_peer = 1; 834*2f89b7f7SBesar Wicaksono c2c_pmu->attr_groups = nv_c2c_pmu_cpu_attr_groups; 835*2f89b7f7SBesar Wicaksono } else { 836*2f89b7f7SBesar Wicaksono u32 i; 837*2f89b7f7SBesar Wicaksono const char *props[C2C_NR_PEER_MAX] = { 838*2f89b7f7SBesar Wicaksono "gpu0_en_mask", "gpu1_en_mask" 839*2f89b7f7SBesar Wicaksono }; 840*2f89b7f7SBesar Wicaksono 841*2f89b7f7SBesar Wicaksono for (i = 0; i < C2C_NR_PEER_MAX; i++) { 842*2f89b7f7SBesar Wicaksono u32 gpu_en = 0; 843*2f89b7f7SBesar Wicaksono 844*2f89b7f7SBesar Wicaksono if (device_property_read_u32(dev, props[i], &gpu_en)) 845*2f89b7f7SBesar Wicaksono dev_dbg(dev, "no %s property\n", props[i]); 846*2f89b7f7SBesar Wicaksono 847*2f89b7f7SBesar Wicaksono if (gpu_en) { 848*2f89b7f7SBesar Wicaksono /* Fill peer_insts bitmap with instances connected to peer GPU. */ 849*2f89b7f7SBesar Wicaksono bitmap_from_arr32(c2c_pmu->peer_insts[i], &gpu_en, 850*2f89b7f7SBesar Wicaksono data->nr_inst); 851*2f89b7f7SBesar Wicaksono 852*2f89b7f7SBesar Wicaksono c2c_pmu->nr_peer++; 853*2f89b7f7SBesar Wicaksono } 854*2f89b7f7SBesar Wicaksono } 855*2f89b7f7SBesar Wicaksono 856*2f89b7f7SBesar Wicaksono if (c2c_pmu->nr_peer == 0) { 857*2f89b7f7SBesar Wicaksono dev_err(dev, "No GPU is enabled\n"); 858*2f89b7f7SBesar Wicaksono return -EINVAL; 859*2f89b7f7SBesar Wicaksono } 860*2f89b7f7SBesar Wicaksono 861*2f89b7f7SBesar Wicaksono c2c_pmu->peer_type = C2C_PEER_TYPE_GPU; 862*2f89b7f7SBesar Wicaksono c2c_pmu->attr_groups = nv_c2c_pmu_gpu_attr_groups; 863*2f89b7f7SBesar Wicaksono } 864*2f89b7f7SBesar Wicaksono 865*2f89b7f7SBesar Wicaksono c2c_pmu->filter_default = (1 << c2c_pmu->nr_peer) - 1; 866*2f89b7f7SBesar Wicaksono 867*2f89b7f7SBesar Wicaksono return 0; 868*2f89b7f7SBesar Wicaksono } 869*2f89b7f7SBesar Wicaksono 870*2f89b7f7SBesar Wicaksono static void *nv_c2c_pmu_init_pmu(struct platform_device *pdev) 871*2f89b7f7SBesar Wicaksono { 872*2f89b7f7SBesar Wicaksono int ret; 873*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu; 874*2f89b7f7SBesar Wicaksono struct acpi_device *acpi_dev; 875*2f89b7f7SBesar Wicaksono struct device *dev = &pdev->dev; 876*2f89b7f7SBesar Wicaksono 877*2f89b7f7SBesar Wicaksono acpi_dev = ACPI_COMPANION(dev); 878*2f89b7f7SBesar Wicaksono if (!acpi_dev) 879*2f89b7f7SBesar Wicaksono return ERR_PTR(-ENODEV); 880*2f89b7f7SBesar Wicaksono 881*2f89b7f7SBesar Wicaksono c2c_pmu = devm_kzalloc(dev, sizeof(*c2c_pmu), GFP_KERNEL); 882*2f89b7f7SBesar Wicaksono if (!c2c_pmu) 883*2f89b7f7SBesar Wicaksono return ERR_PTR(-ENOMEM); 884*2f89b7f7SBesar Wicaksono 885*2f89b7f7SBesar Wicaksono c2c_pmu->dev = dev; 886*2f89b7f7SBesar Wicaksono c2c_pmu->acpi_dev = acpi_dev; 887*2f89b7f7SBesar Wicaksono c2c_pmu->data = (const struct nv_c2c_pmu_data *)device_get_match_data(dev); 888*2f89b7f7SBesar Wicaksono if (!c2c_pmu->data) 889*2f89b7f7SBesar Wicaksono return ERR_PTR(-EINVAL); 890*2f89b7f7SBesar Wicaksono 891*2f89b7f7SBesar Wicaksono platform_set_drvdata(pdev, c2c_pmu); 892*2f89b7f7SBesar Wicaksono 893*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_init_socket(c2c_pmu); 894*2f89b7f7SBesar Wicaksono if (ret) 895*2f89b7f7SBesar Wicaksono return ERR_PTR(ret); 896*2f89b7f7SBesar Wicaksono 897*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_init_id(c2c_pmu); 898*2f89b7f7SBesar Wicaksono if (ret) 899*2f89b7f7SBesar Wicaksono return ERR_PTR(ret); 900*2f89b7f7SBesar Wicaksono 901*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_init_filter(c2c_pmu); 902*2f89b7f7SBesar Wicaksono if (ret) 903*2f89b7f7SBesar Wicaksono return ERR_PTR(ret); 904*2f89b7f7SBesar Wicaksono 905*2f89b7f7SBesar Wicaksono return c2c_pmu; 906*2f89b7f7SBesar Wicaksono } 907*2f89b7f7SBesar Wicaksono 908*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_init_mmio(struct nv_c2c_pmu *c2c_pmu) 909*2f89b7f7SBesar Wicaksono { 910*2f89b7f7SBesar Wicaksono int i; 911*2f89b7f7SBesar Wicaksono struct device *dev = c2c_pmu->dev; 912*2f89b7f7SBesar Wicaksono struct platform_device *pdev = to_platform_device(dev); 913*2f89b7f7SBesar Wicaksono const struct nv_c2c_pmu_data *data = c2c_pmu->data; 914*2f89b7f7SBesar Wicaksono 915*2f89b7f7SBesar Wicaksono /* Map the address of all the instances. */ 916*2f89b7f7SBesar Wicaksono for (i = 0; i < data->nr_inst; i++) { 917*2f89b7f7SBesar Wicaksono c2c_pmu->base[i] = devm_platform_ioremap_resource(pdev, i); 918*2f89b7f7SBesar Wicaksono if (IS_ERR(c2c_pmu->base[i])) { 919*2f89b7f7SBesar Wicaksono dev_err(dev, "Failed map address for instance %d\n", i); 920*2f89b7f7SBesar Wicaksono return PTR_ERR(c2c_pmu->base[i]); 921*2f89b7f7SBesar Wicaksono } 922*2f89b7f7SBesar Wicaksono } 923*2f89b7f7SBesar Wicaksono 924*2f89b7f7SBesar Wicaksono /* Map broadcast address. */ 925*2f89b7f7SBesar Wicaksono c2c_pmu->base_broadcast = devm_platform_ioremap_resource(pdev, 926*2f89b7f7SBesar Wicaksono data->nr_inst); 927*2f89b7f7SBesar Wicaksono if (IS_ERR(c2c_pmu->base_broadcast)) { 928*2f89b7f7SBesar Wicaksono dev_err(dev, "Failed map broadcast address\n"); 929*2f89b7f7SBesar Wicaksono return PTR_ERR(c2c_pmu->base_broadcast); 930*2f89b7f7SBesar Wicaksono } 931*2f89b7f7SBesar Wicaksono 932*2f89b7f7SBesar Wicaksono return 0; 933*2f89b7f7SBesar Wicaksono } 934*2f89b7f7SBesar Wicaksono 935*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_register_pmu(struct nv_c2c_pmu *c2c_pmu) 936*2f89b7f7SBesar Wicaksono { 937*2f89b7f7SBesar Wicaksono int ret; 938*2f89b7f7SBesar Wicaksono 939*2f89b7f7SBesar Wicaksono ret = cpuhp_state_add_instance(nv_c2c_pmu_cpuhp_state, 940*2f89b7f7SBesar Wicaksono &c2c_pmu->cpuhp_node); 941*2f89b7f7SBesar Wicaksono if (ret) { 942*2f89b7f7SBesar Wicaksono dev_err(c2c_pmu->dev, "Error %d registering hotplug\n", ret); 943*2f89b7f7SBesar Wicaksono return ret; 944*2f89b7f7SBesar Wicaksono } 945*2f89b7f7SBesar Wicaksono 946*2f89b7f7SBesar Wicaksono c2c_pmu->pmu = (struct pmu) { 947*2f89b7f7SBesar Wicaksono .parent = c2c_pmu->dev, 948*2f89b7f7SBesar Wicaksono .task_ctx_nr = perf_invalid_context, 949*2f89b7f7SBesar Wicaksono .pmu_enable = nv_c2c_pmu_enable, 950*2f89b7f7SBesar Wicaksono .pmu_disable = nv_c2c_pmu_disable, 951*2f89b7f7SBesar Wicaksono .event_init = nv_c2c_pmu_event_init, 952*2f89b7f7SBesar Wicaksono .add = nv_c2c_pmu_add, 953*2f89b7f7SBesar Wicaksono .del = nv_c2c_pmu_del, 954*2f89b7f7SBesar Wicaksono .start = nv_c2c_pmu_start, 955*2f89b7f7SBesar Wicaksono .stop = nv_c2c_pmu_stop, 956*2f89b7f7SBesar Wicaksono .read = nv_c2c_pmu_read, 957*2f89b7f7SBesar Wicaksono .attr_groups = c2c_pmu->attr_groups, 958*2f89b7f7SBesar Wicaksono .capabilities = PERF_PMU_CAP_NO_EXCLUDE | 959*2f89b7f7SBesar Wicaksono PERF_PMU_CAP_NO_INTERRUPT, 960*2f89b7f7SBesar Wicaksono }; 961*2f89b7f7SBesar Wicaksono 962*2f89b7f7SBesar Wicaksono ret = perf_pmu_register(&c2c_pmu->pmu, c2c_pmu->name, -1); 963*2f89b7f7SBesar Wicaksono if (ret) { 964*2f89b7f7SBesar Wicaksono dev_err(c2c_pmu->dev, "Failed to register C2C PMU: %d\n", ret); 965*2f89b7f7SBesar Wicaksono cpuhp_state_remove_instance(nv_c2c_pmu_cpuhp_state, 966*2f89b7f7SBesar Wicaksono &c2c_pmu->cpuhp_node); 967*2f89b7f7SBesar Wicaksono return ret; 968*2f89b7f7SBesar Wicaksono } 969*2f89b7f7SBesar Wicaksono 970*2f89b7f7SBesar Wicaksono return 0; 971*2f89b7f7SBesar Wicaksono } 972*2f89b7f7SBesar Wicaksono 973*2f89b7f7SBesar Wicaksono static int nv_c2c_pmu_probe(struct platform_device *pdev) 974*2f89b7f7SBesar Wicaksono { 975*2f89b7f7SBesar Wicaksono int ret; 976*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu; 977*2f89b7f7SBesar Wicaksono 978*2f89b7f7SBesar Wicaksono c2c_pmu = nv_c2c_pmu_init_pmu(pdev); 979*2f89b7f7SBesar Wicaksono if (IS_ERR(c2c_pmu)) 980*2f89b7f7SBesar Wicaksono return PTR_ERR(c2c_pmu); 981*2f89b7f7SBesar Wicaksono 982*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_init_mmio(c2c_pmu); 983*2f89b7f7SBesar Wicaksono if (ret) 984*2f89b7f7SBesar Wicaksono return ret; 985*2f89b7f7SBesar Wicaksono 986*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_get_cpus(c2c_pmu); 987*2f89b7f7SBesar Wicaksono if (ret) 988*2f89b7f7SBesar Wicaksono return ret; 989*2f89b7f7SBesar Wicaksono 990*2f89b7f7SBesar Wicaksono ret = nv_c2c_pmu_register_pmu(c2c_pmu); 991*2f89b7f7SBesar Wicaksono if (ret) 992*2f89b7f7SBesar Wicaksono return ret; 993*2f89b7f7SBesar Wicaksono 994*2f89b7f7SBesar Wicaksono dev_dbg(c2c_pmu->dev, "Registered %s PMU\n", c2c_pmu->name); 995*2f89b7f7SBesar Wicaksono 996*2f89b7f7SBesar Wicaksono return 0; 997*2f89b7f7SBesar Wicaksono } 998*2f89b7f7SBesar Wicaksono 999*2f89b7f7SBesar Wicaksono static void nv_c2c_pmu_device_remove(struct platform_device *pdev) 1000*2f89b7f7SBesar Wicaksono { 1001*2f89b7f7SBesar Wicaksono struct nv_c2c_pmu *c2c_pmu = platform_get_drvdata(pdev); 1002*2f89b7f7SBesar Wicaksono 1003*2f89b7f7SBesar Wicaksono perf_pmu_unregister(&c2c_pmu->pmu); 1004*2f89b7f7SBesar Wicaksono cpuhp_state_remove_instance(nv_c2c_pmu_cpuhp_state, &c2c_pmu->cpuhp_node); 1005*2f89b7f7SBesar Wicaksono } 1006*2f89b7f7SBesar Wicaksono 1007*2f89b7f7SBesar Wicaksono static const struct acpi_device_id nv_c2c_pmu_acpi_match[] = { 1008*2f89b7f7SBesar Wicaksono { "NVDA2023", (kernel_ulong_t)&nv_c2c_pmu_data[C2C_TYPE_NVLINK] }, 1009*2f89b7f7SBesar Wicaksono { "NVDA2022", (kernel_ulong_t)&nv_c2c_pmu_data[C2C_TYPE_NVCLINK] }, 1010*2f89b7f7SBesar Wicaksono { "NVDA2020", (kernel_ulong_t)&nv_c2c_pmu_data[C2C_TYPE_NVDLINK] }, 1011*2f89b7f7SBesar Wicaksono { } 1012*2f89b7f7SBesar Wicaksono }; 1013*2f89b7f7SBesar Wicaksono MODULE_DEVICE_TABLE(acpi, nv_c2c_pmu_acpi_match); 1014*2f89b7f7SBesar Wicaksono 1015*2f89b7f7SBesar Wicaksono static struct platform_driver nv_c2c_pmu_driver = { 1016*2f89b7f7SBesar Wicaksono .driver = { 1017*2f89b7f7SBesar Wicaksono .name = "nvidia-t410-c2c-pmu", 1018*2f89b7f7SBesar Wicaksono .acpi_match_table = nv_c2c_pmu_acpi_match, 1019*2f89b7f7SBesar Wicaksono .suppress_bind_attrs = true, 1020*2f89b7f7SBesar Wicaksono }, 1021*2f89b7f7SBesar Wicaksono .probe = nv_c2c_pmu_probe, 1022*2f89b7f7SBesar Wicaksono .remove = nv_c2c_pmu_device_remove, 1023*2f89b7f7SBesar Wicaksono }; 1024*2f89b7f7SBesar Wicaksono 1025*2f89b7f7SBesar Wicaksono static int __init nv_c2c_pmu_init(void) 1026*2f89b7f7SBesar Wicaksono { 1027*2f89b7f7SBesar Wicaksono int ret; 1028*2f89b7f7SBesar Wicaksono 1029*2f89b7f7SBesar Wicaksono ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 1030*2f89b7f7SBesar Wicaksono "perf/nvidia/c2c:online", 1031*2f89b7f7SBesar Wicaksono nv_c2c_pmu_online_cpu, 1032*2f89b7f7SBesar Wicaksono nv_c2c_pmu_cpu_teardown); 1033*2f89b7f7SBesar Wicaksono if (ret < 0) 1034*2f89b7f7SBesar Wicaksono return ret; 1035*2f89b7f7SBesar Wicaksono 1036*2f89b7f7SBesar Wicaksono nv_c2c_pmu_cpuhp_state = ret; 1037*2f89b7f7SBesar Wicaksono return platform_driver_register(&nv_c2c_pmu_driver); 1038*2f89b7f7SBesar Wicaksono } 1039*2f89b7f7SBesar Wicaksono 1040*2f89b7f7SBesar Wicaksono static void __exit nv_c2c_pmu_exit(void) 1041*2f89b7f7SBesar Wicaksono { 1042*2f89b7f7SBesar Wicaksono platform_driver_unregister(&nv_c2c_pmu_driver); 1043*2f89b7f7SBesar Wicaksono cpuhp_remove_multi_state(nv_c2c_pmu_cpuhp_state); 1044*2f89b7f7SBesar Wicaksono } 1045*2f89b7f7SBesar Wicaksono 1046*2f89b7f7SBesar Wicaksono module_init(nv_c2c_pmu_init); 1047*2f89b7f7SBesar Wicaksono module_exit(nv_c2c_pmu_exit); 1048*2f89b7f7SBesar Wicaksono 1049*2f89b7f7SBesar Wicaksono MODULE_LICENSE("GPL"); 1050*2f89b7f7SBesar Wicaksono MODULE_DESCRIPTION("NVIDIA Tegra410 C2C PMU driver"); 1051*2f89b7f7SBesar Wicaksono MODULE_AUTHOR("Besar Wicaksono <bwicaksono@nvidia.com>"); 1052