18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2019 Advanced Micro Devices, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Author: Jonathan Kim <jonathan.kim@amd.com> 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 278c2ecf20Sopenharmony_ci#include <linux/init.h> 288c2ecf20Sopenharmony_ci#include "amdgpu.h" 298c2ecf20Sopenharmony_ci#include "amdgpu_pmu.h" 308c2ecf20Sopenharmony_ci#include "df_v3_6.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define PMU_NAME_SIZE 32 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* record to keep track of pmu entry per pmu type per device */ 358c2ecf20Sopenharmony_cistruct amdgpu_pmu_entry { 368c2ecf20Sopenharmony_ci struct list_head entry; 378c2ecf20Sopenharmony_ci struct amdgpu_device *adev; 388c2ecf20Sopenharmony_ci struct pmu pmu; 398c2ecf20Sopenharmony_ci unsigned int pmu_perf_type; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic LIST_HEAD(amdgpu_pmu_list); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* initialize perf counter */ 468c2ecf20Sopenharmony_cistatic int amdgpu_perf_event_init(struct perf_event *event) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* test the event attr type check for PMU enumeration */ 518c2ecf20Sopenharmony_ci if (event->attr.type != event->pmu->type) 528c2ecf20Sopenharmony_ci return -ENOENT; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* update the hw_perf_event struct with config data */ 558c2ecf20Sopenharmony_ci hwc->config = event->attr.config; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* start perf counter */ 618c2ecf20Sopenharmony_cistatic void amdgpu_perf_start(struct perf_event *event, int flags) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 648c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 658c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry, 668c2ecf20Sopenharmony_ci pmu); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 728c2ecf20Sopenharmony_ci hwc->state = 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (pe->pmu_perf_type) { 758c2ecf20Sopenharmony_ci case PERF_TYPE_AMDGPU_DF: 768c2ecf20Sopenharmony_ci if (!(flags & PERF_EF_RELOAD)) 778c2ecf20Sopenharmony_ci pe->adev->df.funcs->pmc_start(pe->adev, hwc->config, 1); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pe->adev->df.funcs->pmc_start(pe->adev, hwc->config, 0); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci default: 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* read perf counter */ 908c2ecf20Sopenharmony_cistatic void amdgpu_perf_read(struct perf_event *event) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 938c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 948c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry, 958c2ecf20Sopenharmony_ci pmu); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci u64 count, prev; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci do { 1008c2ecf20Sopenharmony_ci prev = local64_read(&hwc->prev_count); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci switch (pe->pmu_perf_type) { 1038c2ecf20Sopenharmony_ci case PERF_TYPE_AMDGPU_DF: 1048c2ecf20Sopenharmony_ci pe->adev->df.funcs->pmc_get_count(pe->adev, hwc->config, 1058c2ecf20Sopenharmony_ci &count); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci default: 1088c2ecf20Sopenharmony_ci count = 0; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } while (local64_cmpxchg(&hwc->prev_count, prev, count) != prev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci local64_add(count - prev, &event->count); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* stop perf counter */ 1178c2ecf20Sopenharmony_cistatic void amdgpu_perf_stop(struct perf_event *event, int flags) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 1208c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 1218c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry, 1228c2ecf20Sopenharmony_ci pmu); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (hwc->state & PERF_HES_UPTODATE) 1258c2ecf20Sopenharmony_ci return; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci switch (pe->pmu_perf_type) { 1288c2ecf20Sopenharmony_ci case PERF_TYPE_AMDGPU_DF: 1298c2ecf20Sopenharmony_ci pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, 0); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci default: 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); 1368c2ecf20Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (hwc->state & PERF_HES_UPTODATE) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci amdgpu_perf_read(event); 1428c2ecf20Sopenharmony_ci hwc->state |= PERF_HES_UPTODATE; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* add perf counter */ 1468c2ecf20Sopenharmony_cistatic int amdgpu_perf_add(struct perf_event *event, int flags) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 1498c2ecf20Sopenharmony_ci int retval; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 1528c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry, 1538c2ecf20Sopenharmony_ci pmu); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci switch (pe->pmu_perf_type) { 1588c2ecf20Sopenharmony_ci case PERF_TYPE_AMDGPU_DF: 1598c2ecf20Sopenharmony_ci retval = pe->adev->df.funcs->pmc_start(pe->adev, 1608c2ecf20Sopenharmony_ci hwc->config, 1); 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci default: 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (retval) 1678c2ecf20Sopenharmony_ci return retval; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 1708c2ecf20Sopenharmony_ci amdgpu_perf_start(event, PERF_EF_RELOAD); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return retval; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* delete perf counter */ 1778c2ecf20Sopenharmony_cistatic void amdgpu_perf_del(struct perf_event *event, int flags) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 1808c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe = container_of(event->pmu, 1818c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry, 1828c2ecf20Sopenharmony_ci pmu); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci amdgpu_perf_stop(event, PERF_EF_UPDATE); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci switch (pe->pmu_perf_type) { 1878c2ecf20Sopenharmony_ci case PERF_TYPE_AMDGPU_DF: 1888c2ecf20Sopenharmony_ci pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, 1); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci default: 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* vega20 pmus */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* init pmu tracking per pmu type */ 2008c2ecf20Sopenharmony_cistatic int init_pmu_by_type(struct amdgpu_device *adev, 2018c2ecf20Sopenharmony_ci const struct attribute_group *attr_groups[], 2028c2ecf20Sopenharmony_ci char *pmu_type_name, char *pmu_file_prefix, 2038c2ecf20Sopenharmony_ci unsigned int pmu_perf_type, 2048c2ecf20Sopenharmony_ci unsigned int num_counters) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci char pmu_name[PMU_NAME_SIZE]; 2078c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pmu_entry; 2088c2ecf20Sopenharmony_ci int ret = 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci pmu_entry = kzalloc(sizeof(struct amdgpu_pmu_entry), GFP_KERNEL); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!pmu_entry) 2138c2ecf20Sopenharmony_ci return -ENOMEM; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci pmu_entry->adev = adev; 2168c2ecf20Sopenharmony_ci pmu_entry->pmu = (struct pmu){ 2178c2ecf20Sopenharmony_ci .event_init = amdgpu_perf_event_init, 2188c2ecf20Sopenharmony_ci .add = amdgpu_perf_add, 2198c2ecf20Sopenharmony_ci .del = amdgpu_perf_del, 2208c2ecf20Sopenharmony_ci .start = amdgpu_perf_start, 2218c2ecf20Sopenharmony_ci .stop = amdgpu_perf_stop, 2228c2ecf20Sopenharmony_ci .read = amdgpu_perf_read, 2238c2ecf20Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 2248c2ecf20Sopenharmony_ci }; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci pmu_entry->pmu.attr_groups = attr_groups; 2278c2ecf20Sopenharmony_ci pmu_entry->pmu_perf_type = pmu_perf_type; 2288c2ecf20Sopenharmony_ci snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d", 2298c2ecf20Sopenharmony_ci pmu_file_prefix, adev_to_drm(adev)->primary->index); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci kfree(pmu_entry); 2358c2ecf20Sopenharmony_ci pr_warn("Error initializing AMDGPU %s PMUs.\n", pmu_type_name); 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n", 2408c2ecf20Sopenharmony_ci pmu_type_name, num_counters); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* init amdgpu_pmu */ 2488c2ecf20Sopenharmony_ciint amdgpu_pmu_init(struct amdgpu_device *adev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret = 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci switch (adev->asic_type) { 2538c2ecf20Sopenharmony_ci case CHIP_VEGA20: 2548c2ecf20Sopenharmony_ci /* init df */ 2558c2ecf20Sopenharmony_ci ret = init_pmu_by_type(adev, df_v3_6_attr_groups, 2568c2ecf20Sopenharmony_ci "DF", "amdgpu_df", PERF_TYPE_AMDGPU_DF, 2578c2ecf20Sopenharmony_ci DF_V3_6_MAX_COUNTERS); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* other pmu types go here*/ 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* destroy all pmu data associated with target device */ 2708c2ecf20Sopenharmony_civoid amdgpu_pmu_fini(struct amdgpu_device *adev) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct amdgpu_pmu_entry *pe, *temp; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) { 2758c2ecf20Sopenharmony_ci if (pe->adev == adev) { 2768c2ecf20Sopenharmony_ci list_del(&pe->entry); 2778c2ecf20Sopenharmony_ci perf_pmu_unregister(&pe->pmu); 2788c2ecf20Sopenharmony_ci kfree(pe); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci} 282