18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CAVIUM THUNDERX2 SoC PMU UNCORE 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Cavium Inc. 58c2ecf20Sopenharmony_ci * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/acpi.h> 98c2ecf20Sopenharmony_ci#include <linux/cpuhotplug.h> 108c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* Each ThunderX2(TX2) Socket has a L3C and DMC UNCORE PMU device. 148c2ecf20Sopenharmony_ci * Each UNCORE PMU device consists of 4 independent programmable counters. 158c2ecf20Sopenharmony_ci * Counters are 32 bit and do not support overflow interrupt, 168c2ecf20Sopenharmony_ci * they need to be sampled before overflow(i.e, at every 2 seconds). 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define TX2_PMU_DMC_L3C_MAX_COUNTERS 4 208c2ecf20Sopenharmony_ci#define TX2_PMU_CCPI2_MAX_COUNTERS 8 218c2ecf20Sopenharmony_ci#define TX2_PMU_MAX_COUNTERS TX2_PMU_CCPI2_MAX_COUNTERS 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define TX2_PMU_DMC_CHANNELS 8 258c2ecf20Sopenharmony_ci#define TX2_PMU_L3_TILES 16 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define TX2_PMU_HRTIMER_INTERVAL (2 * NSEC_PER_SEC) 288c2ecf20Sopenharmony_ci#define GET_EVENTID(ev, mask) ((ev->hw.config) & mask) 298c2ecf20Sopenharmony_ci#define GET_COUNTERID(ev, mask) ((ev->hw.idx) & mask) 308c2ecf20Sopenharmony_ci /* 1 byte per counter(4 counters). 318c2ecf20Sopenharmony_ci * Event id is encoded in bits [5:1] of a byte, 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define DMC_EVENT_CFG(idx, val) ((val) << (((idx) * 8) + 1)) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* bits[3:0] to select counters, are indexed from 8 to 15. */ 368c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_OFFSET 8 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define L3C_COUNTER_CTL 0xA8 398c2ecf20Sopenharmony_ci#define L3C_COUNTER_DATA 0xAC 408c2ecf20Sopenharmony_ci#define DMC_COUNTER_CTL 0x234 418c2ecf20Sopenharmony_ci#define DMC_COUNTER_DATA 0x240 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL 0x108 448c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_CTL 0x10C 458c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_SEL 0x12c 468c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_DATA_L 0x130 478c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_DATA_H 0x134 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* L3C event IDs */ 508c2ecf20Sopenharmony_ci#define L3_EVENT_READ_REQ 0xD 518c2ecf20Sopenharmony_ci#define L3_EVENT_WRITEBACK_REQ 0xE 528c2ecf20Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_REQ 0xF 538c2ecf20Sopenharmony_ci#define L3_EVENT_INV_REQ 0x10 548c2ecf20Sopenharmony_ci#define L3_EVENT_EVICT_REQ 0x13 558c2ecf20Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_HIT 0x14 568c2ecf20Sopenharmony_ci#define L3_EVENT_INV_HIT 0x15 578c2ecf20Sopenharmony_ci#define L3_EVENT_READ_HIT 0x17 588c2ecf20Sopenharmony_ci#define L3_EVENT_MAX 0x18 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* DMC event IDs */ 618c2ecf20Sopenharmony_ci#define DMC_EVENT_COUNT_CYCLES 0x1 628c2ecf20Sopenharmony_ci#define DMC_EVENT_WRITE_TXNS 0xB 638c2ecf20Sopenharmony_ci#define DMC_EVENT_DATA_TRANSFERS 0xD 648c2ecf20Sopenharmony_ci#define DMC_EVENT_READ_TXNS 0xF 658c2ecf20Sopenharmony_ci#define DMC_EVENT_MAX 0x10 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define CCPI2_EVENT_REQ_PKT_SENT 0x3D 688c2ecf20Sopenharmony_ci#define CCPI2_EVENT_SNOOP_PKT_SENT 0x65 698c2ecf20Sopenharmony_ci#define CCPI2_EVENT_DATA_PKT_SENT 0x105 708c2ecf20Sopenharmony_ci#define CCPI2_EVENT_GIC_PKT_SENT 0x12D 718c2ecf20Sopenharmony_ci#define CCPI2_EVENT_MAX 0x200 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_ENABLE BIT(0) 748c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_START BIT(1) 758c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_RESET BIT(4) 768c2ecf20Sopenharmony_ci#define CCPI2_EVENT_LEVEL_RISING_EDGE BIT(10) 778c2ecf20Sopenharmony_ci#define CCPI2_EVENT_TYPE_EDGE_SENSITIVE BIT(11) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cienum tx2_uncore_type { 808c2ecf20Sopenharmony_ci PMU_TYPE_L3C, 818c2ecf20Sopenharmony_ci PMU_TYPE_DMC, 828c2ecf20Sopenharmony_ci PMU_TYPE_CCPI2, 838c2ecf20Sopenharmony_ci PMU_TYPE_INVALID, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Each socket has 3 uncore devices associated with a PMU. The DMC and 888c2ecf20Sopenharmony_ci * L3C have 4 32-bit counters and the CCPI2 has 8 64-bit counters. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistruct tx2_uncore_pmu { 918c2ecf20Sopenharmony_ci struct hlist_node hpnode; 928c2ecf20Sopenharmony_ci struct list_head entry; 938c2ecf20Sopenharmony_ci struct pmu pmu; 948c2ecf20Sopenharmony_ci char *name; 958c2ecf20Sopenharmony_ci int node; 968c2ecf20Sopenharmony_ci int cpu; 978c2ecf20Sopenharmony_ci u32 max_counters; 988c2ecf20Sopenharmony_ci u32 counters_mask; 998c2ecf20Sopenharmony_ci u32 prorate_factor; 1008c2ecf20Sopenharmony_ci u32 max_events; 1018c2ecf20Sopenharmony_ci u32 events_mask; 1028c2ecf20Sopenharmony_ci u64 hrtimer_interval; 1038c2ecf20Sopenharmony_ci void __iomem *base; 1048c2ecf20Sopenharmony_ci DECLARE_BITMAP(active_counters, TX2_PMU_MAX_COUNTERS); 1058c2ecf20Sopenharmony_ci struct perf_event *events[TX2_PMU_MAX_COUNTERS]; 1068c2ecf20Sopenharmony_ci struct device *dev; 1078c2ecf20Sopenharmony_ci struct hrtimer hrtimer; 1088c2ecf20Sopenharmony_ci const struct attribute_group **attr_groups; 1098c2ecf20Sopenharmony_ci enum tx2_uncore_type type; 1108c2ecf20Sopenharmony_ci enum hrtimer_restart (*hrtimer_callback)(struct hrtimer *cb); 1118c2ecf20Sopenharmony_ci void (*init_cntr_base)(struct perf_event *event, 1128c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu); 1138c2ecf20Sopenharmony_ci void (*stop_event)(struct perf_event *event); 1148c2ecf20Sopenharmony_ci void (*start_event)(struct perf_event *event, int flags); 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic LIST_HEAD(tx2_pmus); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline struct tx2_uncore_pmu *pmu_to_tx2_pmu(struct pmu *pmu) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return container_of(pmu, struct tx2_uncore_pmu, pmu); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define TX2_PMU_FORMAT_ATTR(_var, _name, _format) \ 1258c2ecf20Sopenharmony_cistatic ssize_t \ 1268c2ecf20Sopenharmony_ci__tx2_pmu_##_var##_show(struct device *dev, \ 1278c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 1288c2ecf20Sopenharmony_ci char *page) \ 1298c2ecf20Sopenharmony_ci{ \ 1308c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \ 1318c2ecf20Sopenharmony_ci return sprintf(page, _format "\n"); \ 1328c2ecf20Sopenharmony_ci} \ 1338c2ecf20Sopenharmony_ci \ 1348c2ecf20Sopenharmony_cistatic struct device_attribute format_attr_##_var = \ 1358c2ecf20Sopenharmony_ci __ATTR(_name, 0444, __tx2_pmu_##_var##_show, NULL) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event, event, "config:0-4"); 1388c2ecf20Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event_ccpi2, event, "config:0-9"); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct attribute *l3c_pmu_format_attrs[] = { 1418c2ecf20Sopenharmony_ci &format_attr_event.attr, 1428c2ecf20Sopenharmony_ci NULL, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct attribute *dmc_pmu_format_attrs[] = { 1468c2ecf20Sopenharmony_ci &format_attr_event.attr, 1478c2ecf20Sopenharmony_ci NULL, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct attribute *ccpi2_pmu_format_attrs[] = { 1518c2ecf20Sopenharmony_ci &format_attr_event_ccpi2.attr, 1528c2ecf20Sopenharmony_ci NULL, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct attribute_group l3c_pmu_format_attr_group = { 1568c2ecf20Sopenharmony_ci .name = "format", 1578c2ecf20Sopenharmony_ci .attrs = l3c_pmu_format_attrs, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct attribute_group dmc_pmu_format_attr_group = { 1618c2ecf20Sopenharmony_ci .name = "format", 1628c2ecf20Sopenharmony_ci .attrs = dmc_pmu_format_attrs, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_format_attr_group = { 1668c2ecf20Sopenharmony_ci .name = "format", 1678c2ecf20Sopenharmony_ci .attrs = ccpi2_pmu_format_attrs, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * sysfs event attributes 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic ssize_t tx2_pmu_event_show(struct device *dev, 1748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct dev_ext_attribute *eattr; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci eattr = container_of(attr, struct dev_ext_attribute, attr); 1798c2ecf20Sopenharmony_ci return sprintf(buf, "event=0x%lx\n", (unsigned long) eattr->var); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define TX2_EVENT_ATTR(name, config) \ 1838c2ecf20Sopenharmony_ci PMU_EVENT_ATTR(name, tx2_pmu_event_attr_##name, \ 1848c2ecf20Sopenharmony_ci config, tx2_pmu_event_show) 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_request, L3_EVENT_READ_REQ); 1878c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(writeback_request, L3_EVENT_WRITEBACK_REQ); 1888c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_request, L3_EVENT_INV_N_WRITE_REQ); 1898c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_request, L3_EVENT_INV_REQ); 1908c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(evict_request, L3_EVENT_EVICT_REQ); 1918c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_hit, L3_EVENT_INV_N_WRITE_HIT); 1928c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_hit, L3_EVENT_INV_HIT); 1938c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_hit, L3_EVENT_READ_HIT); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct attribute *l3c_pmu_events_attrs[] = { 1968c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_read_request.attr.attr, 1978c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_writeback_request.attr.attr, 1988c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_inv_nwrite_request.attr.attr, 1998c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_inv_request.attr.attr, 2008c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_evict_request.attr.attr, 2018c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_inv_nwrite_hit.attr.attr, 2028c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_inv_hit.attr.attr, 2038c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_read_hit.attr.attr, 2048c2ecf20Sopenharmony_ci NULL, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(cnt_cycles, DMC_EVENT_COUNT_CYCLES); 2088c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(write_txns, DMC_EVENT_WRITE_TXNS); 2098c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(data_transfers, DMC_EVENT_DATA_TRANSFERS); 2108c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_txns, DMC_EVENT_READ_TXNS); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic struct attribute *dmc_pmu_events_attrs[] = { 2138c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_cnt_cycles.attr.attr, 2148c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_write_txns.attr.attr, 2158c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_data_transfers.attr.attr, 2168c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_read_txns.attr.attr, 2178c2ecf20Sopenharmony_ci NULL, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(req_pktsent, CCPI2_EVENT_REQ_PKT_SENT); 2218c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(snoop_pktsent, CCPI2_EVENT_SNOOP_PKT_SENT); 2228c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(data_pktsent, CCPI2_EVENT_DATA_PKT_SENT); 2238c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(gic_pktsent, CCPI2_EVENT_GIC_PKT_SENT); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic struct attribute *ccpi2_pmu_events_attrs[] = { 2268c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_req_pktsent.attr.attr, 2278c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_snoop_pktsent.attr.attr, 2288c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_data_pktsent.attr.attr, 2298c2ecf20Sopenharmony_ci &tx2_pmu_event_attr_gic_pktsent.attr.attr, 2308c2ecf20Sopenharmony_ci NULL, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const struct attribute_group l3c_pmu_events_attr_group = { 2348c2ecf20Sopenharmony_ci .name = "events", 2358c2ecf20Sopenharmony_ci .attrs = l3c_pmu_events_attrs, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic const struct attribute_group dmc_pmu_events_attr_group = { 2398c2ecf20Sopenharmony_ci .name = "events", 2408c2ecf20Sopenharmony_ci .attrs = dmc_pmu_events_attrs, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_events_attr_group = { 2448c2ecf20Sopenharmony_ci .name = "events", 2458c2ecf20Sopenharmony_ci .attrs = ccpi2_pmu_events_attrs, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * sysfs cpumask attributes 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, 2528c2ecf20Sopenharmony_ci char *buf) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(dev_get_drvdata(dev)); 2578c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask_of(tx2_pmu->cpu)); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic struct attribute *tx2_pmu_cpumask_attrs[] = { 2628c2ecf20Sopenharmony_ci &dev_attr_cpumask.attr, 2638c2ecf20Sopenharmony_ci NULL, 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const struct attribute_group pmu_cpumask_attr_group = { 2678c2ecf20Sopenharmony_ci .attrs = tx2_pmu_cpumask_attrs, 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * Per PMU device attribute groups 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic const struct attribute_group *l3c_pmu_attr_groups[] = { 2748c2ecf20Sopenharmony_ci &l3c_pmu_format_attr_group, 2758c2ecf20Sopenharmony_ci &pmu_cpumask_attr_group, 2768c2ecf20Sopenharmony_ci &l3c_pmu_events_attr_group, 2778c2ecf20Sopenharmony_ci NULL 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct attribute_group *dmc_pmu_attr_groups[] = { 2818c2ecf20Sopenharmony_ci &dmc_pmu_format_attr_group, 2828c2ecf20Sopenharmony_ci &pmu_cpumask_attr_group, 2838c2ecf20Sopenharmony_ci &dmc_pmu_events_attr_group, 2848c2ecf20Sopenharmony_ci NULL 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic const struct attribute_group *ccpi2_pmu_attr_groups[] = { 2888c2ecf20Sopenharmony_ci &ccpi2_pmu_format_attr_group, 2898c2ecf20Sopenharmony_ci &pmu_cpumask_attr_group, 2908c2ecf20Sopenharmony_ci &ccpi2_pmu_events_attr_group, 2918c2ecf20Sopenharmony_ci NULL 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic inline u32 reg_readl(unsigned long addr) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci return readl((void __iomem *)addr); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic inline void reg_writel(u32 val, unsigned long addr) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci writel(val, (void __iomem *)addr); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int alloc_counter(struct tx2_uncore_pmu *tx2_pmu) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int counter; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci counter = find_first_zero_bit(tx2_pmu->active_counters, 3098c2ecf20Sopenharmony_ci tx2_pmu->max_counters); 3108c2ecf20Sopenharmony_ci if (counter == tx2_pmu->max_counters) 3118c2ecf20Sopenharmony_ci return -ENOSPC; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci set_bit(counter, tx2_pmu->active_counters); 3148c2ecf20Sopenharmony_ci return counter; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic inline void free_counter(struct tx2_uncore_pmu *tx2_pmu, int counter) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci clear_bit(counter, tx2_pmu->active_counters); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void init_cntr_base_l3c(struct perf_event *event, 3238c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 3268c2ecf20Sopenharmony_ci u32 cmask; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 3298c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* counter ctrl/data reg offset at 8 */ 3328c2ecf20Sopenharmony_ci hwc->config_base = (unsigned long)tx2_pmu->base 3338c2ecf20Sopenharmony_ci + L3C_COUNTER_CTL + (8 * GET_COUNTERID(event, cmask)); 3348c2ecf20Sopenharmony_ci hwc->event_base = (unsigned long)tx2_pmu->base 3358c2ecf20Sopenharmony_ci + L3C_COUNTER_DATA + (8 * GET_COUNTERID(event, cmask)); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void init_cntr_base_dmc(struct perf_event *event, 3398c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 3428c2ecf20Sopenharmony_ci u32 cmask; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 3458c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci hwc->config_base = (unsigned long)tx2_pmu->base 3488c2ecf20Sopenharmony_ci + DMC_COUNTER_CTL; 3498c2ecf20Sopenharmony_ci /* counter data reg offset at 0xc */ 3508c2ecf20Sopenharmony_ci hwc->event_base = (unsigned long)tx2_pmu->base 3518c2ecf20Sopenharmony_ci + DMC_COUNTER_DATA + (0xc * GET_COUNTERID(event, cmask)); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void init_cntr_base_ccpi2(struct perf_event *event, 3558c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 3588c2ecf20Sopenharmony_ci u32 cmask; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci hwc->config_base = (unsigned long)tx2_pmu->base 3638c2ecf20Sopenharmony_ci + CCPI2_COUNTER_CTL + (4 * GET_COUNTERID(event, cmask)); 3648c2ecf20Sopenharmony_ci hwc->event_base = (unsigned long)tx2_pmu->base; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void uncore_start_event_l3c(struct perf_event *event, int flags) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci u32 val, emask; 3708c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 3718c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 3748c2ecf20Sopenharmony_ci emask = tx2_pmu->events_mask; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* event id encoded in bits [07:03] */ 3778c2ecf20Sopenharmony_ci val = GET_EVENTID(event, emask) << 3; 3788c2ecf20Sopenharmony_ci reg_writel(val, hwc->config_base); 3798c2ecf20Sopenharmony_ci local64_set(&hwc->prev_count, 0); 3808c2ecf20Sopenharmony_ci reg_writel(0, hwc->event_base); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic inline void uncore_stop_event_l3c(struct perf_event *event) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci reg_writel(0, event->hw.config_base); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void uncore_start_event_dmc(struct perf_event *event, int flags) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci u32 val, cmask, emask; 3918c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 3928c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 3938c2ecf20Sopenharmony_ci int idx, event_id; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 3968c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 3978c2ecf20Sopenharmony_ci emask = tx2_pmu->events_mask; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci idx = GET_COUNTERID(event, cmask); 4008c2ecf20Sopenharmony_ci event_id = GET_EVENTID(event, emask); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* enable and start counters. 4038c2ecf20Sopenharmony_ci * 8 bits for each counter, bits[05:01] of a counter to set event type. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci val = reg_readl(hwc->config_base); 4068c2ecf20Sopenharmony_ci val &= ~DMC_EVENT_CFG(idx, 0x1f); 4078c2ecf20Sopenharmony_ci val |= DMC_EVENT_CFG(idx, event_id); 4088c2ecf20Sopenharmony_ci reg_writel(val, hwc->config_base); 4098c2ecf20Sopenharmony_ci local64_set(&hwc->prev_count, 0); 4108c2ecf20Sopenharmony_ci reg_writel(0, hwc->event_base); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void uncore_stop_event_dmc(struct perf_event *event) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci u32 val, cmask; 4168c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 4178c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 4188c2ecf20Sopenharmony_ci int idx; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 4218c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 4228c2ecf20Sopenharmony_ci idx = GET_COUNTERID(event, cmask); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* clear event type(bits[05:01]) to stop counter */ 4258c2ecf20Sopenharmony_ci val = reg_readl(hwc->config_base); 4268c2ecf20Sopenharmony_ci val &= ~DMC_EVENT_CFG(idx, 0x1f); 4278c2ecf20Sopenharmony_ci reg_writel(val, hwc->config_base); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void uncore_start_event_ccpi2(struct perf_event *event, int flags) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u32 emask; 4338c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 4348c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 4378c2ecf20Sopenharmony_ci emask = tx2_pmu->events_mask; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* Bit [09:00] to set event id. 4408c2ecf20Sopenharmony_ci * Bits [10], set level to rising edge. 4418c2ecf20Sopenharmony_ci * Bits [11], set type to edge sensitive. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ci reg_writel((CCPI2_EVENT_TYPE_EDGE_SENSITIVE | 4448c2ecf20Sopenharmony_ci CCPI2_EVENT_LEVEL_RISING_EDGE | 4458c2ecf20Sopenharmony_ci GET_EVENTID(event, emask)), hwc->config_base); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* reset[4], enable[0] and start[1] counters */ 4488c2ecf20Sopenharmony_ci reg_writel(CCPI2_PERF_CTL_RESET | 4498c2ecf20Sopenharmony_ci CCPI2_PERF_CTL_START | 4508c2ecf20Sopenharmony_ci CCPI2_PERF_CTL_ENABLE, 4518c2ecf20Sopenharmony_ci hwc->event_base + CCPI2_PERF_CTL); 4528c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, 0ULL); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void uncore_stop_event_ccpi2(struct perf_event *event) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* disable and stop counter */ 4608c2ecf20Sopenharmony_ci reg_writel(0, hwc->event_base + CCPI2_PERF_CTL); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void tx2_uncore_event_update(struct perf_event *event) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci u64 prev, delta, new = 0; 4668c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 4678c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 4688c2ecf20Sopenharmony_ci enum tx2_uncore_type type; 4698c2ecf20Sopenharmony_ci u32 prorate_factor; 4708c2ecf20Sopenharmony_ci u32 cmask, emask; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 4738c2ecf20Sopenharmony_ci type = tx2_pmu->type; 4748c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 4758c2ecf20Sopenharmony_ci emask = tx2_pmu->events_mask; 4768c2ecf20Sopenharmony_ci prorate_factor = tx2_pmu->prorate_factor; 4778c2ecf20Sopenharmony_ci if (type == PMU_TYPE_CCPI2) { 4788c2ecf20Sopenharmony_ci reg_writel(CCPI2_COUNTER_OFFSET + 4798c2ecf20Sopenharmony_ci GET_COUNTERID(event, cmask), 4808c2ecf20Sopenharmony_ci hwc->event_base + CCPI2_COUNTER_SEL); 4818c2ecf20Sopenharmony_ci new = reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_H); 4828c2ecf20Sopenharmony_ci new = (new << 32) + 4838c2ecf20Sopenharmony_ci reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_L); 4848c2ecf20Sopenharmony_ci prev = local64_xchg(&hwc->prev_count, new); 4858c2ecf20Sopenharmony_ci delta = new - prev; 4868c2ecf20Sopenharmony_ci } else { 4878c2ecf20Sopenharmony_ci new = reg_readl(hwc->event_base); 4888c2ecf20Sopenharmony_ci prev = local64_xchg(&hwc->prev_count, new); 4898c2ecf20Sopenharmony_ci /* handles rollover of 32 bit counter */ 4908c2ecf20Sopenharmony_ci delta = (u32)(((1UL << 32) - prev) + new); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* DMC event data_transfers granularity is 16 Bytes, convert it to 64 */ 4948c2ecf20Sopenharmony_ci if (type == PMU_TYPE_DMC && 4958c2ecf20Sopenharmony_ci GET_EVENTID(event, emask) == DMC_EVENT_DATA_TRANSFERS) 4968c2ecf20Sopenharmony_ci delta = delta/4; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* L3C and DMC has 16 and 8 interleave channels respectively. 4998c2ecf20Sopenharmony_ci * The sampled value is for channel 0 and multiplied with 5008c2ecf20Sopenharmony_ci * prorate_factor to get the count for a device. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci local64_add(delta * prorate_factor, &event->count); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic enum tx2_uncore_type get_tx2_pmu_type(struct acpi_device *adev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci int i = 0; 5088c2ecf20Sopenharmony_ci struct acpi_tx2_pmu_device { 5098c2ecf20Sopenharmony_ci __u8 id[ACPI_ID_LEN]; 5108c2ecf20Sopenharmony_ci enum tx2_uncore_type type; 5118c2ecf20Sopenharmony_ci } devices[] = { 5128c2ecf20Sopenharmony_ci {"CAV901D", PMU_TYPE_L3C}, 5138c2ecf20Sopenharmony_ci {"CAV901F", PMU_TYPE_DMC}, 5148c2ecf20Sopenharmony_ci {"CAV901E", PMU_TYPE_CCPI2}, 5158c2ecf20Sopenharmony_ci {"", PMU_TYPE_INVALID} 5168c2ecf20Sopenharmony_ci }; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci while (devices[i].type != PMU_TYPE_INVALID) { 5198c2ecf20Sopenharmony_ci if (!strcmp(acpi_device_hid(adev), devices[i].id)) 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci i++; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return devices[i].type; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic bool tx2_uncore_validate_event(struct pmu *pmu, 5288c2ecf20Sopenharmony_ci struct perf_event *event, int *counters) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci if (is_software_event(event)) 5318c2ecf20Sopenharmony_ci return true; 5328c2ecf20Sopenharmony_ci /* Reject groups spanning multiple HW PMUs. */ 5338c2ecf20Sopenharmony_ci if (event->pmu != pmu) 5348c2ecf20Sopenharmony_ci return false; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci *counters = *counters + 1; 5378c2ecf20Sopenharmony_ci return true; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* 5418c2ecf20Sopenharmony_ci * Make sure the group of events can be scheduled at once 5428c2ecf20Sopenharmony_ci * on the PMU. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_cistatic bool tx2_uncore_validate_event_group(struct perf_event *event, 5458c2ecf20Sopenharmony_ci int max_counters) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct perf_event *sibling, *leader = event->group_leader; 5488c2ecf20Sopenharmony_ci int counters = 0; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (event->group_leader == event) 5518c2ecf20Sopenharmony_ci return true; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!tx2_uncore_validate_event(event->pmu, leader, &counters)) 5548c2ecf20Sopenharmony_ci return false; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for_each_sibling_event(sibling, leader) { 5578c2ecf20Sopenharmony_ci if (!tx2_uncore_validate_event(event->pmu, sibling, &counters)) 5588c2ecf20Sopenharmony_ci return false; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!tx2_uncore_validate_event(event->pmu, event, &counters)) 5628c2ecf20Sopenharmony_ci return false; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* 5658c2ecf20Sopenharmony_ci * If the group requires more counters than the HW has, 5668c2ecf20Sopenharmony_ci * it cannot ever be scheduled. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci return counters <= max_counters; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic int tx2_uncore_event_init(struct perf_event *event) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 5758c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Test the event attr type check for PMU enumeration */ 5788c2ecf20Sopenharmony_ci if (event->attr.type != event->pmu->type) 5798c2ecf20Sopenharmony_ci return -ENOENT; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * SOC PMU counters are shared across all cores. 5838c2ecf20Sopenharmony_ci * Therefore, it does not support per-process mode. 5848c2ecf20Sopenharmony_ci * Also, it does not support event sampling mode. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) 5878c2ecf20Sopenharmony_ci return -EINVAL; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (event->cpu < 0) 5908c2ecf20Sopenharmony_ci return -EINVAL; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 5938c2ecf20Sopenharmony_ci if (tx2_pmu->cpu >= nr_cpu_ids) 5948c2ecf20Sopenharmony_ci return -EINVAL; 5958c2ecf20Sopenharmony_ci event->cpu = tx2_pmu->cpu; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (event->attr.config >= tx2_pmu->max_events) 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* store event id */ 6018c2ecf20Sopenharmony_ci hwc->config = event->attr.config; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Validate the group */ 6048c2ecf20Sopenharmony_ci if (!tx2_uncore_validate_event_group(event, tx2_pmu->max_counters)) 6058c2ecf20Sopenharmony_ci return -EINVAL; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic void tx2_uncore_event_start(struct perf_event *event, int flags) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 6138c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci hwc->state = 0; 6168c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci tx2_pmu->start_event(event, flags); 6198c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* No hrtimer needed for CCPI2, 64-bit counters */ 6228c2ecf20Sopenharmony_ci if (!tx2_pmu->hrtimer_callback) 6238c2ecf20Sopenharmony_ci return; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Start timer for first event */ 6268c2ecf20Sopenharmony_ci if (bitmap_weight(tx2_pmu->active_counters, 6278c2ecf20Sopenharmony_ci tx2_pmu->max_counters) == 1) { 6288c2ecf20Sopenharmony_ci hrtimer_start(&tx2_pmu->hrtimer, 6298c2ecf20Sopenharmony_ci ns_to_ktime(tx2_pmu->hrtimer_interval), 6308c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_PINNED); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic void tx2_uncore_event_stop(struct perf_event *event, int flags) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 6378c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (hwc->state & PERF_HES_UPTODATE) 6408c2ecf20Sopenharmony_ci return; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 6438c2ecf20Sopenharmony_ci tx2_pmu->stop_event(event); 6448c2ecf20Sopenharmony_ci WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); 6458c2ecf20Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 6468c2ecf20Sopenharmony_ci if (flags & PERF_EF_UPDATE) { 6478c2ecf20Sopenharmony_ci tx2_uncore_event_update(event); 6488c2ecf20Sopenharmony_ci hwc->state |= PERF_HES_UPTODATE; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int tx2_uncore_event_add(struct perf_event *event, int flags) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 6558c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci tx2_pmu = pmu_to_tx2_pmu(event->pmu); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Allocate a free counter */ 6608c2ecf20Sopenharmony_ci hwc->idx = alloc_counter(tx2_pmu); 6618c2ecf20Sopenharmony_ci if (hwc->idx < 0) 6628c2ecf20Sopenharmony_ci return -EAGAIN; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci tx2_pmu->events[hwc->idx] = event; 6658c2ecf20Sopenharmony_ci /* set counter control and data registers base address */ 6668c2ecf20Sopenharmony_ci tx2_pmu->init_cntr_base(event, tx2_pmu); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 6698c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 6708c2ecf20Sopenharmony_ci tx2_uncore_event_start(event, flags); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic void tx2_uncore_event_del(struct perf_event *event, int flags) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu = pmu_to_tx2_pmu(event->pmu); 6788c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 6798c2ecf20Sopenharmony_ci u32 cmask; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci cmask = tx2_pmu->counters_mask; 6828c2ecf20Sopenharmony_ci tx2_uncore_event_stop(event, PERF_EF_UPDATE); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* clear the assigned counter */ 6858c2ecf20Sopenharmony_ci free_counter(tx2_pmu, GET_COUNTERID(event, cmask)); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 6888c2ecf20Sopenharmony_ci tx2_pmu->events[hwc->idx] = NULL; 6898c2ecf20Sopenharmony_ci hwc->idx = -1; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (!tx2_pmu->hrtimer_callback) 6928c2ecf20Sopenharmony_ci return; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (bitmap_empty(tx2_pmu->active_counters, tx2_pmu->max_counters)) 6958c2ecf20Sopenharmony_ci hrtimer_cancel(&tx2_pmu->hrtimer); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic void tx2_uncore_event_read(struct perf_event *event) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci tx2_uncore_event_update(event); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic enum hrtimer_restart tx2_hrtimer_callback(struct hrtimer *timer) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 7068c2ecf20Sopenharmony_ci int max_counters, idx; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci tx2_pmu = container_of(timer, struct tx2_uncore_pmu, hrtimer); 7098c2ecf20Sopenharmony_ci max_counters = tx2_pmu->max_counters; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (bitmap_empty(tx2_pmu->active_counters, max_counters)) 7128c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci for_each_set_bit(idx, tx2_pmu->active_counters, max_counters) { 7158c2ecf20Sopenharmony_ci struct perf_event *event = tx2_pmu->events[idx]; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci tx2_uncore_event_update(event); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci hrtimer_forward_now(timer, ns_to_ktime(tx2_pmu->hrtimer_interval)); 7208c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_register( 7248c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct device *dev = tx2_pmu->dev; 7278c2ecf20Sopenharmony_ci char *name = tx2_pmu->name; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Perf event registration */ 7308c2ecf20Sopenharmony_ci tx2_pmu->pmu = (struct pmu) { 7318c2ecf20Sopenharmony_ci .module = THIS_MODULE, 7328c2ecf20Sopenharmony_ci .attr_groups = tx2_pmu->attr_groups, 7338c2ecf20Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 7348c2ecf20Sopenharmony_ci .event_init = tx2_uncore_event_init, 7358c2ecf20Sopenharmony_ci .add = tx2_uncore_event_add, 7368c2ecf20Sopenharmony_ci .del = tx2_uncore_event_del, 7378c2ecf20Sopenharmony_ci .start = tx2_uncore_event_start, 7388c2ecf20Sopenharmony_ci .stop = tx2_uncore_event_stop, 7398c2ecf20Sopenharmony_ci .read = tx2_uncore_event_read, 7408c2ecf20Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 7418c2ecf20Sopenharmony_ci }; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci tx2_pmu->pmu.name = devm_kasprintf(dev, GFP_KERNEL, 7448c2ecf20Sopenharmony_ci "%s", name); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return perf_pmu_register(&tx2_pmu->pmu, tx2_pmu->pmu.name, -1); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_add_dev(struct tx2_uncore_pmu *tx2_pmu) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci int ret, cpu; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci cpu = cpumask_any_and(cpumask_of_node(tx2_pmu->node), 7548c2ecf20Sopenharmony_ci cpu_online_mask); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci tx2_pmu->cpu = cpu; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (tx2_pmu->hrtimer_callback) { 7598c2ecf20Sopenharmony_ci hrtimer_init(&tx2_pmu->hrtimer, 7608c2ecf20Sopenharmony_ci CLOCK_MONOTONIC, HRTIMER_MODE_REL); 7618c2ecf20Sopenharmony_ci tx2_pmu->hrtimer.function = tx2_pmu->hrtimer_callback; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci ret = tx2_uncore_pmu_register(tx2_pmu); 7658c2ecf20Sopenharmony_ci if (ret) { 7668c2ecf20Sopenharmony_ci dev_err(tx2_pmu->dev, "%s PMU: Failed to init driver\n", 7678c2ecf20Sopenharmony_ci tx2_pmu->name); 7688c2ecf20Sopenharmony_ci return -ENODEV; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* register hotplug callback for the pmu */ 7728c2ecf20Sopenharmony_ci ret = cpuhp_state_add_instance( 7738c2ecf20Sopenharmony_ci CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE, 7748c2ecf20Sopenharmony_ci &tx2_pmu->hpnode); 7758c2ecf20Sopenharmony_ci if (ret) { 7768c2ecf20Sopenharmony_ci dev_err(tx2_pmu->dev, "Error %d registering hotplug", ret); 7778c2ecf20Sopenharmony_ci return ret; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Add to list */ 7818c2ecf20Sopenharmony_ci list_add(&tx2_pmu->entry, &tx2_pmus); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci dev_dbg(tx2_pmu->dev, "%s PMU UNCORE registered\n", 7848c2ecf20Sopenharmony_ci tx2_pmu->pmu.name); 7858c2ecf20Sopenharmony_ci return ret; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic struct tx2_uncore_pmu *tx2_uncore_pmu_init_dev(struct device *dev, 7898c2ecf20Sopenharmony_ci acpi_handle handle, struct acpi_device *adev, u32 type) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 7928c2ecf20Sopenharmony_ci void __iomem *base; 7938c2ecf20Sopenharmony_ci struct resource res; 7948c2ecf20Sopenharmony_ci struct resource_entry *rentry; 7958c2ecf20Sopenharmony_ci struct list_head list; 7968c2ecf20Sopenharmony_ci int ret; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 7998c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &list, NULL, NULL); 8008c2ecf20Sopenharmony_ci if (ret <= 0) { 8018c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse _CRS method, error %d\n", ret); 8028c2ecf20Sopenharmony_ci return NULL; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci list_for_each_entry(rentry, &list, node) { 8068c2ecf20Sopenharmony_ci if (resource_type(rentry->res) == IORESOURCE_MEM) { 8078c2ecf20Sopenharmony_ci res = *rentry->res; 8088c2ecf20Sopenharmony_ci rentry = NULL; 8098c2ecf20Sopenharmony_ci break; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&list); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (rentry) { 8158c2ecf20Sopenharmony_ci dev_err(dev, "PMU type %d: Fail to find resource\n", type); 8168c2ecf20Sopenharmony_ci return NULL; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci base = devm_ioremap_resource(dev, &res); 8208c2ecf20Sopenharmony_ci if (IS_ERR(base)) { 8218c2ecf20Sopenharmony_ci dev_err(dev, "PMU type %d: Fail to map resource\n", type); 8228c2ecf20Sopenharmony_ci return NULL; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci tx2_pmu = devm_kzalloc(dev, sizeof(*tx2_pmu), GFP_KERNEL); 8268c2ecf20Sopenharmony_ci if (!tx2_pmu) 8278c2ecf20Sopenharmony_ci return NULL; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci tx2_pmu->dev = dev; 8308c2ecf20Sopenharmony_ci tx2_pmu->type = type; 8318c2ecf20Sopenharmony_ci tx2_pmu->base = base; 8328c2ecf20Sopenharmony_ci tx2_pmu->node = dev_to_node(dev); 8338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tx2_pmu->entry); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci switch (tx2_pmu->type) { 8368c2ecf20Sopenharmony_ci case PMU_TYPE_L3C: 8378c2ecf20Sopenharmony_ci tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS; 8388c2ecf20Sopenharmony_ci tx2_pmu->counters_mask = 0x3; 8398c2ecf20Sopenharmony_ci tx2_pmu->prorate_factor = TX2_PMU_L3_TILES; 8408c2ecf20Sopenharmony_ci tx2_pmu->max_events = L3_EVENT_MAX; 8418c2ecf20Sopenharmony_ci tx2_pmu->events_mask = 0x1f; 8428c2ecf20Sopenharmony_ci tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL; 8438c2ecf20Sopenharmony_ci tx2_pmu->hrtimer_callback = tx2_hrtimer_callback; 8448c2ecf20Sopenharmony_ci tx2_pmu->attr_groups = l3c_pmu_attr_groups; 8458c2ecf20Sopenharmony_ci tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL, 8468c2ecf20Sopenharmony_ci "uncore_l3c_%d", tx2_pmu->node); 8478c2ecf20Sopenharmony_ci tx2_pmu->init_cntr_base = init_cntr_base_l3c; 8488c2ecf20Sopenharmony_ci tx2_pmu->start_event = uncore_start_event_l3c; 8498c2ecf20Sopenharmony_ci tx2_pmu->stop_event = uncore_stop_event_l3c; 8508c2ecf20Sopenharmony_ci break; 8518c2ecf20Sopenharmony_ci case PMU_TYPE_DMC: 8528c2ecf20Sopenharmony_ci tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS; 8538c2ecf20Sopenharmony_ci tx2_pmu->counters_mask = 0x3; 8548c2ecf20Sopenharmony_ci tx2_pmu->prorate_factor = TX2_PMU_DMC_CHANNELS; 8558c2ecf20Sopenharmony_ci tx2_pmu->max_events = DMC_EVENT_MAX; 8568c2ecf20Sopenharmony_ci tx2_pmu->events_mask = 0x1f; 8578c2ecf20Sopenharmony_ci tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL; 8588c2ecf20Sopenharmony_ci tx2_pmu->hrtimer_callback = tx2_hrtimer_callback; 8598c2ecf20Sopenharmony_ci tx2_pmu->attr_groups = dmc_pmu_attr_groups; 8608c2ecf20Sopenharmony_ci tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL, 8618c2ecf20Sopenharmony_ci "uncore_dmc_%d", tx2_pmu->node); 8628c2ecf20Sopenharmony_ci tx2_pmu->init_cntr_base = init_cntr_base_dmc; 8638c2ecf20Sopenharmony_ci tx2_pmu->start_event = uncore_start_event_dmc; 8648c2ecf20Sopenharmony_ci tx2_pmu->stop_event = uncore_stop_event_dmc; 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci case PMU_TYPE_CCPI2: 8678c2ecf20Sopenharmony_ci /* CCPI2 has 8 counters */ 8688c2ecf20Sopenharmony_ci tx2_pmu->max_counters = TX2_PMU_CCPI2_MAX_COUNTERS; 8698c2ecf20Sopenharmony_ci tx2_pmu->counters_mask = 0x7; 8708c2ecf20Sopenharmony_ci tx2_pmu->prorate_factor = 1; 8718c2ecf20Sopenharmony_ci tx2_pmu->max_events = CCPI2_EVENT_MAX; 8728c2ecf20Sopenharmony_ci tx2_pmu->events_mask = 0x1ff; 8738c2ecf20Sopenharmony_ci tx2_pmu->attr_groups = ccpi2_pmu_attr_groups; 8748c2ecf20Sopenharmony_ci tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL, 8758c2ecf20Sopenharmony_ci "uncore_ccpi2_%d", tx2_pmu->node); 8768c2ecf20Sopenharmony_ci tx2_pmu->init_cntr_base = init_cntr_base_ccpi2; 8778c2ecf20Sopenharmony_ci tx2_pmu->start_event = uncore_start_event_ccpi2; 8788c2ecf20Sopenharmony_ci tx2_pmu->stop_event = uncore_stop_event_ccpi2; 8798c2ecf20Sopenharmony_ci tx2_pmu->hrtimer_callback = NULL; 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci case PMU_TYPE_INVALID: 8828c2ecf20Sopenharmony_ci devm_kfree(dev, tx2_pmu); 8838c2ecf20Sopenharmony_ci return NULL; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return tx2_pmu; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic acpi_status tx2_uncore_pmu_add(acpi_handle handle, u32 level, 8908c2ecf20Sopenharmony_ci void *data, void **return_value) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 8938c2ecf20Sopenharmony_ci struct acpi_device *adev; 8948c2ecf20Sopenharmony_ci enum tx2_uncore_type type; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (acpi_bus_get_device(handle, &adev)) 8978c2ecf20Sopenharmony_ci return AE_OK; 8988c2ecf20Sopenharmony_ci if (acpi_bus_get_status(adev) || !adev->status.present) 8998c2ecf20Sopenharmony_ci return AE_OK; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci type = get_tx2_pmu_type(adev); 9028c2ecf20Sopenharmony_ci if (type == PMU_TYPE_INVALID) 9038c2ecf20Sopenharmony_ci return AE_OK; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci tx2_pmu = tx2_uncore_pmu_init_dev((struct device *)data, 9068c2ecf20Sopenharmony_ci handle, adev, type); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (!tx2_pmu) 9098c2ecf20Sopenharmony_ci return AE_ERROR; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (tx2_uncore_pmu_add_dev(tx2_pmu)) { 9128c2ecf20Sopenharmony_ci /* Can't add the PMU device, abort */ 9138c2ecf20Sopenharmony_ci return AE_ERROR; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci return AE_OK; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_online_cpu(unsigned int cpu, 9198c2ecf20Sopenharmony_ci struct hlist_node *hpnode) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci tx2_pmu = hlist_entry_safe(hpnode, 9248c2ecf20Sopenharmony_ci struct tx2_uncore_pmu, hpnode); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* Pick this CPU, If there is no CPU/PMU association and both are 9278c2ecf20Sopenharmony_ci * from same node. 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ci if ((tx2_pmu->cpu >= nr_cpu_ids) && 9308c2ecf20Sopenharmony_ci (tx2_pmu->node == cpu_to_node(cpu))) 9318c2ecf20Sopenharmony_ci tx2_pmu->cpu = cpu; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_offline_cpu(unsigned int cpu, 9378c2ecf20Sopenharmony_ci struct hlist_node *hpnode) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci int new_cpu; 9408c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu; 9418c2ecf20Sopenharmony_ci struct cpumask cpu_online_mask_temp; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci tx2_pmu = hlist_entry_safe(hpnode, 9448c2ecf20Sopenharmony_ci struct tx2_uncore_pmu, hpnode); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (cpu != tx2_pmu->cpu) 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (tx2_pmu->hrtimer_callback) 9508c2ecf20Sopenharmony_ci hrtimer_cancel(&tx2_pmu->hrtimer); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci cpumask_copy(&cpu_online_mask_temp, cpu_online_mask); 9538c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, &cpu_online_mask_temp); 9548c2ecf20Sopenharmony_ci new_cpu = cpumask_any_and( 9558c2ecf20Sopenharmony_ci cpumask_of_node(tx2_pmu->node), 9568c2ecf20Sopenharmony_ci &cpu_online_mask_temp); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci tx2_pmu->cpu = new_cpu; 9598c2ecf20Sopenharmony_ci if (new_cpu >= nr_cpu_ids) 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci perf_pmu_migrate_context(&tx2_pmu->pmu, cpu, new_cpu); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic const struct acpi_device_id tx2_uncore_acpi_match[] = { 9678c2ecf20Sopenharmony_ci {"CAV901C", 0}, 9688c2ecf20Sopenharmony_ci {}, 9698c2ecf20Sopenharmony_ci}; 9708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, tx2_uncore_acpi_match); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic int tx2_uncore_probe(struct platform_device *pdev) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 9758c2ecf20Sopenharmony_ci acpi_handle handle; 9768c2ecf20Sopenharmony_ci acpi_status status; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci set_dev_node(dev, acpi_get_node(ACPI_HANDLE(dev))); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (!has_acpi_companion(dev)) 9818c2ecf20Sopenharmony_ci return -ENODEV; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(dev); 9848c2ecf20Sopenharmony_ci if (!handle) 9858c2ecf20Sopenharmony_ci return -EINVAL; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* Walk through the tree for all PMU UNCORE devices */ 9888c2ecf20Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 9898c2ecf20Sopenharmony_ci tx2_uncore_pmu_add, 9908c2ecf20Sopenharmony_ci NULL, dev, NULL); 9918c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 9928c2ecf20Sopenharmony_ci dev_err(dev, "failed to probe PMU devices\n"); 9938c2ecf20Sopenharmony_ci return_ACPI_STATUS(status); 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci dev_info(dev, "node%d: pmu uncore registered\n", dev_to_node(dev)); 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int tx2_uncore_remove(struct platform_device *pdev) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct tx2_uncore_pmu *tx2_pmu, *temp; 10038c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (!list_empty(&tx2_pmus)) { 10068c2ecf20Sopenharmony_ci list_for_each_entry_safe(tx2_pmu, temp, &tx2_pmus, entry) { 10078c2ecf20Sopenharmony_ci if (tx2_pmu->node == dev_to_node(dev)) { 10088c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls( 10098c2ecf20Sopenharmony_ci CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE, 10108c2ecf20Sopenharmony_ci &tx2_pmu->hpnode); 10118c2ecf20Sopenharmony_ci perf_pmu_unregister(&tx2_pmu->pmu); 10128c2ecf20Sopenharmony_ci list_del(&tx2_pmu->entry); 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic struct platform_driver tx2_uncore_driver = { 10208c2ecf20Sopenharmony_ci .driver = { 10218c2ecf20Sopenharmony_ci .name = "tx2-uncore-pmu", 10228c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(tx2_uncore_acpi_match), 10238c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 10248c2ecf20Sopenharmony_ci }, 10258c2ecf20Sopenharmony_ci .probe = tx2_uncore_probe, 10268c2ecf20Sopenharmony_ci .remove = tx2_uncore_remove, 10278c2ecf20Sopenharmony_ci}; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic int __init tx2_uncore_driver_init(void) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci int ret; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE, 10348c2ecf20Sopenharmony_ci "perf/tx2/uncore:online", 10358c2ecf20Sopenharmony_ci tx2_uncore_pmu_online_cpu, 10368c2ecf20Sopenharmony_ci tx2_uncore_pmu_offline_cpu); 10378c2ecf20Sopenharmony_ci if (ret) { 10388c2ecf20Sopenharmony_ci pr_err("TX2 PMU: setup hotplug failed(%d)\n", ret); 10398c2ecf20Sopenharmony_ci return ret; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci ret = platform_driver_register(&tx2_uncore_driver); 10428c2ecf20Sopenharmony_ci if (ret) 10438c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci return ret; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_cimodule_init(tx2_uncore_driver_init); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic void __exit tx2_uncore_driver_exit(void) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci platform_driver_unregister(&tx2_uncore_driver); 10528c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE); 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_cimodule_exit(tx2_uncore_driver_exit); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ThunderX2 UNCORE PMU driver"); 10578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ganapatrao Kulkarni <gkulkarni@cavium.com>"); 1059