18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 PA Semi, Inc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Shashi Rao, PA Semi 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Maintained by: Olof Johansson <olof@lixom.net> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on arch/powerpc/oprofile/op_model_power4.c 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 138c2ecf20Sopenharmony_ci#include <linux/smp.h> 148c2ecf20Sopenharmony_ci#include <linux/percpu.h> 158c2ecf20Sopenharmony_ci#include <asm/processor.h> 168c2ecf20Sopenharmony_ci#include <asm/cputable.h> 178c2ecf20Sopenharmony_ci#include <asm/oprofile_impl.h> 188c2ecf20Sopenharmony_ci#include <asm/reg.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic unsigned char oprofile_running; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */ 238c2ecf20Sopenharmony_cistatic u64 mmcr0_val; 248c2ecf20Sopenharmony_cistatic u64 mmcr1_val; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* inited in pa6t_reg_setup */ 278c2ecf20Sopenharmony_cistatic u64 reset_value[OP_MAX_COUNTER]; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline u64 ctr_read(unsigned int i) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci switch (i) { 328c2ecf20Sopenharmony_ci case 0: 338c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC0); 348c2ecf20Sopenharmony_ci case 1: 358c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC1); 368c2ecf20Sopenharmony_ci case 2: 378c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC2); 388c2ecf20Sopenharmony_ci case 3: 398c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC3); 408c2ecf20Sopenharmony_ci case 4: 418c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC4); 428c2ecf20Sopenharmony_ci case 5: 438c2ecf20Sopenharmony_ci return mfspr(SPRN_PA6T_PMC5); 448c2ecf20Sopenharmony_ci default: 458c2ecf20Sopenharmony_ci printk(KERN_ERR "ctr_read called with bad arg %u\n", i); 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline void ctr_write(unsigned int i, u64 val) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci switch (i) { 538c2ecf20Sopenharmony_ci case 0: 548c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC0, val); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case 1: 578c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC1, val); 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case 2: 608c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC2, val); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci case 3: 638c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC3, val); 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case 4: 668c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC4, val); 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci case 5: 698c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_PMC5, val); 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci default: 728c2ecf20Sopenharmony_ci printk(KERN_ERR "ctr_write called with bad arg %u\n", i); 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* precompute the values to stuff in the hardware registers */ 798c2ecf20Sopenharmony_cistatic int pa6t_reg_setup(struct op_counter_config *ctr, 808c2ecf20Sopenharmony_ci struct op_system_config *sys, 818c2ecf20Sopenharmony_ci int num_ctrs) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int pmc; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the 878c2ecf20Sopenharmony_ci * event_mappings file by turning off the counters that the user doesn't 888c2ecf20Sopenharmony_ci * care about 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * setup user and kernel profiling 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) 938c2ecf20Sopenharmony_ci if (!ctr[pmc].enabled) { 948c2ecf20Sopenharmony_ci sys->mmcr0 &= ~(0x1UL << pmc); 958c2ecf20Sopenharmony_ci sys->mmcr0 &= ~(0x1UL << (pmc+12)); 968c2ecf20Sopenharmony_ci pr_debug("turned off counter %u\n", pmc); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (sys->enable_kernel) 1008c2ecf20Sopenharmony_ci sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN; 1018c2ecf20Sopenharmony_ci else 1028c2ecf20Sopenharmony_ci sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (sys->enable_user) 1058c2ecf20Sopenharmony_ci sys->mmcr0 |= PA6T_MMCR0_PREN; 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci sys->mmcr0 &= ~PA6T_MMCR0_PREN; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * The performance counter event settings are given in the mmcr0 and 1118c2ecf20Sopenharmony_ci * mmcr1 values passed from the user in the op_system_config 1128c2ecf20Sopenharmony_ci * structure (sys variable). 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci mmcr0_val = sys->mmcr0; 1158c2ecf20Sopenharmony_ci mmcr1_val = sys->mmcr1; 1168c2ecf20Sopenharmony_ci pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0); 1178c2ecf20Sopenharmony_ci pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) { 1208c2ecf20Sopenharmony_ci /* counters are 40 bit. Move to cputable at some point? */ 1218c2ecf20Sopenharmony_ci reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count; 1228c2ecf20Sopenharmony_ci pr_debug("reset_value for pmc%u inited to 0x%llx\n", 1238c2ecf20Sopenharmony_ci pmc, reset_value[pmc]); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* configure registers on this cpu */ 1308c2ecf20Sopenharmony_cistatic int pa6t_cpu_setup(struct op_counter_config *ctr) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u64 mmcr0 = mmcr0_val; 1338c2ecf20Sopenharmony_ci u64 mmcr1 = mmcr1_val; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Default is all PMCs off */ 1368c2ecf20Sopenharmony_ci mmcr0 &= ~(0x3FUL); 1378c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR0, mmcr0); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* program selected programmable events in */ 1408c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR1, mmcr1); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(), 1438c2ecf20Sopenharmony_ci mfspr(SPRN_PA6T_MMCR0)); 1448c2ecf20Sopenharmony_ci pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(), 1458c2ecf20Sopenharmony_ci mfspr(SPRN_PA6T_MMCR1)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int pa6t_start(struct op_counter_config *ctr) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int i; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Hold off event counting until rfid */ 1558c2ecf20Sopenharmony_ci u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < cur_cpu_spec->num_pmcs; i++) 1588c2ecf20Sopenharmony_ci if (ctr[i].enabled) 1598c2ecf20Sopenharmony_ci ctr_write(i, reset_value[i]); 1608c2ecf20Sopenharmony_ci else 1618c2ecf20Sopenharmony_ci ctr_write(i, 0UL); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR0, mmcr0); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci oprofile_running = 1; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci pr_debug("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void pa6t_stop(void) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u64 mmcr0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* freeze counters */ 1778c2ecf20Sopenharmony_ci mmcr0 = mfspr(SPRN_PA6T_MMCR0); 1788c2ecf20Sopenharmony_ci mmcr0 |= PA6T_MMCR0_FCM0; 1798c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR0, mmcr0); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci oprofile_running = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pr_debug("stop on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* handle the perfmon overflow vector */ 1878c2ecf20Sopenharmony_cistatic void pa6t_handle_interrupt(struct pt_regs *regs, 1888c2ecf20Sopenharmony_ci struct op_counter_config *ctr) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long pc = mfspr(SPRN_PA6T_SIAR); 1918c2ecf20Sopenharmony_ci int is_kernel = is_kernel_addr(pc); 1928c2ecf20Sopenharmony_ci u64 val; 1938c2ecf20Sopenharmony_ci int i; 1948c2ecf20Sopenharmony_ci u64 mmcr0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* disable perfmon counting until rfid */ 1978c2ecf20Sopenharmony_ci mmcr0 = mfspr(SPRN_PA6T_MMCR0); 1988c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Record samples. We've got one global bit for whether a sample 2018c2ecf20Sopenharmony_ci * was taken, so add it for any counter that triggered overflow. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci for (i = 0; i < cur_cpu_spec->num_pmcs; i++) { 2048c2ecf20Sopenharmony_ci val = ctr_read(i); 2058c2ecf20Sopenharmony_ci if (val & (0x1UL << 39)) { /* Overflow bit set */ 2068c2ecf20Sopenharmony_ci if (oprofile_running && ctr[i].enabled) { 2078c2ecf20Sopenharmony_ci if (mmcr0 & PA6T_MMCR0_SIARLOG) 2088c2ecf20Sopenharmony_ci oprofile_add_ext_sample(pc, regs, i, is_kernel); 2098c2ecf20Sopenharmony_ci ctr_write(i, reset_value[i]); 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci ctr_write(i, 0UL); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Restore mmcr0 to a good known value since the PMI changes it */ 2178c2ecf20Sopenharmony_ci mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; 2188c2ecf20Sopenharmony_ci mtspr(SPRN_PA6T_MMCR0, mmcr0); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct op_powerpc_model op_model_pa6t = { 2228c2ecf20Sopenharmony_ci .reg_setup = pa6t_reg_setup, 2238c2ecf20Sopenharmony_ci .cpu_setup = pa6t_cpu_setup, 2248c2ecf20Sopenharmony_ci .start = pa6t_start, 2258c2ecf20Sopenharmony_ci .stop = pa6t_stop, 2268c2ecf20Sopenharmony_ci .handle_interrupt = pa6t_handle_interrupt, 2278c2ecf20Sopenharmony_ci}; 228