18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * L220/L310 cache controller support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 ARM Limited 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/list.h> 118c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 128c2ecf20Sopenharmony_ci#include <linux/printk.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PMU_NR_COUNTERS 2 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void __iomem *l2x0_base; 218c2ecf20Sopenharmony_cistatic struct pmu *l2x0_pmu; 228c2ecf20Sopenharmony_cistatic cpumask_t pmu_cpu; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char *l2x0_name; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic ktime_t l2x0_pmu_poll_period; 278c2ecf20Sopenharmony_cistatic struct hrtimer l2x0_pmu_hrtimer; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * The L220/PL310 PMU has two equivalent counters, Counter1 and Counter0. 318c2ecf20Sopenharmony_ci * Registers controlling these are laid out in pairs, in descending order, i.e. 328c2ecf20Sopenharmony_ci * the register for Counter1 comes first, followed by the register for 338c2ecf20Sopenharmony_ci * Counter0. 348c2ecf20Sopenharmony_ci * We ensure that idx 0 -> Counter0, and idx1 -> Counter1. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic struct perf_event *events[PMU_NR_COUNTERS]; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Find an unused counter */ 398c2ecf20Sopenharmony_cistatic int l2x0_pmu_find_idx(void) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) { 448c2ecf20Sopenharmony_ci if (!events[i]) 458c2ecf20Sopenharmony_ci return i; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return -1; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* How many counters are allocated? */ 528c2ecf20Sopenharmony_cistatic int l2x0_pmu_num_active_counters(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int i, cnt = 0; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) { 578c2ecf20Sopenharmony_ci if (events[i]) 588c2ecf20Sopenharmony_ci cnt++; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return cnt; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void l2x0_pmu_counter_config_write(int idx, u32 val) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT0_CFG - 4 * idx); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic u32 l2x0_pmu_counter_read(int idx) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return readl_relaxed(l2x0_base + L2X0_EVENT_CNT0_VAL - 4 * idx); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void l2x0_pmu_counter_write(int idx, u32 val) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT0_VAL - 4 * idx); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void __l2x0_pmu_enable(void) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci u32 val = readl_relaxed(l2x0_base + L2X0_EVENT_CNT_CTRL); 828c2ecf20Sopenharmony_ci val |= L2X0_EVENT_CNT_CTRL_ENABLE; 838c2ecf20Sopenharmony_ci writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT_CTRL); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void __l2x0_pmu_disable(void) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u32 val = readl_relaxed(l2x0_base + L2X0_EVENT_CNT_CTRL); 898c2ecf20Sopenharmony_ci val &= ~L2X0_EVENT_CNT_CTRL_ENABLE; 908c2ecf20Sopenharmony_ci writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT_CTRL); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void l2x0_pmu_enable(struct pmu *pmu) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci if (l2x0_pmu_num_active_counters() == 0) 968c2ecf20Sopenharmony_ci return; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci __l2x0_pmu_enable(); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void l2x0_pmu_disable(struct pmu *pmu) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci if (l2x0_pmu_num_active_counters() == 0) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci __l2x0_pmu_disable(); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void warn_if_saturated(u32 count) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci if (count != 0xffffffff) 1128c2ecf20Sopenharmony_ci return; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci pr_warn_ratelimited("L2X0 counter saturated. Poll period too long\n"); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_read(struct perf_event *event) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 1208c2ecf20Sopenharmony_ci u64 prev_count, new_count, mask; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci do { 1238c2ecf20Sopenharmony_ci prev_count = local64_read(&hw->prev_count); 1248c2ecf20Sopenharmony_ci new_count = l2x0_pmu_counter_read(hw->idx); 1258c2ecf20Sopenharmony_ci } while (local64_xchg(&hw->prev_count, new_count) != prev_count); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mask = GENMASK_ULL(31, 0); 1288c2ecf20Sopenharmony_ci local64_add((new_count - prev_count) & mask, &event->count); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci warn_if_saturated(new_count); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_configure(struct perf_event *event) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * The L2X0 counters saturate at 0xffffffff rather than wrapping, so we 1398c2ecf20Sopenharmony_ci * will *always* lose some number of events when a counter saturates, 1408c2ecf20Sopenharmony_ci * and have no way of detecting how many were lost. 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * To minimize the impact of this, we try to maximize the period by 1438c2ecf20Sopenharmony_ci * always starting counters at zero. To ensure that group ratios are 1448c2ecf20Sopenharmony_ci * representative, we poll periodically to avoid counters saturating. 1458c2ecf20Sopenharmony_ci * See l2x0_pmu_poll(). 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci local64_set(&hw->prev_count, 0); 1488c2ecf20Sopenharmony_ci l2x0_pmu_counter_write(hw->idx, 0); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic enum hrtimer_restart l2x0_pmu_poll(struct hrtimer *hrtimer) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci unsigned long flags; 1548c2ecf20Sopenharmony_ci int i; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci local_irq_save(flags); 1578c2ecf20Sopenharmony_ci __l2x0_pmu_disable(); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) { 1608c2ecf20Sopenharmony_ci struct perf_event *event = events[i]; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (!event) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci l2x0_pmu_event_read(event); 1668c2ecf20Sopenharmony_ci l2x0_pmu_event_configure(event); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci __l2x0_pmu_enable(); 1708c2ecf20Sopenharmony_ci local_irq_restore(flags); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci hrtimer_forward_now(hrtimer, l2x0_pmu_poll_period); 1738c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void __l2x0_pmu_event_enable(int idx, u32 event) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u32 val; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci val = event << L2X0_EVENT_CNT_CFG_SRC_SHIFT; 1828c2ecf20Sopenharmony_ci val |= L2X0_EVENT_CNT_CFG_INT_DISABLED; 1838c2ecf20Sopenharmony_ci l2x0_pmu_counter_config_write(idx, val); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_start(struct perf_event *event, int flags) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (flags & PERF_EF_RELOAD) { 1948c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(hw->state & PERF_HES_UPTODATE)); 1958c2ecf20Sopenharmony_ci l2x0_pmu_event_configure(event); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci hw->state = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci __l2x0_pmu_event_enable(hw->idx, hw->config_base); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void __l2x0_pmu_event_disable(int idx) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci u32 val; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci val = L2X0_EVENT_CNT_CFG_SRC_DISABLED << L2X0_EVENT_CNT_CFG_SRC_SHIFT; 2088c2ecf20Sopenharmony_ci val |= L2X0_EVENT_CNT_CFG_INT_DISABLED; 2098c2ecf20Sopenharmony_ci l2x0_pmu_counter_config_write(idx, val); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_stop(struct perf_event *event, int flags) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(event->hw.state & PERF_HES_STOPPED)) 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci __l2x0_pmu_event_disable(hw->idx); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci hw->state |= PERF_HES_STOPPED; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (flags & PERF_EF_UPDATE) { 2248c2ecf20Sopenharmony_ci l2x0_pmu_event_read(event); 2258c2ecf20Sopenharmony_ci hw->state |= PERF_HES_UPTODATE; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int l2x0_pmu_event_add(struct perf_event *event, int flags) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 2328c2ecf20Sopenharmony_ci int idx = l2x0_pmu_find_idx(); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (idx == -1) 2358c2ecf20Sopenharmony_ci return -EAGAIN; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * Pin the timer, so that the overflows are handled by the chosen 2398c2ecf20Sopenharmony_ci * event->cpu (this is the same one as presented in "cpumask" 2408c2ecf20Sopenharmony_ci * attribute). 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (l2x0_pmu_num_active_counters() == 0) 2438c2ecf20Sopenharmony_ci hrtimer_start(&l2x0_pmu_hrtimer, l2x0_pmu_poll_period, 2448c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_PINNED); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci events[idx] = event; 2478c2ecf20Sopenharmony_ci hw->idx = idx; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci l2x0_pmu_event_configure(event); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci hw->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 2548c2ecf20Sopenharmony_ci l2x0_pmu_event_start(event, 0); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_del(struct perf_event *event, int flags) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci l2x0_pmu_event_stop(event, PERF_EF_UPDATE); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci events[hw->idx] = NULL; 2668c2ecf20Sopenharmony_ci hw->idx = -1; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (l2x0_pmu_num_active_counters() == 0) 2698c2ecf20Sopenharmony_ci hrtimer_cancel(&l2x0_pmu_hrtimer); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic bool l2x0_pmu_group_is_valid(struct perf_event *event) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct pmu *pmu = event->pmu; 2758c2ecf20Sopenharmony_ci struct perf_event *leader = event->group_leader; 2768c2ecf20Sopenharmony_ci struct perf_event *sibling; 2778c2ecf20Sopenharmony_ci int num_hw = 0; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (leader->pmu == pmu) 2808c2ecf20Sopenharmony_ci num_hw++; 2818c2ecf20Sopenharmony_ci else if (!is_software_event(leader)) 2828c2ecf20Sopenharmony_ci return false; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci for_each_sibling_event(sibling, leader) { 2858c2ecf20Sopenharmony_ci if (sibling->pmu == pmu) 2868c2ecf20Sopenharmony_ci num_hw++; 2878c2ecf20Sopenharmony_ci else if (!is_software_event(sibling)) 2888c2ecf20Sopenharmony_ci return false; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return num_hw <= PMU_NR_COUNTERS; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int l2x0_pmu_event_init(struct perf_event *event) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct hw_perf_event *hw = &event->hw; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (event->attr.type != l2x0_pmu->type) 2998c2ecf20Sopenharmony_ci return -ENOENT; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (is_sampling_event(event) || 3028c2ecf20Sopenharmony_ci event->attach_state & PERF_ATTACH_TASK) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (event->cpu < 0) 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (event->attr.config & ~L2X0_EVENT_CNT_CFG_SRC_MASK) 3098c2ecf20Sopenharmony_ci return -EINVAL; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci hw->config_base = event->attr.config; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!l2x0_pmu_group_is_valid(event)) 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci event->cpu = cpumask_first(&pmu_cpu); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistruct l2x0_event_attribute { 3228c2ecf20Sopenharmony_ci struct device_attribute attr; 3238c2ecf20Sopenharmony_ci unsigned int config; 3248c2ecf20Sopenharmony_ci bool pl310_only; 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci#define L2X0_EVENT_ATTR(_name, _config, _pl310_only) \ 3288c2ecf20Sopenharmony_ci (&((struct l2x0_event_attribute[]) {{ \ 3298c2ecf20Sopenharmony_ci .attr = __ATTR(_name, S_IRUGO, l2x0_pmu_event_show, NULL), \ 3308c2ecf20Sopenharmony_ci .config = _config, \ 3318c2ecf20Sopenharmony_ci .pl310_only = _pl310_only, \ 3328c2ecf20Sopenharmony_ci }})[0].attr.attr) 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci#define L220_PLUS_EVENT_ATTR(_name, _config) \ 3358c2ecf20Sopenharmony_ci L2X0_EVENT_ATTR(_name, _config, false) 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#define PL310_EVENT_ATTR(_name, _config) \ 3388c2ecf20Sopenharmony_ci L2X0_EVENT_ATTR(_name, _config, true) 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic ssize_t l2x0_pmu_event_show(struct device *dev, 3418c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct l2x0_event_attribute *lattr; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci lattr = container_of(attr, typeof(*lattr), attr); 3468c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "config=0x%x\n", lattr->config); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic umode_t l2x0_pmu_event_attr_is_visible(struct kobject *kobj, 3508c2ecf20Sopenharmony_ci struct attribute *attr, 3518c2ecf20Sopenharmony_ci int unused) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 3548c2ecf20Sopenharmony_ci struct pmu *pmu = dev_get_drvdata(dev); 3558c2ecf20Sopenharmony_ci struct l2x0_event_attribute *lattr; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci lattr = container_of(attr, typeof(*lattr), attr.attr); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!lattr->pl310_only || strcmp("l2c_310", pmu->name) == 0) 3608c2ecf20Sopenharmony_ci return attr->mode; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic struct attribute *l2x0_pmu_event_attrs[] = { 3668c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(co, 0x1), 3678c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(drhit, 0x2), 3688c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(drreq, 0x3), 3698c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(dwhit, 0x4), 3708c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(dwreq, 0x5), 3718c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(dwtreq, 0x6), 3728c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(irhit, 0x7), 3738c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(irreq, 0x8), 3748c2ecf20Sopenharmony_ci L220_PLUS_EVENT_ATTR(wa, 0x9), 3758c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(ipfalloc, 0xa), 3768c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(epfhit, 0xb), 3778c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(epfalloc, 0xc), 3788c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(srrcvd, 0xd), 3798c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(srconf, 0xe), 3808c2ecf20Sopenharmony_ci PL310_EVENT_ATTR(epfrcvd, 0xf), 3818c2ecf20Sopenharmony_ci NULL 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic struct attribute_group l2x0_pmu_event_attrs_group = { 3858c2ecf20Sopenharmony_ci .name = "events", 3868c2ecf20Sopenharmony_ci .attrs = l2x0_pmu_event_attrs, 3878c2ecf20Sopenharmony_ci .is_visible = l2x0_pmu_event_attr_is_visible, 3888c2ecf20Sopenharmony_ci}; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic ssize_t l2x0_pmu_cpumask_show(struct device *dev, 3918c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &pmu_cpu); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic struct device_attribute l2x0_pmu_cpumask_attr = 3978c2ecf20Sopenharmony_ci __ATTR(cpumask, S_IRUGO, l2x0_pmu_cpumask_show, NULL); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic struct attribute *l2x0_pmu_cpumask_attrs[] = { 4008c2ecf20Sopenharmony_ci &l2x0_pmu_cpumask_attr.attr, 4018c2ecf20Sopenharmony_ci NULL, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic struct attribute_group l2x0_pmu_cpumask_attr_group = { 4058c2ecf20Sopenharmony_ci .attrs = l2x0_pmu_cpumask_attrs, 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct attribute_group *l2x0_pmu_attr_groups[] = { 4098c2ecf20Sopenharmony_ci &l2x0_pmu_event_attrs_group, 4108c2ecf20Sopenharmony_ci &l2x0_pmu_cpumask_attr_group, 4118c2ecf20Sopenharmony_ci NULL, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void l2x0_pmu_reset(void) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci int i; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci __l2x0_pmu_disable(); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) 4218c2ecf20Sopenharmony_ci __l2x0_pmu_event_disable(i); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int l2x0_pmu_offline_cpu(unsigned int cpu) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci unsigned int target; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!cpumask_test_and_clear_cpu(cpu, &pmu_cpu)) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 4328c2ecf20Sopenharmony_ci if (target >= nr_cpu_ids) 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci perf_pmu_migrate_context(l2x0_pmu, cpu, target); 4368c2ecf20Sopenharmony_ci cpumask_set_cpu(target, &pmu_cpu); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_civoid l2x0_pmu_suspend(void) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int i; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!l2x0_pmu) 4468c2ecf20Sopenharmony_ci return; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci l2x0_pmu_disable(l2x0_pmu); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) { 4518c2ecf20Sopenharmony_ci if (events[i]) 4528c2ecf20Sopenharmony_ci l2x0_pmu_event_stop(events[i], PERF_EF_UPDATE); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_civoid l2x0_pmu_resume(void) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!l2x0_pmu) 4628c2ecf20Sopenharmony_ci return; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci l2x0_pmu_reset(); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (i = 0; i < PMU_NR_COUNTERS; i++) { 4678c2ecf20Sopenharmony_ci if (events[i]) 4688c2ecf20Sopenharmony_ci l2x0_pmu_event_start(events[i], PERF_EF_RELOAD); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci l2x0_pmu_enable(l2x0_pmu); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_civoid __init l2x0_pmu_register(void __iomem *base, u32 part) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * Determine whether we support the PMU, and choose the name for sysfs. 4788c2ecf20Sopenharmony_ci * This is also used by l2x0_pmu_event_attr_is_visible to determine 4798c2ecf20Sopenharmony_ci * which events to display, as the PL310 PMU supports a superset of 4808c2ecf20Sopenharmony_ci * L220 events. 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * The L210 PMU has a different programmer's interface, and is not 4838c2ecf20Sopenharmony_ci * supported by this driver. 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * We must defer registering the PMU until the perf subsystem is up and 4868c2ecf20Sopenharmony_ci * running, so just stash the name and base, and leave that to another 4878c2ecf20Sopenharmony_ci * initcall. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci switch (part & L2X0_CACHE_ID_PART_MASK) { 4908c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L220: 4918c2ecf20Sopenharmony_ci l2x0_name = "l2c_220"; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L310: 4948c2ecf20Sopenharmony_ci l2x0_name = "l2c_310"; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci l2x0_base = base; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic __init int l2x0_pmu_init(void) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci int ret; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (!l2x0_base) 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci l2x0_pmu = kzalloc(sizeof(*l2x0_pmu), GFP_KERNEL); 5118c2ecf20Sopenharmony_ci if (!l2x0_pmu) { 5128c2ecf20Sopenharmony_ci pr_warn("Unable to allocate L2x0 PMU\n"); 5138c2ecf20Sopenharmony_ci return -ENOMEM; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci *l2x0_pmu = (struct pmu) { 5178c2ecf20Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 5188c2ecf20Sopenharmony_ci .pmu_enable = l2x0_pmu_enable, 5198c2ecf20Sopenharmony_ci .pmu_disable = l2x0_pmu_disable, 5208c2ecf20Sopenharmony_ci .read = l2x0_pmu_event_read, 5218c2ecf20Sopenharmony_ci .start = l2x0_pmu_event_start, 5228c2ecf20Sopenharmony_ci .stop = l2x0_pmu_event_stop, 5238c2ecf20Sopenharmony_ci .add = l2x0_pmu_event_add, 5248c2ecf20Sopenharmony_ci .del = l2x0_pmu_event_del, 5258c2ecf20Sopenharmony_ci .event_init = l2x0_pmu_event_init, 5268c2ecf20Sopenharmony_ci .attr_groups = l2x0_pmu_attr_groups, 5278c2ecf20Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 5288c2ecf20Sopenharmony_ci }; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci l2x0_pmu_reset(); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * We always use a hrtimer rather than an interrupt. 5348c2ecf20Sopenharmony_ci * See comments in l2x0_pmu_event_configure and l2x0_pmu_poll. 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * Polling once a second allows the counters to fill up to 1/128th on a 5378c2ecf20Sopenharmony_ci * quad-core test chip with cores clocked at 400MHz. Hopefully this 5388c2ecf20Sopenharmony_ci * leaves sufficient headroom to avoid overflow on production silicon 5398c2ecf20Sopenharmony_ci * at higher frequencies. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci l2x0_pmu_poll_period = ms_to_ktime(1000); 5428c2ecf20Sopenharmony_ci hrtimer_init(&l2x0_pmu_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 5438c2ecf20Sopenharmony_ci l2x0_pmu_hrtimer.function = l2x0_pmu_poll; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci cpumask_set_cpu(0, &pmu_cpu); 5468c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_L2X0_ONLINE, 5478c2ecf20Sopenharmony_ci "perf/arm/l2x0:online", NULL, 5488c2ecf20Sopenharmony_ci l2x0_pmu_offline_cpu); 5498c2ecf20Sopenharmony_ci if (ret) 5508c2ecf20Sopenharmony_ci goto out_pmu; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ret = perf_pmu_register(l2x0_pmu, l2x0_name, -1); 5538c2ecf20Sopenharmony_ci if (ret) 5548c2ecf20Sopenharmony_ci goto out_cpuhp; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciout_cpuhp: 5598c2ecf20Sopenharmony_ci cpuhp_remove_state_nocalls(CPUHP_AP_PERF_ARM_L2X0_ONLINE); 5608c2ecf20Sopenharmony_ciout_pmu: 5618c2ecf20Sopenharmony_ci kfree(l2x0_pmu); 5628c2ecf20Sopenharmony_ci l2x0_pmu = NULL; 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_cidevice_initcall(l2x0_pmu_init); 566