162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2019 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1262306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/perf_event.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include "amdgpu.h" 2762306a36Sopenharmony_ci#include "amdgpu_pmu.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define PMU_NAME_SIZE 32 3062306a36Sopenharmony_ci#define NUM_FORMATS_AMDGPU_PMU 4 3162306a36Sopenharmony_ci#define NUM_FORMATS_DF_VEGA20 3 3262306a36Sopenharmony_ci#define NUM_EVENTS_DF_VEGA20 8 3362306a36Sopenharmony_ci#define NUM_EVENT_TYPES_VEGA20 1 3462306a36Sopenharmony_ci#define NUM_EVENTS_VEGA20_XGMI 2 3562306a36Sopenharmony_ci#define NUM_EVENTS_VEGA20_MAX NUM_EVENTS_VEGA20_XGMI 3662306a36Sopenharmony_ci#define NUM_EVENT_TYPES_ARCTURUS 1 3762306a36Sopenharmony_ci#define NUM_EVENTS_ARCTURUS_XGMI 6 3862306a36Sopenharmony_ci#define NUM_EVENTS_ARCTURUS_MAX NUM_EVENTS_ARCTURUS_XGMI 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct amdgpu_pmu_event_attribute { 4162306a36Sopenharmony_ci struct device_attribute attr; 4262306a36Sopenharmony_ci const char *event_str; 4362306a36Sopenharmony_ci unsigned int type; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* record to keep track of pmu entry per pmu type per device */ 4762306a36Sopenharmony_cistruct amdgpu_pmu_entry { 4862306a36Sopenharmony_ci struct list_head entry; 4962306a36Sopenharmony_ci struct amdgpu_device *adev; 5062306a36Sopenharmony_ci struct pmu pmu; 5162306a36Sopenharmony_ci unsigned int pmu_perf_type; 5262306a36Sopenharmony_ci char *pmu_type_name; 5362306a36Sopenharmony_ci char *pmu_file_prefix; 5462306a36Sopenharmony_ci struct attribute_group fmt_attr_group; 5562306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute *fmt_attr; 5662306a36Sopenharmony_ci struct attribute_group evt_attr_group; 5762306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute *evt_attr; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic ssize_t amdgpu_pmu_event_show(struct device *dev, 6162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute *amdgpu_pmu_attr; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci amdgpu_pmu_attr = container_of(attr, struct amdgpu_pmu_event_attribute, 6662306a36Sopenharmony_ci attr); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!amdgpu_pmu_attr->type) 6962306a36Sopenharmony_ci return sprintf(buf, "%s\n", amdgpu_pmu_attr->event_str); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return sprintf(buf, "%s,type=0x%x\n", 7262306a36Sopenharmony_ci amdgpu_pmu_attr->event_str, amdgpu_pmu_attr->type); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic LIST_HEAD(amdgpu_pmu_list); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct amdgpu_pmu_attr { 7962306a36Sopenharmony_ci const char *name; 8062306a36Sopenharmony_ci const char *config; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct amdgpu_pmu_type { 8462306a36Sopenharmony_ci const unsigned int type; 8562306a36Sopenharmony_ci const unsigned int num_of_type; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct amdgpu_pmu_config { 8962306a36Sopenharmony_ci struct amdgpu_pmu_attr *formats; 9062306a36Sopenharmony_ci unsigned int num_formats; 9162306a36Sopenharmony_ci struct amdgpu_pmu_attr *events; 9262306a36Sopenharmony_ci unsigned int num_events; 9362306a36Sopenharmony_ci struct amdgpu_pmu_type *types; 9462306a36Sopenharmony_ci unsigned int num_types; 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * Events fall under two categories: 9962306a36Sopenharmony_ci * - PMU typed 10062306a36Sopenharmony_ci * Events in /sys/bus/event_source/devices/amdgpu_<pmu_type>_<dev_num> have 10162306a36Sopenharmony_ci * performance counter operations handled by one IP <pmu_type>. Formats and 10262306a36Sopenharmony_ci * events should be defined by <pmu_type>_<asic_type>_formats and 10362306a36Sopenharmony_ci * <pmu_type>_<asic_type>_events respectively. 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * - Event config typed 10662306a36Sopenharmony_ci * Events in /sys/bus/event_source/devices/amdgpu_<dev_num> have performance 10762306a36Sopenharmony_ci * counter operations that can be handled by multiple IPs dictated by their 10862306a36Sopenharmony_ci * "type" format field. Formats and events should be defined by 10962306a36Sopenharmony_ci * amdgpu_pmu_formats and <asic_type>_events respectively. Format field 11062306a36Sopenharmony_ci * "type" is generated in amdgpu_pmu_event_show and defined in 11162306a36Sopenharmony_ci * <asic_type>_event_config_types. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct amdgpu_pmu_attr amdgpu_pmu_formats[NUM_FORMATS_AMDGPU_PMU] = { 11562306a36Sopenharmony_ci { .name = "event", .config = "config:0-7" }, 11662306a36Sopenharmony_ci { .name = "instance", .config = "config:8-15" }, 11762306a36Sopenharmony_ci { .name = "umask", .config = "config:16-23"}, 11862306a36Sopenharmony_ci { .name = "type", .config = "config:56-63"} 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Vega20 events */ 12262306a36Sopenharmony_cistatic struct amdgpu_pmu_attr vega20_events[NUM_EVENTS_VEGA20_MAX] = { 12362306a36Sopenharmony_ci { .name = "xgmi_link0_data_outbound", 12462306a36Sopenharmony_ci .config = "event=0x7,instance=0x46,umask=0x2" }, 12562306a36Sopenharmony_ci { .name = "xgmi_link1_data_outbound", 12662306a36Sopenharmony_ci .config = "event=0x7,instance=0x47,umask=0x2" } 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct amdgpu_pmu_type vega20_types[NUM_EVENT_TYPES_VEGA20] = { 13062306a36Sopenharmony_ci { .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI, 13162306a36Sopenharmony_ci .num_of_type = NUM_EVENTS_VEGA20_XGMI } 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic struct amdgpu_pmu_config vega20_config = { 13562306a36Sopenharmony_ci .formats = amdgpu_pmu_formats, 13662306a36Sopenharmony_ci .num_formats = ARRAY_SIZE(amdgpu_pmu_formats), 13762306a36Sopenharmony_ci .events = vega20_events, 13862306a36Sopenharmony_ci .num_events = ARRAY_SIZE(vega20_events), 13962306a36Sopenharmony_ci .types = vega20_types, 14062306a36Sopenharmony_ci .num_types = ARRAY_SIZE(vega20_types) 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Vega20 data fabric (DF) events */ 14462306a36Sopenharmony_cistatic struct amdgpu_pmu_attr df_vega20_formats[NUM_FORMATS_DF_VEGA20] = { 14562306a36Sopenharmony_ci { .name = "event", .config = "config:0-7" }, 14662306a36Sopenharmony_ci { .name = "instance", .config = "config:8-15" }, 14762306a36Sopenharmony_ci { .name = "umask", .config = "config:16-23"} 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct amdgpu_pmu_attr df_vega20_events[NUM_EVENTS_DF_VEGA20] = { 15162306a36Sopenharmony_ci { .name = "cake0_pcsout_txdata", 15262306a36Sopenharmony_ci .config = "event=0x7,instance=0x46,umask=0x2" }, 15362306a36Sopenharmony_ci { .name = "cake1_pcsout_txdata", 15462306a36Sopenharmony_ci .config = "event=0x7,instance=0x47,umask=0x2" }, 15562306a36Sopenharmony_ci { .name = "cake0_pcsout_txmeta", 15662306a36Sopenharmony_ci .config = "event=0x7,instance=0x46,umask=0x4" }, 15762306a36Sopenharmony_ci { .name = "cake1_pcsout_txmeta", 15862306a36Sopenharmony_ci .config = "event=0x7,instance=0x47,umask=0x4" }, 15962306a36Sopenharmony_ci { .name = "cake0_ftiinstat_reqalloc", 16062306a36Sopenharmony_ci .config = "event=0xb,instance=0x46,umask=0x4" }, 16162306a36Sopenharmony_ci { .name = "cake1_ftiinstat_reqalloc", 16262306a36Sopenharmony_ci .config = "event=0xb,instance=0x47,umask=0x4" }, 16362306a36Sopenharmony_ci { .name = "cake0_ftiinstat_rspalloc", 16462306a36Sopenharmony_ci .config = "event=0xb,instance=0x46,umask=0x8" }, 16562306a36Sopenharmony_ci { .name = "cake1_ftiinstat_rspalloc", 16662306a36Sopenharmony_ci .config = "event=0xb,instance=0x47,umask=0x8" } 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct amdgpu_pmu_config df_vega20_config = { 17062306a36Sopenharmony_ci .formats = df_vega20_formats, 17162306a36Sopenharmony_ci .num_formats = ARRAY_SIZE(df_vega20_formats), 17262306a36Sopenharmony_ci .events = df_vega20_events, 17362306a36Sopenharmony_ci .num_events = ARRAY_SIZE(df_vega20_events), 17462306a36Sopenharmony_ci .types = NULL, 17562306a36Sopenharmony_ci .num_types = 0 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* Arcturus events */ 17962306a36Sopenharmony_cistatic struct amdgpu_pmu_attr arcturus_events[NUM_EVENTS_ARCTURUS_MAX] = { 18062306a36Sopenharmony_ci { .name = "xgmi_link0_data_outbound", 18162306a36Sopenharmony_ci .config = "event=0x7,instance=0x4b,umask=0x2" }, 18262306a36Sopenharmony_ci { .name = "xgmi_link1_data_outbound", 18362306a36Sopenharmony_ci .config = "event=0x7,instance=0x4c,umask=0x2" }, 18462306a36Sopenharmony_ci { .name = "xgmi_link2_data_outbound", 18562306a36Sopenharmony_ci .config = "event=0x7,instance=0x4d,umask=0x2" }, 18662306a36Sopenharmony_ci { .name = "xgmi_link3_data_outbound", 18762306a36Sopenharmony_ci .config = "event=0x7,instance=0x4e,umask=0x2" }, 18862306a36Sopenharmony_ci { .name = "xgmi_link4_data_outbound", 18962306a36Sopenharmony_ci .config = "event=0x7,instance=0x4f,umask=0x2" }, 19062306a36Sopenharmony_ci { .name = "xgmi_link5_data_outbound", 19162306a36Sopenharmony_ci .config = "event=0x7,instance=0x50,umask=0x2" } 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic struct amdgpu_pmu_type arcturus_types[NUM_EVENT_TYPES_ARCTURUS] = { 19562306a36Sopenharmony_ci { .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI, 19662306a36Sopenharmony_ci .num_of_type = NUM_EVENTS_ARCTURUS_XGMI } 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct amdgpu_pmu_config arcturus_config = { 20062306a36Sopenharmony_ci .formats = amdgpu_pmu_formats, 20162306a36Sopenharmony_ci .num_formats = ARRAY_SIZE(amdgpu_pmu_formats), 20262306a36Sopenharmony_ci .events = arcturus_events, 20362306a36Sopenharmony_ci .num_events = ARRAY_SIZE(arcturus_events), 20462306a36Sopenharmony_ci .types = arcturus_types, 20562306a36Sopenharmony_ci .num_types = ARRAY_SIZE(arcturus_types) 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* initialize perf counter */ 20962306a36Sopenharmony_cistatic int amdgpu_perf_event_init(struct perf_event *event) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* test the event attr type check for PMU enumeration */ 21462306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 21562306a36Sopenharmony_ci return -ENOENT; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* update the hw_perf_event struct with config data */ 21862306a36Sopenharmony_ci hwc->config = event->attr.config; 21962306a36Sopenharmony_ci hwc->config_base = AMDGPU_PMU_PERF_TYPE_NONE; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* start perf counter */ 22562306a36Sopenharmony_cistatic void amdgpu_perf_start(struct perf_event *event, int flags) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 22862306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 22962306a36Sopenharmony_ci struct amdgpu_pmu_entry, 23062306a36Sopenharmony_ci pmu); 23162306a36Sopenharmony_ci int target_cntr = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if ((!pe->adev->df.funcs) || 23762306a36Sopenharmony_ci (!pe->adev->df.funcs->pmc_start)) 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 24162306a36Sopenharmony_ci hwc->state = 0; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci switch (hwc->config_base) { 24462306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: 24562306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: 24662306a36Sopenharmony_ci if (!(flags & PERF_EF_RELOAD)) { 24762306a36Sopenharmony_ci target_cntr = pe->adev->df.funcs->pmc_start(pe->adev, 24862306a36Sopenharmony_ci hwc->config, 0 /* unused */, 24962306a36Sopenharmony_ci 1 /* add counter */); 25062306a36Sopenharmony_ci if (target_cntr < 0) 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci hwc->idx = target_cntr; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pe->adev->df.funcs->pmc_start(pe->adev, hwc->config, 25762306a36Sopenharmony_ci hwc->idx, 0); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci perf_event_update_userpage(event); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* read perf counter */ 26762306a36Sopenharmony_cistatic void amdgpu_perf_read(struct perf_event *event) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 27062306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 27162306a36Sopenharmony_ci struct amdgpu_pmu_entry, 27262306a36Sopenharmony_ci pmu); 27362306a36Sopenharmony_ci u64 count, prev; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if ((!pe->adev->df.funcs) || 27662306a36Sopenharmony_ci (!pe->adev->df.funcs->pmc_get_count)) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci prev = local64_read(&hwc->prev_count); 28062306a36Sopenharmony_ci do { 28162306a36Sopenharmony_ci switch (hwc->config_base) { 28262306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: 28362306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: 28462306a36Sopenharmony_ci pe->adev->df.funcs->pmc_get_count(pe->adev, 28562306a36Sopenharmony_ci hwc->config, hwc->idx, &count); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci count = 0; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } while (!local64_try_cmpxchg(&hwc->prev_count, &prev, count)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci local64_add(count - prev, &event->count); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* stop perf counter */ 29762306a36Sopenharmony_cistatic void amdgpu_perf_stop(struct perf_event *event, int flags) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 30062306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 30162306a36Sopenharmony_ci struct amdgpu_pmu_entry, 30262306a36Sopenharmony_ci pmu); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (hwc->state & PERF_HES_UPTODATE) 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if ((!pe->adev->df.funcs) || 30862306a36Sopenharmony_ci (!pe->adev->df.funcs->pmc_stop)) 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci switch (hwc->config_base) { 31262306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: 31362306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: 31462306a36Sopenharmony_ci pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx, 31562306a36Sopenharmony_ci 0); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); 32262306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (hwc->state & PERF_HES_UPTODATE) 32562306a36Sopenharmony_ci return; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci amdgpu_perf_read(event); 32862306a36Sopenharmony_ci hwc->state |= PERF_HES_UPTODATE; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* add perf counter */ 33262306a36Sopenharmony_cistatic int amdgpu_perf_add(struct perf_event *event, int flags) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 33562306a36Sopenharmony_ci int retval = 0, target_cntr; 33662306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 33762306a36Sopenharmony_ci struct amdgpu_pmu_entry, 33862306a36Sopenharmony_ci pmu); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if ((!pe->adev->df.funcs) || 34162306a36Sopenharmony_ci (!pe->adev->df.funcs->pmc_start)) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci switch (pe->pmu_perf_type) { 34562306a36Sopenharmony_ci case AMDGPU_PMU_PERF_TYPE_DF: 34662306a36Sopenharmony_ci hwc->config_base = AMDGPU_PMU_EVENT_CONFIG_TYPE_DF; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case AMDGPU_PMU_PERF_TYPE_ALL: 34962306a36Sopenharmony_ci hwc->config_base = (hwc->config >> 35062306a36Sopenharmony_ci AMDGPU_PMU_EVENT_CONFIG_TYPE_SHIFT) & 35162306a36Sopenharmony_ci AMDGPU_PMU_EVENT_CONFIG_TYPE_MASK; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (hwc->config_base) { 35862306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: 35962306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: 36062306a36Sopenharmony_ci target_cntr = pe->adev->df.funcs->pmc_start(pe->adev, 36162306a36Sopenharmony_ci hwc->config, 0 /* unused */, 36262306a36Sopenharmony_ci 1 /* add counter */); 36362306a36Sopenharmony_ci if (target_cntr < 0) 36462306a36Sopenharmony_ci retval = target_cntr; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci hwc->idx = target_cntr; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci default: 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (retval) 37462306a36Sopenharmony_ci return retval; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (flags & PERF_EF_START) 37762306a36Sopenharmony_ci amdgpu_perf_start(event, PERF_EF_RELOAD); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return retval; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* delete perf counter */ 38362306a36Sopenharmony_cistatic void amdgpu_perf_del(struct perf_event *event, int flags) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 38662306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 38762306a36Sopenharmony_ci struct amdgpu_pmu_entry, 38862306a36Sopenharmony_ci pmu); 38962306a36Sopenharmony_ci if ((!pe->adev->df.funcs) || 39062306a36Sopenharmony_ci (!pe->adev->df.funcs->pmc_stop)) 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci amdgpu_perf_stop(event, PERF_EF_UPDATE); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci switch (hwc->config_base) { 39662306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: 39762306a36Sopenharmony_ci case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: 39862306a36Sopenharmony_ci pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx, 39962306a36Sopenharmony_ci 1); 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci default: 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci perf_event_update_userpage(event); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void amdgpu_pmu_create_event_attrs_by_type( 40962306a36Sopenharmony_ci struct attribute_group *attr_group, 41062306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute *pmu_attr, 41162306a36Sopenharmony_ci struct amdgpu_pmu_attr events[], 41262306a36Sopenharmony_ci int s_offset, 41362306a36Sopenharmony_ci int e_offset, 41462306a36Sopenharmony_ci unsigned int type) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int i; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci pmu_attr += s_offset; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci for (i = s_offset; i < e_offset; i++) { 42162306a36Sopenharmony_ci attr_group->attrs[i] = &pmu_attr->attr.attr; 42262306a36Sopenharmony_ci sysfs_attr_init(&pmu_attr->attr.attr); 42362306a36Sopenharmony_ci pmu_attr->attr.attr.name = events[i].name; 42462306a36Sopenharmony_ci pmu_attr->attr.attr.mode = 0444; 42562306a36Sopenharmony_ci pmu_attr->attr.show = amdgpu_pmu_event_show; 42662306a36Sopenharmony_ci pmu_attr->event_str = events[i].config; 42762306a36Sopenharmony_ci pmu_attr->type = type; 42862306a36Sopenharmony_ci pmu_attr++; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void amdgpu_pmu_create_attrs(struct attribute_group *attr_group, 43362306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute *pmu_attr, 43462306a36Sopenharmony_ci struct amdgpu_pmu_attr events[], 43562306a36Sopenharmony_ci int num_events) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci amdgpu_pmu_create_event_attrs_by_type(attr_group, pmu_attr, events, 0, 43862306a36Sopenharmony_ci num_events, AMDGPU_PMU_EVENT_CONFIG_TYPE_NONE); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int amdgpu_pmu_alloc_pmu_attrs( 44362306a36Sopenharmony_ci struct attribute_group *fmt_attr_group, 44462306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute **fmt_attr, 44562306a36Sopenharmony_ci struct attribute_group *evt_attr_group, 44662306a36Sopenharmony_ci struct amdgpu_pmu_event_attribute **evt_attr, 44762306a36Sopenharmony_ci struct amdgpu_pmu_config *config) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci *fmt_attr = kcalloc(config->num_formats, sizeof(**fmt_attr), 45062306a36Sopenharmony_ci GFP_KERNEL); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!(*fmt_attr)) 45362306a36Sopenharmony_ci return -ENOMEM; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci fmt_attr_group->attrs = kcalloc(config->num_formats + 1, 45662306a36Sopenharmony_ci sizeof(*fmt_attr_group->attrs), GFP_KERNEL); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!fmt_attr_group->attrs) 45962306a36Sopenharmony_ci goto err_fmt_attr_grp; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci *evt_attr = kcalloc(config->num_events, sizeof(**evt_attr), GFP_KERNEL); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!(*evt_attr)) 46462306a36Sopenharmony_ci goto err_evt_attr; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci evt_attr_group->attrs = kcalloc(config->num_events + 1, 46762306a36Sopenharmony_ci sizeof(*evt_attr_group->attrs), GFP_KERNEL); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!evt_attr_group->attrs) 47062306a36Sopenharmony_ci goto err_evt_attr_grp; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_cierr_evt_attr_grp: 47462306a36Sopenharmony_ci kfree(*evt_attr); 47562306a36Sopenharmony_cierr_evt_attr: 47662306a36Sopenharmony_ci kfree(fmt_attr_group->attrs); 47762306a36Sopenharmony_cierr_fmt_attr_grp: 47862306a36Sopenharmony_ci kfree(*fmt_attr); 47962306a36Sopenharmony_ci return -ENOMEM; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* init pmu tracking per pmu type */ 48362306a36Sopenharmony_cistatic int init_pmu_entry_by_type_and_add(struct amdgpu_pmu_entry *pmu_entry, 48462306a36Sopenharmony_ci struct amdgpu_pmu_config *config) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci const struct attribute_group *attr_groups[] = { 48762306a36Sopenharmony_ci &pmu_entry->fmt_attr_group, 48862306a36Sopenharmony_ci &pmu_entry->evt_attr_group, 48962306a36Sopenharmony_ci NULL 49062306a36Sopenharmony_ci }; 49162306a36Sopenharmony_ci char pmu_name[PMU_NAME_SIZE]; 49262306a36Sopenharmony_ci int ret = 0, total_num_events = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci pmu_entry->pmu = (struct pmu){ 49562306a36Sopenharmony_ci .event_init = amdgpu_perf_event_init, 49662306a36Sopenharmony_ci .add = amdgpu_perf_add, 49762306a36Sopenharmony_ci .del = amdgpu_perf_del, 49862306a36Sopenharmony_ci .start = amdgpu_perf_start, 49962306a36Sopenharmony_ci .stop = amdgpu_perf_stop, 50062306a36Sopenharmony_ci .read = amdgpu_perf_read, 50162306a36Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 50262306a36Sopenharmony_ci }; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci ret = amdgpu_pmu_alloc_pmu_attrs(&pmu_entry->fmt_attr_group, 50562306a36Sopenharmony_ci &pmu_entry->fmt_attr, 50662306a36Sopenharmony_ci &pmu_entry->evt_attr_group, 50762306a36Sopenharmony_ci &pmu_entry->evt_attr, 50862306a36Sopenharmony_ci config); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci goto err_out; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci amdgpu_pmu_create_attrs(&pmu_entry->fmt_attr_group, pmu_entry->fmt_attr, 51462306a36Sopenharmony_ci config->formats, config->num_formats); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (pmu_entry->pmu_perf_type == AMDGPU_PMU_PERF_TYPE_ALL) { 51762306a36Sopenharmony_ci int i; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for (i = 0; i < config->num_types; i++) { 52062306a36Sopenharmony_ci amdgpu_pmu_create_event_attrs_by_type( 52162306a36Sopenharmony_ci &pmu_entry->evt_attr_group, 52262306a36Sopenharmony_ci pmu_entry->evt_attr, 52362306a36Sopenharmony_ci config->events, 52462306a36Sopenharmony_ci total_num_events, 52562306a36Sopenharmony_ci total_num_events + 52662306a36Sopenharmony_ci config->types[i].num_of_type, 52762306a36Sopenharmony_ci config->types[i].type); 52862306a36Sopenharmony_ci total_num_events += config->types[i].num_of_type; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci } else { 53162306a36Sopenharmony_ci amdgpu_pmu_create_attrs(&pmu_entry->evt_attr_group, 53262306a36Sopenharmony_ci pmu_entry->evt_attr, 53362306a36Sopenharmony_ci config->events, config->num_events); 53462306a36Sopenharmony_ci total_num_events = config->num_events; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci pmu_entry->pmu.attr_groups = kmemdup(attr_groups, sizeof(attr_groups), 53862306a36Sopenharmony_ci GFP_KERNEL); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!pmu_entry->pmu.attr_groups) { 54162306a36Sopenharmony_ci ret = -ENOMEM; 54262306a36Sopenharmony_ci goto err_attr_group; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d", pmu_entry->pmu_file_prefix, 54662306a36Sopenharmony_ci adev_to_drm(pmu_entry->adev)->primary->index); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci goto err_register; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (pmu_entry->pmu_perf_type != AMDGPU_PMU_PERF_TYPE_ALL) 55462306a36Sopenharmony_ci pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n", 55562306a36Sopenharmony_ci pmu_entry->pmu_type_name, total_num_events); 55662306a36Sopenharmony_ci else 55762306a36Sopenharmony_ci pr_info("Detected AMDGPU %d Perf Events.\n", total_num_events); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_cierr_register: 56462306a36Sopenharmony_ci kfree(pmu_entry->pmu.attr_groups); 56562306a36Sopenharmony_cierr_attr_group: 56662306a36Sopenharmony_ci kfree(pmu_entry->fmt_attr_group.attrs); 56762306a36Sopenharmony_ci kfree(pmu_entry->fmt_attr); 56862306a36Sopenharmony_ci kfree(pmu_entry->evt_attr_group.attrs); 56962306a36Sopenharmony_ci kfree(pmu_entry->evt_attr); 57062306a36Sopenharmony_cierr_out: 57162306a36Sopenharmony_ci pr_warn("Error initializing AMDGPU %s PMUs.\n", 57262306a36Sopenharmony_ci pmu_entry->pmu_type_name); 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* destroy all pmu data associated with target device */ 57762306a36Sopenharmony_civoid amdgpu_pmu_fini(struct amdgpu_device *adev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct amdgpu_pmu_entry *pe, *temp; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) { 58262306a36Sopenharmony_ci if (pe->adev != adev) 58362306a36Sopenharmony_ci continue; 58462306a36Sopenharmony_ci list_del(&pe->entry); 58562306a36Sopenharmony_ci perf_pmu_unregister(&pe->pmu); 58662306a36Sopenharmony_ci kfree(pe->pmu.attr_groups); 58762306a36Sopenharmony_ci kfree(pe->fmt_attr_group.attrs); 58862306a36Sopenharmony_ci kfree(pe->fmt_attr); 58962306a36Sopenharmony_ci kfree(pe->evt_attr_group.attrs); 59062306a36Sopenharmony_ci kfree(pe->evt_attr); 59162306a36Sopenharmony_ci kfree(pe); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic struct amdgpu_pmu_entry *create_pmu_entry(struct amdgpu_device *adev, 59662306a36Sopenharmony_ci unsigned int pmu_type, 59762306a36Sopenharmony_ci char *pmu_type_name, 59862306a36Sopenharmony_ci char *pmu_file_prefix) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct amdgpu_pmu_entry *pmu_entry; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pmu_entry = kzalloc(sizeof(struct amdgpu_pmu_entry), GFP_KERNEL); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!pmu_entry) 60562306a36Sopenharmony_ci return pmu_entry; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci pmu_entry->adev = adev; 60862306a36Sopenharmony_ci pmu_entry->fmt_attr_group.name = "format"; 60962306a36Sopenharmony_ci pmu_entry->fmt_attr_group.attrs = NULL; 61062306a36Sopenharmony_ci pmu_entry->evt_attr_group.name = "events"; 61162306a36Sopenharmony_ci pmu_entry->evt_attr_group.attrs = NULL; 61262306a36Sopenharmony_ci pmu_entry->pmu_perf_type = pmu_type; 61362306a36Sopenharmony_ci pmu_entry->pmu_type_name = pmu_type_name; 61462306a36Sopenharmony_ci pmu_entry->pmu_file_prefix = pmu_file_prefix; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return pmu_entry; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/* init amdgpu_pmu */ 62062306a36Sopenharmony_ciint amdgpu_pmu_init(struct amdgpu_device *adev) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci int ret = 0; 62362306a36Sopenharmony_ci struct amdgpu_pmu_entry *pmu_entry, *pmu_entry_df; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci switch (adev->asic_type) { 62662306a36Sopenharmony_ci case CHIP_VEGA20: 62762306a36Sopenharmony_ci pmu_entry_df = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_DF, 62862306a36Sopenharmony_ci "DF", "amdgpu_df"); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!pmu_entry_df) 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ret = init_pmu_entry_by_type_and_add(pmu_entry_df, 63462306a36Sopenharmony_ci &df_vega20_config); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (ret) { 63762306a36Sopenharmony_ci kfree(pmu_entry_df); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL, 64262306a36Sopenharmony_ci "", "amdgpu"); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!pmu_entry) { 64562306a36Sopenharmony_ci amdgpu_pmu_fini(adev); 64662306a36Sopenharmony_ci return -ENOMEM; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = init_pmu_entry_by_type_and_add(pmu_entry, 65062306a36Sopenharmony_ci &vega20_config); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (ret) { 65362306a36Sopenharmony_ci kfree(pmu_entry); 65462306a36Sopenharmony_ci amdgpu_pmu_fini(adev); 65562306a36Sopenharmony_ci return ret; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci case CHIP_ARCTURUS: 66062306a36Sopenharmony_ci pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL, 66162306a36Sopenharmony_ci "", "amdgpu"); 66262306a36Sopenharmony_ci if (!pmu_entry) 66362306a36Sopenharmony_ci return -ENOMEM; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = init_pmu_entry_by_type_and_add(pmu_entry, 66662306a36Sopenharmony_ci &arcturus_config); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (ret) { 66962306a36Sopenharmony_ci kfree(pmu_entry); 67062306a36Sopenharmony_ci return -ENOMEM; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci default: 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci} 681