18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Performance event support - Freescale Embedded Performance Monitor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2008-2009 Paul Mackerras, IBM Corporation. 68c2ecf20Sopenharmony_ci * Copyright 2010 Freescale Semiconductor, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 118c2ecf20Sopenharmony_ci#include <linux/percpu.h> 128c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 138c2ecf20Sopenharmony_ci#include <asm/reg_fsl_emb.h> 148c2ecf20Sopenharmony_ci#include <asm/pmc.h> 158c2ecf20Sopenharmony_ci#include <asm/machdep.h> 168c2ecf20Sopenharmony_ci#include <asm/firmware.h> 178c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct cpu_hw_events { 208c2ecf20Sopenharmony_ci int n_events; 218c2ecf20Sopenharmony_ci int disabled; 228c2ecf20Sopenharmony_ci u8 pmcs_enabled; 238c2ecf20Sopenharmony_ci struct perf_event *event[MAX_HWEVENTS]; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic struct fsl_emb_pmu *ppmu; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Number of perf_events counting hardware events */ 308c2ecf20Sopenharmony_cistatic atomic_t num_events; 318c2ecf20Sopenharmony_ci/* Used to avoid races in calling reserve/release_pmc_hardware */ 328c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pmc_reserve_mutex); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void perf_event_interrupt(struct pt_regs *regs); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Read one performance monitor counter (PMC). 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic unsigned long read_pmc(int idx) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci unsigned long val; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci switch (idx) { 448c2ecf20Sopenharmony_ci case 0: 458c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC0); 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci case 1: 488c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC1); 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci case 2: 518c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC2); 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci case 3: 548c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC3); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case 4: 578c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC4); 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case 5: 608c2ecf20Sopenharmony_ci val = mfpmr(PMRN_PMC5); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci default: 638c2ecf20Sopenharmony_ci printk(KERN_ERR "oops trying to read PMC%d\n", idx); 648c2ecf20Sopenharmony_ci val = 0; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci return val; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Write one PMC. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic void write_pmc(int idx, unsigned long val) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci switch (idx) { 758c2ecf20Sopenharmony_ci case 0: 768c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC0, val); 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 1: 798c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC1, val); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci case 2: 828c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC2, val); 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci case 3: 858c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC3, val); 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case 4: 888c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC4, val); 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case 5: 918c2ecf20Sopenharmony_ci mtpmr(PMRN_PMC5, val); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci default: 948c2ecf20Sopenharmony_ci printk(KERN_ERR "oops trying to write PMC%d\n", idx); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci isync(); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * Write one local control A register 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic void write_pmlca(int idx, unsigned long val) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci switch (idx) { 1068c2ecf20Sopenharmony_ci case 0: 1078c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA0, val); 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci case 1: 1108c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA1, val); 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci case 2: 1138c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA2, val); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case 3: 1168c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA3, val); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case 4: 1198c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA4, val); 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case 5: 1228c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCA5, val); 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci default: 1258c2ecf20Sopenharmony_ci printk(KERN_ERR "oops trying to write PMLCA%d\n", idx); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci isync(); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Write one local control B register 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic void write_pmlcb(int idx, unsigned long val) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci switch (idx) { 1378c2ecf20Sopenharmony_ci case 0: 1388c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB0, val); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case 1: 1418c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB1, val); 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci case 2: 1448c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB2, val); 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case 3: 1478c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB3, val); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 4: 1508c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB4, val); 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci case 5: 1538c2ecf20Sopenharmony_ci mtpmr(PMRN_PMLCB5, val); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci default: 1568c2ecf20Sopenharmony_ci printk(KERN_ERR "oops trying to write PMLCB%d\n", idx); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci isync(); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_read(struct perf_event *event) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci s64 val, delta, prev; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) 1678c2ecf20Sopenharmony_ci return; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Performance monitor interrupts come even when interrupts 1718c2ecf20Sopenharmony_ci * are soft-disabled, as long as interrupts are hard-enabled. 1728c2ecf20Sopenharmony_ci * Therefore we treat them like NMIs. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci do { 1758c2ecf20Sopenharmony_ci prev = local64_read(&event->hw.prev_count); 1768c2ecf20Sopenharmony_ci barrier(); 1778c2ecf20Sopenharmony_ci val = read_pmc(event->hw.idx); 1788c2ecf20Sopenharmony_ci } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* The counters are only 32 bits wide */ 1818c2ecf20Sopenharmony_ci delta = (val - prev) & 0xfffffffful; 1828c2ecf20Sopenharmony_ci local64_add(delta, &event->count); 1838c2ecf20Sopenharmony_ci local64_sub(delta, &event->hw.period_left); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Disable all events to prevent PMU interrupts and to allow 1888c2ecf20Sopenharmony_ci * events to be added or removed. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_disable(struct pmu *pmu) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw; 1938c2ecf20Sopenharmony_ci unsigned long flags; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci local_irq_save(flags); 1968c2ecf20Sopenharmony_ci cpuhw = this_cpu_ptr(&cpu_hw_events); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!cpuhw->disabled) { 1998c2ecf20Sopenharmony_ci cpuhw->disabled = 1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Check if we ever enabled the PMU on this cpu. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci if (!cpuhw->pmcs_enabled) { 2058c2ecf20Sopenharmony_ci ppc_enable_pmcs(); 2068c2ecf20Sopenharmony_ci cpuhw->pmcs_enabled = 1; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (atomic_read(&num_events)) { 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Set the 'freeze all counters' bit, and disable 2128c2ecf20Sopenharmony_ci * interrupts. The barrier is to make sure the 2138c2ecf20Sopenharmony_ci * mtpmr has been executed and the PMU has frozen 2148c2ecf20Sopenharmony_ci * the events before we return. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci mtpmr(PMRN_PMGC0, PMGC0_FAC); 2188c2ecf20Sopenharmony_ci isync(); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci local_irq_restore(flags); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * Re-enable all events if disable == 0. 2268c2ecf20Sopenharmony_ci * If we were previously disabled and events were added, then 2278c2ecf20Sopenharmony_ci * put the new config on the PMU. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_enable(struct pmu *pmu) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw; 2328c2ecf20Sopenharmony_ci unsigned long flags; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci local_irq_save(flags); 2358c2ecf20Sopenharmony_ci cpuhw = this_cpu_ptr(&cpu_hw_events); 2368c2ecf20Sopenharmony_ci if (!cpuhw->disabled) 2378c2ecf20Sopenharmony_ci goto out; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci cpuhw->disabled = 0; 2408c2ecf20Sopenharmony_ci ppc_set_pmu_inuse(cpuhw->n_events != 0); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (cpuhw->n_events > 0) { 2438c2ecf20Sopenharmony_ci mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); 2448c2ecf20Sopenharmony_ci isync(); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci out: 2488c2ecf20Sopenharmony_ci local_irq_restore(flags); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int collect_events(struct perf_event *group, int max_count, 2528c2ecf20Sopenharmony_ci struct perf_event *ctrs[]) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int n = 0; 2558c2ecf20Sopenharmony_ci struct perf_event *event; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!is_software_event(group)) { 2588c2ecf20Sopenharmony_ci if (n >= max_count) 2598c2ecf20Sopenharmony_ci return -1; 2608c2ecf20Sopenharmony_ci ctrs[n] = group; 2618c2ecf20Sopenharmony_ci n++; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci for_each_sibling_event(event, group) { 2648c2ecf20Sopenharmony_ci if (!is_software_event(event) && 2658c2ecf20Sopenharmony_ci event->state != PERF_EVENT_STATE_OFF) { 2668c2ecf20Sopenharmony_ci if (n >= max_count) 2678c2ecf20Sopenharmony_ci return -1; 2688c2ecf20Sopenharmony_ci ctrs[n] = event; 2698c2ecf20Sopenharmony_ci n++; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci return n; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* context locked on entry */ 2768c2ecf20Sopenharmony_cistatic int fsl_emb_pmu_add(struct perf_event *event, int flags) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw; 2798c2ecf20Sopenharmony_ci int ret = -EAGAIN; 2808c2ecf20Sopenharmony_ci int num_counters = ppmu->n_counter; 2818c2ecf20Sopenharmony_ci u64 val; 2828c2ecf20Sopenharmony_ci int i; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci perf_pmu_disable(event->pmu); 2858c2ecf20Sopenharmony_ci cpuhw = &get_cpu_var(cpu_hw_events); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) 2888c2ecf20Sopenharmony_ci num_counters = ppmu->n_restricted; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * Allocate counters from top-down, so that restricted-capable 2928c2ecf20Sopenharmony_ci * counters are kept free as long as possible. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci for (i = num_counters - 1; i >= 0; i--) { 2958c2ecf20Sopenharmony_ci if (cpuhw->event[i]) 2968c2ecf20Sopenharmony_ci continue; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (i < 0) 3028c2ecf20Sopenharmony_ci goto out; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci event->hw.idx = i; 3058c2ecf20Sopenharmony_ci cpuhw->event[i] = event; 3068c2ecf20Sopenharmony_ci ++cpuhw->n_events; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci val = 0; 3098c2ecf20Sopenharmony_ci if (event->hw.sample_period) { 3108c2ecf20Sopenharmony_ci s64 left = local64_read(&event->hw.period_left); 3118c2ecf20Sopenharmony_ci if (left < 0x80000000L) 3128c2ecf20Sopenharmony_ci val = 0x80000000L - left; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, val); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (unlikely(!(flags & PERF_EF_START))) { 3178c2ecf20Sopenharmony_ci event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 3188c2ecf20Sopenharmony_ci val = 0; 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci event->hw.state &= ~(PERF_HES_STOPPED | PERF_HES_UPTODATE); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci write_pmc(i, val); 3248c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci write_pmlcb(i, event->hw.config >> 32); 3278c2ecf20Sopenharmony_ci write_pmlca(i, event->hw.config_base); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = 0; 3308c2ecf20Sopenharmony_ci out: 3318c2ecf20Sopenharmony_ci put_cpu_var(cpu_hw_events); 3328c2ecf20Sopenharmony_ci perf_pmu_enable(event->pmu); 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* context locked on entry */ 3378c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_del(struct perf_event *event, int flags) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw; 3408c2ecf20Sopenharmony_ci int i = event->hw.idx; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci perf_pmu_disable(event->pmu); 3438c2ecf20Sopenharmony_ci if (i < 0) 3448c2ecf20Sopenharmony_ci goto out; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci fsl_emb_pmu_read(event); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci cpuhw = &get_cpu_var(cpu_hw_events); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci WARN_ON(event != cpuhw->event[event->hw.idx]); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci write_pmlca(i, 0); 3538c2ecf20Sopenharmony_ci write_pmlcb(i, 0); 3548c2ecf20Sopenharmony_ci write_pmc(i, 0); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci cpuhw->event[i] = NULL; 3578c2ecf20Sopenharmony_ci event->hw.idx = -1; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * TODO: if at least one restricted event exists, and we 3618c2ecf20Sopenharmony_ci * just freed up a non-restricted-capable counter, and 3628c2ecf20Sopenharmony_ci * there is a restricted-capable counter occupied by 3638c2ecf20Sopenharmony_ci * a non-restricted event, migrate that event to the 3648c2ecf20Sopenharmony_ci * vacated counter. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci cpuhw->n_events--; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci out: 3708c2ecf20Sopenharmony_ci perf_pmu_enable(event->pmu); 3718c2ecf20Sopenharmony_ci put_cpu_var(cpu_hw_events); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci unsigned long flags; 3778c2ecf20Sopenharmony_ci unsigned long val; 3788c2ecf20Sopenharmony_ci s64 left; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (event->hw.idx < 0 || !event->hw.sample_period) 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!(event->hw.state & PERF_HES_STOPPED)) 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (ef_flags & PERF_EF_RELOAD) 3878c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci local_irq_save(flags); 3908c2ecf20Sopenharmony_ci perf_pmu_disable(event->pmu); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci event->hw.state = 0; 3938c2ecf20Sopenharmony_ci left = local64_read(&event->hw.period_left); 3948c2ecf20Sopenharmony_ci val = 0; 3958c2ecf20Sopenharmony_ci if (left < 0x80000000L) 3968c2ecf20Sopenharmony_ci val = 0x80000000L - left; 3978c2ecf20Sopenharmony_ci write_pmc(event->hw.idx, val); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 4008c2ecf20Sopenharmony_ci perf_pmu_enable(event->pmu); 4018c2ecf20Sopenharmony_ci local_irq_restore(flags); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void fsl_emb_pmu_stop(struct perf_event *event, int ef_flags) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci unsigned long flags; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (event->hw.idx < 0 || !event->hw.sample_period) 4098c2ecf20Sopenharmony_ci return; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci local_irq_save(flags); 4158c2ecf20Sopenharmony_ci perf_pmu_disable(event->pmu); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci fsl_emb_pmu_read(event); 4188c2ecf20Sopenharmony_ci event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 4198c2ecf20Sopenharmony_ci write_pmc(event->hw.idx, 0); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 4228c2ecf20Sopenharmony_ci perf_pmu_enable(event->pmu); 4238c2ecf20Sopenharmony_ci local_irq_restore(flags); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/* 4278c2ecf20Sopenharmony_ci * Release the PMU if this is the last perf_event. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_cistatic void hw_perf_event_destroy(struct perf_event *event) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci if (!atomic_add_unless(&num_events, -1, 1)) { 4328c2ecf20Sopenharmony_ci mutex_lock(&pmc_reserve_mutex); 4338c2ecf20Sopenharmony_ci if (atomic_dec_return(&num_events) == 0) 4348c2ecf20Sopenharmony_ci release_pmc_hardware(); 4358c2ecf20Sopenharmony_ci mutex_unlock(&pmc_reserve_mutex); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* 4408c2ecf20Sopenharmony_ci * Translate a generic cache event_id config to a raw event_id code. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic int hw_perf_cache_event(u64 config, u64 *eventp) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci unsigned long type, op, result; 4458c2ecf20Sopenharmony_ci int ev; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!ppmu->cache_events) 4488c2ecf20Sopenharmony_ci return -EINVAL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* unpack config */ 4518c2ecf20Sopenharmony_ci type = config & 0xff; 4528c2ecf20Sopenharmony_ci op = (config >> 8) & 0xff; 4538c2ecf20Sopenharmony_ci result = (config >> 16) & 0xff; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (type >= PERF_COUNT_HW_CACHE_MAX || 4568c2ecf20Sopenharmony_ci op >= PERF_COUNT_HW_CACHE_OP_MAX || 4578c2ecf20Sopenharmony_ci result >= PERF_COUNT_HW_CACHE_RESULT_MAX) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ev = (*ppmu->cache_events)[type][op][result]; 4618c2ecf20Sopenharmony_ci if (ev == 0) 4628c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4638c2ecf20Sopenharmony_ci if (ev == -1) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci *eventp = ev; 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic int fsl_emb_pmu_event_init(struct perf_event *event) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci u64 ev; 4728c2ecf20Sopenharmony_ci struct perf_event *events[MAX_HWEVENTS]; 4738c2ecf20Sopenharmony_ci int n; 4748c2ecf20Sopenharmony_ci int err; 4758c2ecf20Sopenharmony_ci int num_restricted; 4768c2ecf20Sopenharmony_ci int i; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (ppmu->n_counter > MAX_HWEVENTS) { 4798c2ecf20Sopenharmony_ci WARN(1, "No. of perf counters (%d) is higher than max array size(%d)\n", 4808c2ecf20Sopenharmony_ci ppmu->n_counter, MAX_HWEVENTS); 4818c2ecf20Sopenharmony_ci ppmu->n_counter = MAX_HWEVENTS; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci switch (event->attr.type) { 4858c2ecf20Sopenharmony_ci case PERF_TYPE_HARDWARE: 4868c2ecf20Sopenharmony_ci ev = event->attr.config; 4878c2ecf20Sopenharmony_ci if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) 4888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4898c2ecf20Sopenharmony_ci ev = ppmu->generic_events[ev]; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci case PERF_TYPE_HW_CACHE: 4938c2ecf20Sopenharmony_ci err = hw_perf_cache_event(event->attr.config, &ev); 4948c2ecf20Sopenharmony_ci if (err) 4958c2ecf20Sopenharmony_ci return err; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci case PERF_TYPE_RAW: 4998c2ecf20Sopenharmony_ci ev = event->attr.config; 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci default: 5038c2ecf20Sopenharmony_ci return -ENOENT; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci event->hw.config = ppmu->xlate_event(ev); 5078c2ecf20Sopenharmony_ci if (!(event->hw.config & FSL_EMB_EVENT_VALID)) 5088c2ecf20Sopenharmony_ci return -EINVAL; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* 5118c2ecf20Sopenharmony_ci * If this is in a group, check if it can go on with all the 5128c2ecf20Sopenharmony_ci * other hardware events in the group. We assume the event 5138c2ecf20Sopenharmony_ci * hasn't been linked into its leader's sibling list at this point. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci n = 0; 5168c2ecf20Sopenharmony_ci if (event->group_leader != event) { 5178c2ecf20Sopenharmony_ci n = collect_events(event->group_leader, 5188c2ecf20Sopenharmony_ci ppmu->n_counter - 1, events); 5198c2ecf20Sopenharmony_ci if (n < 0) 5208c2ecf20Sopenharmony_ci return -EINVAL; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) { 5248c2ecf20Sopenharmony_ci num_restricted = 0; 5258c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 5268c2ecf20Sopenharmony_ci if (events[i]->hw.config & FSL_EMB_EVENT_RESTRICTED) 5278c2ecf20Sopenharmony_ci num_restricted++; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (num_restricted >= ppmu->n_restricted) 5318c2ecf20Sopenharmony_ci return -EINVAL; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci event->hw.idx = -1; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci event->hw.config_base = PMLCA_CE | PMLCA_FCM1 | 5378c2ecf20Sopenharmony_ci (u32)((ev << 16) & PMLCA_EVENT_MASK); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (event->attr.exclude_user) 5408c2ecf20Sopenharmony_ci event->hw.config_base |= PMLCA_FCU; 5418c2ecf20Sopenharmony_ci if (event->attr.exclude_kernel) 5428c2ecf20Sopenharmony_ci event->hw.config_base |= PMLCA_FCS; 5438c2ecf20Sopenharmony_ci if (event->attr.exclude_idle) 5448c2ecf20Sopenharmony_ci return -ENOTSUPP; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci event->hw.last_period = event->hw.sample_period; 5478c2ecf20Sopenharmony_ci local64_set(&event->hw.period_left, event->hw.last_period); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* 5508c2ecf20Sopenharmony_ci * See if we need to reserve the PMU. 5518c2ecf20Sopenharmony_ci * If no events are currently in use, then we have to take a 5528c2ecf20Sopenharmony_ci * mutex to ensure that we don't race with another task doing 5538c2ecf20Sopenharmony_ci * reserve_pmc_hardware or release_pmc_hardware. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci err = 0; 5568c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&num_events)) { 5578c2ecf20Sopenharmony_ci mutex_lock(&pmc_reserve_mutex); 5588c2ecf20Sopenharmony_ci if (atomic_read(&num_events) == 0 && 5598c2ecf20Sopenharmony_ci reserve_pmc_hardware(perf_event_interrupt)) 5608c2ecf20Sopenharmony_ci err = -EBUSY; 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci atomic_inc(&num_events); 5638c2ecf20Sopenharmony_ci mutex_unlock(&pmc_reserve_mutex); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci mtpmr(PMRN_PMGC0, PMGC0_FAC); 5668c2ecf20Sopenharmony_ci isync(); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci event->destroy = hw_perf_event_destroy; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return err; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic struct pmu fsl_emb_pmu = { 5748c2ecf20Sopenharmony_ci .pmu_enable = fsl_emb_pmu_enable, 5758c2ecf20Sopenharmony_ci .pmu_disable = fsl_emb_pmu_disable, 5768c2ecf20Sopenharmony_ci .event_init = fsl_emb_pmu_event_init, 5778c2ecf20Sopenharmony_ci .add = fsl_emb_pmu_add, 5788c2ecf20Sopenharmony_ci .del = fsl_emb_pmu_del, 5798c2ecf20Sopenharmony_ci .start = fsl_emb_pmu_start, 5808c2ecf20Sopenharmony_ci .stop = fsl_emb_pmu_stop, 5818c2ecf20Sopenharmony_ci .read = fsl_emb_pmu_read, 5828c2ecf20Sopenharmony_ci}; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* 5858c2ecf20Sopenharmony_ci * A counter has overflowed; update its count and record 5868c2ecf20Sopenharmony_ci * things if requested. Note that interrupts are hard-disabled 5878c2ecf20Sopenharmony_ci * here so there is no possibility of being interrupted. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_cistatic void record_and_restart(struct perf_event *event, unsigned long val, 5908c2ecf20Sopenharmony_ci struct pt_regs *regs) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci u64 period = event->hw.sample_period; 5938c2ecf20Sopenharmony_ci s64 prev, delta, left; 5948c2ecf20Sopenharmony_ci int record = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) { 5978c2ecf20Sopenharmony_ci write_pmc(event->hw.idx, 0); 5988c2ecf20Sopenharmony_ci return; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* we don't have to worry about interrupts here */ 6028c2ecf20Sopenharmony_ci prev = local64_read(&event->hw.prev_count); 6038c2ecf20Sopenharmony_ci delta = (val - prev) & 0xfffffffful; 6048c2ecf20Sopenharmony_ci local64_add(delta, &event->count); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* 6078c2ecf20Sopenharmony_ci * See if the total period for this event has expired, 6088c2ecf20Sopenharmony_ci * and update for the next period. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci val = 0; 6118c2ecf20Sopenharmony_ci left = local64_read(&event->hw.period_left) - delta; 6128c2ecf20Sopenharmony_ci if (period) { 6138c2ecf20Sopenharmony_ci if (left <= 0) { 6148c2ecf20Sopenharmony_ci left += period; 6158c2ecf20Sopenharmony_ci if (left <= 0) 6168c2ecf20Sopenharmony_ci left = period; 6178c2ecf20Sopenharmony_ci record = 1; 6188c2ecf20Sopenharmony_ci event->hw.last_period = event->hw.sample_period; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci if (left < 0x80000000LL) 6218c2ecf20Sopenharmony_ci val = 0x80000000LL - left; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci write_pmc(event->hw.idx, val); 6258c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, val); 6268c2ecf20Sopenharmony_ci local64_set(&event->hw.period_left, left); 6278c2ecf20Sopenharmony_ci perf_event_update_userpage(event); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * Finally record data if requested. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci if (record) { 6338c2ecf20Sopenharmony_ci struct perf_sample_data data; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci perf_sample_data_init(&data, 0, event->hw.last_period); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (perf_event_overflow(event, &data, regs)) 6388c2ecf20Sopenharmony_ci fsl_emb_pmu_stop(event, 0); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic void perf_event_interrupt(struct pt_regs *regs) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci int i; 6458c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); 6468c2ecf20Sopenharmony_ci struct perf_event *event; 6478c2ecf20Sopenharmony_ci unsigned long val; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci for (i = 0; i < ppmu->n_counter; ++i) { 6508c2ecf20Sopenharmony_ci event = cpuhw->event[i]; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci val = read_pmc(i); 6538c2ecf20Sopenharmony_ci if ((int)val < 0) { 6548c2ecf20Sopenharmony_ci if (event) { 6558c2ecf20Sopenharmony_ci /* event has overflowed */ 6568c2ecf20Sopenharmony_ci record_and_restart(event, val, regs); 6578c2ecf20Sopenharmony_ci } else { 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * Disabled counter is negative, 6608c2ecf20Sopenharmony_ci * reset it just in case. 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci write_pmc(i, 0); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* PMM will keep counters frozen until we return from the interrupt. */ 6688c2ecf20Sopenharmony_ci mtmsr(mfmsr() | MSR_PMM); 6698c2ecf20Sopenharmony_ci mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); 6708c2ecf20Sopenharmony_ci isync(); 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic int fsl_emb_pmu_prepare_cpu(unsigned int cpu) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci memset(cpuhw, 0, sizeof(*cpuhw)); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint register_fsl_emb_pmu(struct fsl_emb_pmu *pmu) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci if (ppmu) 6858c2ecf20Sopenharmony_ci return -EBUSY; /* something's already registered */ 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ppmu = pmu; 6888c2ecf20Sopenharmony_ci pr_info("%s performance monitor hardware support registered\n", 6898c2ecf20Sopenharmony_ci pmu->name); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci perf_pmu_register(&fsl_emb_pmu, "cpu", PERF_TYPE_RAW); 6928c2ecf20Sopenharmony_ci cpuhp_setup_state(CPUHP_PERF_POWER, "perf/powerpc:prepare", 6938c2ecf20Sopenharmony_ci fsl_emb_pmu_prepare_cpu, NULL); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 697