18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hypervisor supplied "gpci" ("get performance counter info") performance 48c2ecf20Sopenharmony_ci * counter support 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Cody P Schafer <cody@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci * Copyright 2014 IBM Corporation. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hv-gpci: " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 148c2ecf20Sopenharmony_ci#include <asm/firmware.h> 158c2ecf20Sopenharmony_ci#include <asm/hvcall.h> 168c2ecf20Sopenharmony_ci#include <asm/io.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "hv-gpci.h" 198c2ecf20Sopenharmony_ci#include "hv-common.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Example usage: 238c2ecf20Sopenharmony_ci * perf stat -e 'hv_gpci/counter_info_version=3,offset=0,length=8, 248c2ecf20Sopenharmony_ci * secondary_index=0,starting_index=0xffffffff,request=0x10/' ... 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* u32 */ 288c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(request, config, 0, 31); 298c2ecf20Sopenharmony_ci/* u32 */ 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Note that starting_index, phys_processor_idx, sibling_part_id, 328c2ecf20Sopenharmony_ci * hw_chip_id, partition_id all refer to the same bit range. They 338c2ecf20Sopenharmony_ci * are basically aliases for the starting_index. The specific alias 348c2ecf20Sopenharmony_ci * used depends on the event. See REQUEST_IDX_KIND in hv-gpci-requests.h 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(starting_index, config, 32, 63); 378c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT_LITE(phys_processor_idx, config, 32, 63); 388c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT_LITE(sibling_part_id, config, 32, 63); 398c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT_LITE(hw_chip_id, config, 32, 63); 408c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT_LITE(partition_id, config, 32, 63); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* u16 */ 438c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(secondary_index, config1, 0, 15); 448c2ecf20Sopenharmony_ci/* u8 */ 458c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(counter_info_version, config1, 16, 23); 468c2ecf20Sopenharmony_ci/* u8, bytes of data (1-8) */ 478c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(length, config1, 24, 31); 488c2ecf20Sopenharmony_ci/* u32, byte offset */ 498c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(offset, config1, 32, 63); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic cpumask_t hv_gpci_cpumask; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic struct attribute *format_attrs[] = { 548c2ecf20Sopenharmony_ci &format_attr_request.attr, 558c2ecf20Sopenharmony_ci &format_attr_starting_index.attr, 568c2ecf20Sopenharmony_ci &format_attr_phys_processor_idx.attr, 578c2ecf20Sopenharmony_ci &format_attr_sibling_part_id.attr, 588c2ecf20Sopenharmony_ci &format_attr_hw_chip_id.attr, 598c2ecf20Sopenharmony_ci &format_attr_partition_id.attr, 608c2ecf20Sopenharmony_ci &format_attr_secondary_index.attr, 618c2ecf20Sopenharmony_ci &format_attr_counter_info_version.attr, 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci &format_attr_offset.attr, 648c2ecf20Sopenharmony_ci &format_attr_length.attr, 658c2ecf20Sopenharmony_ci NULL, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct attribute_group format_group = { 698c2ecf20Sopenharmony_ci .name = "format", 708c2ecf20Sopenharmony_ci .attrs = format_attrs, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct attribute_group event_group = { 748c2ecf20Sopenharmony_ci .name = "events", 758c2ecf20Sopenharmony_ci /* .attrs is set in init */ 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define HV_CAPS_ATTR(_name, _format) \ 798c2ecf20Sopenharmony_cistatic ssize_t _name##_show(struct device *dev, \ 808c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 818c2ecf20Sopenharmony_ci char *page) \ 828c2ecf20Sopenharmony_ci{ \ 838c2ecf20Sopenharmony_ci struct hv_perf_caps caps; \ 848c2ecf20Sopenharmony_ci unsigned long hret = hv_perf_caps_get(&caps); \ 858c2ecf20Sopenharmony_ci if (hret) \ 868c2ecf20Sopenharmony_ci return -EIO; \ 878c2ecf20Sopenharmony_ci \ 888c2ecf20Sopenharmony_ci return sprintf(page, _format, caps._name); \ 898c2ecf20Sopenharmony_ci} \ 908c2ecf20Sopenharmony_cistatic struct device_attribute hv_caps_attr_##_name = __ATTR_RO(_name) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic ssize_t kernel_version_show(struct device *dev, 938c2ecf20Sopenharmony_ci struct device_attribute *attr, 948c2ecf20Sopenharmony_ci char *page) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return sprintf(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, 1008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &hv_gpci_cpumask); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(kernel_version); 1068c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ciHV_CAPS_ATTR(version, "0x%x\n"); 1098c2ecf20Sopenharmony_ciHV_CAPS_ATTR(ga, "%d\n"); 1108c2ecf20Sopenharmony_ciHV_CAPS_ATTR(expanded, "%d\n"); 1118c2ecf20Sopenharmony_ciHV_CAPS_ATTR(lab, "%d\n"); 1128c2ecf20Sopenharmony_ciHV_CAPS_ATTR(collect_privileged, "%d\n"); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic struct attribute *interface_attrs[] = { 1158c2ecf20Sopenharmony_ci &dev_attr_kernel_version.attr, 1168c2ecf20Sopenharmony_ci &hv_caps_attr_version.attr, 1178c2ecf20Sopenharmony_ci &hv_caps_attr_ga.attr, 1188c2ecf20Sopenharmony_ci &hv_caps_attr_expanded.attr, 1198c2ecf20Sopenharmony_ci &hv_caps_attr_lab.attr, 1208c2ecf20Sopenharmony_ci &hv_caps_attr_collect_privileged.attr, 1218c2ecf20Sopenharmony_ci NULL, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic struct attribute *cpumask_attrs[] = { 1258c2ecf20Sopenharmony_ci &dev_attr_cpumask.attr, 1268c2ecf20Sopenharmony_ci NULL, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct attribute_group cpumask_attr_group = { 1308c2ecf20Sopenharmony_ci .attrs = cpumask_attrs, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct attribute_group interface_group = { 1348c2ecf20Sopenharmony_ci .name = "interface", 1358c2ecf20Sopenharmony_ci .attrs = interface_attrs, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct attribute_group *attr_groups[] = { 1398c2ecf20Sopenharmony_ci &format_group, 1408c2ecf20Sopenharmony_ci &event_group, 1418c2ecf20Sopenharmony_ci &interface_group, 1428c2ecf20Sopenharmony_ci &cpumask_attr_group, 1438c2ecf20Sopenharmony_ci NULL, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(char, hv_gpci_reqb[HGPCI_REQ_BUFFER_SIZE]) __aligned(sizeof(uint64_t)); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic unsigned long single_gpci_request(u32 req, u32 starting_index, 1498c2ecf20Sopenharmony_ci u16 secondary_index, u8 version_in, u32 offset, u8 length, 1508c2ecf20Sopenharmony_ci u64 *value) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned long ret; 1538c2ecf20Sopenharmony_ci size_t i; 1548c2ecf20Sopenharmony_ci u64 count; 1558c2ecf20Sopenharmony_ci struct hv_gpci_request_buffer *arg; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci arg = (void *)get_cpu_var(hv_gpci_reqb); 1588c2ecf20Sopenharmony_ci memset(arg, 0, HGPCI_REQ_BUFFER_SIZE); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci arg->params.counter_request = cpu_to_be32(req); 1618c2ecf20Sopenharmony_ci arg->params.starting_index = cpu_to_be32(starting_index); 1628c2ecf20Sopenharmony_ci arg->params.secondary_index = cpu_to_be16(secondary_index); 1638c2ecf20Sopenharmony_ci arg->params.counter_info_version_in = version_in; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = plpar_hcall_norets(H_GET_PERF_COUNTER_INFO, 1668c2ecf20Sopenharmony_ci virt_to_phys(arg), HGPCI_REQ_BUFFER_SIZE); 1678c2ecf20Sopenharmony_ci if (ret) { 1688c2ecf20Sopenharmony_ci pr_devel("hcall failed: 0x%lx\n", ret); 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * we verify offset and length are within the zeroed buffer at event 1748c2ecf20Sopenharmony_ci * init. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci count = 0; 1778c2ecf20Sopenharmony_ci for (i = offset; i < offset + length; i++) 1788c2ecf20Sopenharmony_ci count |= (u64)(arg->bytes[i]) << ((length - 1 - (i - offset)) * 8); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci *value = count; 1818c2ecf20Sopenharmony_ciout: 1828c2ecf20Sopenharmony_ci put_cpu_var(hv_gpci_reqb); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic u64 h_gpci_get_value(struct perf_event *event) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci u64 count; 1898c2ecf20Sopenharmony_ci unsigned long ret = single_gpci_request(event_get_request(event), 1908c2ecf20Sopenharmony_ci event_get_starting_index(event), 1918c2ecf20Sopenharmony_ci event_get_secondary_index(event), 1928c2ecf20Sopenharmony_ci event_get_counter_info_version(event), 1938c2ecf20Sopenharmony_ci event_get_offset(event), 1948c2ecf20Sopenharmony_ci event_get_length(event), 1958c2ecf20Sopenharmony_ci &count); 1968c2ecf20Sopenharmony_ci if (ret) 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci return count; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void h_gpci_event_update(struct perf_event *event) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci s64 prev; 2048c2ecf20Sopenharmony_ci u64 now = h_gpci_get_value(event); 2058c2ecf20Sopenharmony_ci prev = local64_xchg(&event->hw.prev_count, now); 2068c2ecf20Sopenharmony_ci local64_add(now - prev, &event->count); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void h_gpci_event_start(struct perf_event *event, int flags) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, h_gpci_get_value(event)); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void h_gpci_event_stop(struct perf_event *event, int flags) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci h_gpci_event_update(event); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int h_gpci_event_add(struct perf_event *event, int flags) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 2228c2ecf20Sopenharmony_ci h_gpci_event_start(event, flags); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int h_gpci_event_init(struct perf_event *event) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci u64 count; 2308c2ecf20Sopenharmony_ci u8 length; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Not our event */ 2338c2ecf20Sopenharmony_ci if (event->attr.type != event->pmu->type) 2348c2ecf20Sopenharmony_ci return -ENOENT; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* config2 is unused */ 2378c2ecf20Sopenharmony_ci if (event->attr.config2) { 2388c2ecf20Sopenharmony_ci pr_devel("config2 set when reserved\n"); 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* no branch sampling */ 2438c2ecf20Sopenharmony_ci if (has_branch_stack(event)) 2448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci length = event_get_length(event); 2478c2ecf20Sopenharmony_ci if (length < 1 || length > 8) { 2488c2ecf20Sopenharmony_ci pr_devel("length invalid\n"); 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* last byte within the buffer? */ 2538c2ecf20Sopenharmony_ci if ((event_get_offset(event) + length) > HGPCI_MAX_DATA_BYTES) { 2548c2ecf20Sopenharmony_ci pr_devel("request outside of buffer: %zu > %zu\n", 2558c2ecf20Sopenharmony_ci (size_t)event_get_offset(event) + length, 2568c2ecf20Sopenharmony_ci HGPCI_MAX_DATA_BYTES); 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* check if the request works... */ 2618c2ecf20Sopenharmony_ci if (single_gpci_request(event_get_request(event), 2628c2ecf20Sopenharmony_ci event_get_starting_index(event), 2638c2ecf20Sopenharmony_ci event_get_secondary_index(event), 2648c2ecf20Sopenharmony_ci event_get_counter_info_version(event), 2658c2ecf20Sopenharmony_ci event_get_offset(event), 2668c2ecf20Sopenharmony_ci length, 2678c2ecf20Sopenharmony_ci &count)) { 2688c2ecf20Sopenharmony_ci pr_devel("gpci hcall failed\n"); 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct pmu h_gpci_pmu = { 2768c2ecf20Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci .name = "hv_gpci", 2798c2ecf20Sopenharmony_ci .attr_groups = attr_groups, 2808c2ecf20Sopenharmony_ci .event_init = h_gpci_event_init, 2818c2ecf20Sopenharmony_ci .add = h_gpci_event_add, 2828c2ecf20Sopenharmony_ci .del = h_gpci_event_stop, 2838c2ecf20Sopenharmony_ci .start = h_gpci_event_start, 2848c2ecf20Sopenharmony_ci .stop = h_gpci_event_stop, 2858c2ecf20Sopenharmony_ci .read = h_gpci_event_update, 2868c2ecf20Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int ppc_hv_gpci_cpu_online(unsigned int cpu) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci if (cpumask_empty(&hv_gpci_cpumask)) 2928c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &hv_gpci_cpumask); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int ppc_hv_gpci_cpu_offline(unsigned int cpu) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci int target; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Check if exiting cpu is used for collecting gpci events */ 3028c2ecf20Sopenharmony_ci if (!cpumask_test_and_clear_cpu(cpu, &hv_gpci_cpumask)) 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Find a new cpu to collect gpci events */ 3068c2ecf20Sopenharmony_ci target = cpumask_last(cpu_active_mask); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (target < 0 || target >= nr_cpu_ids) { 3098c2ecf20Sopenharmony_ci pr_err("hv_gpci: CPU hotplug init failed\n"); 3108c2ecf20Sopenharmony_ci return -1; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Migrate gpci events to the new target */ 3148c2ecf20Sopenharmony_ci cpumask_set_cpu(target, &hv_gpci_cpumask); 3158c2ecf20Sopenharmony_ci perf_pmu_migrate_context(&h_gpci_pmu, cpu, target); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int hv_gpci_cpu_hotplug_init(void) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return cpuhp_setup_state(CPUHP_AP_PERF_POWERPC_HV_GPCI_ONLINE, 3238c2ecf20Sopenharmony_ci "perf/powerpc/hv_gcpi:online", 3248c2ecf20Sopenharmony_ci ppc_hv_gpci_cpu_online, 3258c2ecf20Sopenharmony_ci ppc_hv_gpci_cpu_offline); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int hv_gpci_init(void) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int r; 3318c2ecf20Sopenharmony_ci unsigned long hret; 3328c2ecf20Sopenharmony_ci struct hv_perf_caps caps; 3338c2ecf20Sopenharmony_ci struct hv_gpci_request_buffer *arg; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci hv_gpci_assert_offsets_correct(); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 3388c2ecf20Sopenharmony_ci pr_debug("not a virtualized system, not enabling\n"); 3398c2ecf20Sopenharmony_ci return -ENODEV; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci hret = hv_perf_caps_get(&caps); 3438c2ecf20Sopenharmony_ci if (hret) { 3448c2ecf20Sopenharmony_ci pr_debug("could not obtain capabilities, not enabling, rc=%ld\n", 3458c2ecf20Sopenharmony_ci hret); 3468c2ecf20Sopenharmony_ci return -ENODEV; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* init cpuhotplug */ 3508c2ecf20Sopenharmony_ci r = hv_gpci_cpu_hotplug_init(); 3518c2ecf20Sopenharmony_ci if (r) 3528c2ecf20Sopenharmony_ci return r; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* sampling not supported */ 3558c2ecf20Sopenharmony_ci h_gpci_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci arg = (void *)get_cpu_var(hv_gpci_reqb); 3588c2ecf20Sopenharmony_ci memset(arg, 0, HGPCI_REQ_BUFFER_SIZE); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * hcall H_GET_PERF_COUNTER_INFO populates the output 3628c2ecf20Sopenharmony_ci * counter_info_version value based on the system hypervisor. 3638c2ecf20Sopenharmony_ci * Pass the counter request 0x10 corresponds to request type 3648c2ecf20Sopenharmony_ci * 'Dispatch_timebase_by_processor', to get the supported 3658c2ecf20Sopenharmony_ci * counter_info_version. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci arg->params.counter_request = cpu_to_be32(0x10); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci r = plpar_hcall_norets(H_GET_PERF_COUNTER_INFO, 3708c2ecf20Sopenharmony_ci virt_to_phys(arg), HGPCI_REQ_BUFFER_SIZE); 3718c2ecf20Sopenharmony_ci if (r) { 3728c2ecf20Sopenharmony_ci pr_devel("hcall failed, can't get supported counter_info_version: 0x%x\n", r); 3738c2ecf20Sopenharmony_ci arg->params.counter_info_version_out = 0x8; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Use counter_info_version_out value to assign 3788c2ecf20Sopenharmony_ci * required hv-gpci event list. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci if (arg->params.counter_info_version_out >= 0x8) 3818c2ecf20Sopenharmony_ci event_group.attrs = hv_gpci_event_attrs; 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci event_group.attrs = hv_gpci_event_attrs_v6; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci put_cpu_var(hv_gpci_reqb); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci r = perf_pmu_register(&h_gpci_pmu, h_gpci_pmu.name, -1); 3888c2ecf20Sopenharmony_ci if (r) 3898c2ecf20Sopenharmony_ci return r; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cidevice_initcall(hv_gpci_init); 395