18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/cpu.h> 98c2ecf20Sopenharmony_ci#include <linux/smp.h> 108c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 118c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <irq.h> 168c2ecf20Sopenharmony_ci#include <loongson.h> 178c2ecf20Sopenharmony_ci#include "op_impl.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCNT_OVERFLOW (1ULL << 63) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_EXL (1UL << 0) 228c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_KERNEL (1UL << 1) 238c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_SUPERVISOR (1UL << 2) 248c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_USER (1UL << 3) 258c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_ENABLE (1UL << 4) 268c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_W (1UL << 30) 278c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_M (1UL << 31) 288c2ecf20Sopenharmony_ci#define LOONGSON3_PERFCTRL_EVENT(idx, event) \ 298c2ecf20Sopenharmony_ci (((event) & (idx ? 0x0f : 0x3f)) << 5) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Loongson-3 PerfCount performance counter1 register */ 328c2ecf20Sopenharmony_ci#define read_c0_perflo1() __read_64bit_c0_register($25, 0) 338c2ecf20Sopenharmony_ci#define write_c0_perflo1(val) __write_64bit_c0_register($25, 0, val) 348c2ecf20Sopenharmony_ci#define read_c0_perfhi1() __read_64bit_c0_register($25, 1) 358c2ecf20Sopenharmony_ci#define write_c0_perfhi1(val) __write_64bit_c0_register($25, 1, val) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Loongson-3 PerfCount performance counter2 register */ 388c2ecf20Sopenharmony_ci#define read_c0_perflo2() __read_64bit_c0_register($25, 2) 398c2ecf20Sopenharmony_ci#define write_c0_perflo2(val) __write_64bit_c0_register($25, 2, val) 408c2ecf20Sopenharmony_ci#define read_c0_perfhi2() __read_64bit_c0_register($25, 3) 418c2ecf20Sopenharmony_ci#define write_c0_perfhi2(val) __write_64bit_c0_register($25, 3, val) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int (*save_perf_irq)(void); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct loongson3_register_config { 468c2ecf20Sopenharmony_ci unsigned int control1; 478c2ecf20Sopenharmony_ci unsigned int control2; 488c2ecf20Sopenharmony_ci unsigned long long reset_counter1; 498c2ecf20Sopenharmony_ci unsigned long long reset_counter2; 508c2ecf20Sopenharmony_ci int ctr1_enable, ctr2_enable; 518c2ecf20Sopenharmony_ci} reg; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void reset_counters(void *arg) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci write_c0_perfhi1(0); 568c2ecf20Sopenharmony_ci write_c0_perfhi2(0); 578c2ecf20Sopenharmony_ci write_c0_perflo1(0xc0000000); 588c2ecf20Sopenharmony_ci write_c0_perflo2(0x40000000); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Compute all of the registers in preparation for enabling profiling. */ 628c2ecf20Sopenharmony_cistatic void loongson3_reg_setup(struct op_counter_config *ctr) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned int control1 = 0; 658c2ecf20Sopenharmony_ci unsigned int control2 = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci reg.reset_counter1 = 0; 688c2ecf20Sopenharmony_ci reg.reset_counter2 = 0; 698c2ecf20Sopenharmony_ci /* Compute the performance counter control word. */ 708c2ecf20Sopenharmony_ci /* For now count kernel and user mode */ 718c2ecf20Sopenharmony_ci if (ctr[0].enabled) { 728c2ecf20Sopenharmony_ci control1 |= LOONGSON3_PERFCTRL_EVENT(0, ctr[0].event) | 738c2ecf20Sopenharmony_ci LOONGSON3_PERFCTRL_ENABLE; 748c2ecf20Sopenharmony_ci if (ctr[0].kernel) 758c2ecf20Sopenharmony_ci control1 |= LOONGSON3_PERFCTRL_KERNEL; 768c2ecf20Sopenharmony_ci if (ctr[0].user) 778c2ecf20Sopenharmony_ci control1 |= LOONGSON3_PERFCTRL_USER; 788c2ecf20Sopenharmony_ci reg.reset_counter1 = 0x8000000000000000ULL - ctr[0].count; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (ctr[1].enabled) { 828c2ecf20Sopenharmony_ci control2 |= LOONGSON3_PERFCTRL_EVENT(1, ctr[1].event) | 838c2ecf20Sopenharmony_ci LOONGSON3_PERFCTRL_ENABLE; 848c2ecf20Sopenharmony_ci if (ctr[1].kernel) 858c2ecf20Sopenharmony_ci control2 |= LOONGSON3_PERFCTRL_KERNEL; 868c2ecf20Sopenharmony_ci if (ctr[1].user) 878c2ecf20Sopenharmony_ci control2 |= LOONGSON3_PERFCTRL_USER; 888c2ecf20Sopenharmony_ci reg.reset_counter2 = 0x8000000000000000ULL - ctr[1].count; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (ctr[0].enabled) 928c2ecf20Sopenharmony_ci control1 |= LOONGSON3_PERFCTRL_EXL; 938c2ecf20Sopenharmony_ci if (ctr[1].enabled) 948c2ecf20Sopenharmony_ci control2 |= LOONGSON3_PERFCTRL_EXL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci reg.control1 = control1; 978c2ecf20Sopenharmony_ci reg.control2 = control2; 988c2ecf20Sopenharmony_ci reg.ctr1_enable = ctr[0].enabled; 998c2ecf20Sopenharmony_ci reg.ctr2_enable = ctr[1].enabled; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Program all of the registers in preparation for enabling profiling. */ 1038c2ecf20Sopenharmony_cistatic void loongson3_cpu_setup(void *args) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci uint64_t perfcount1, perfcount2; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci perfcount1 = reg.reset_counter1; 1088c2ecf20Sopenharmony_ci perfcount2 = reg.reset_counter2; 1098c2ecf20Sopenharmony_ci write_c0_perfhi1(perfcount1); 1108c2ecf20Sopenharmony_ci write_c0_perfhi2(perfcount2); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void loongson3_cpu_start(void *args) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci /* Start all counters on current CPU */ 1168c2ecf20Sopenharmony_ci reg.control1 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M); 1178c2ecf20Sopenharmony_ci reg.control2 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (reg.ctr1_enable) 1208c2ecf20Sopenharmony_ci write_c0_perflo1(reg.control1); 1218c2ecf20Sopenharmony_ci if (reg.ctr2_enable) 1228c2ecf20Sopenharmony_ci write_c0_perflo2(reg.control2); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void loongson3_cpu_stop(void *args) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci /* Stop all counters on current CPU */ 1288c2ecf20Sopenharmony_ci write_c0_perflo1(0xc0000000); 1298c2ecf20Sopenharmony_ci write_c0_perflo2(0x40000000); 1308c2ecf20Sopenharmony_ci memset(®, 0, sizeof(reg)); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int loongson3_perfcount_handler(void) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci uint64_t counter1, counter2; 1378c2ecf20Sopenharmony_ci uint32_t cause, handled = IRQ_NONE; 1388c2ecf20Sopenharmony_ci struct pt_regs *regs = get_irq_regs(); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci cause = read_c0_cause(); 1418c2ecf20Sopenharmony_ci if (!(cause & CAUSEF_PCI)) 1428c2ecf20Sopenharmony_ci return handled; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci counter1 = read_c0_perfhi1(); 1458c2ecf20Sopenharmony_ci counter2 = read_c0_perfhi2(); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci local_irq_save(flags); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (counter1 & LOONGSON3_PERFCNT_OVERFLOW) { 1508c2ecf20Sopenharmony_ci if (reg.ctr1_enable) 1518c2ecf20Sopenharmony_ci oprofile_add_sample(regs, 0); 1528c2ecf20Sopenharmony_ci counter1 = reg.reset_counter1; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (counter2 & LOONGSON3_PERFCNT_OVERFLOW) { 1558c2ecf20Sopenharmony_ci if (reg.ctr2_enable) 1568c2ecf20Sopenharmony_ci oprofile_add_sample(regs, 1); 1578c2ecf20Sopenharmony_ci counter2 = reg.reset_counter2; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci local_irq_restore(flags); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci write_c0_perfhi1(counter1); 1638c2ecf20Sopenharmony_ci write_c0_perfhi2(counter2); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (!(cause & CAUSEF_TI)) 1668c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return handled; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int loongson3_starting_cpu(unsigned int cpu) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci write_c0_perflo1(reg.control1); 1748c2ecf20Sopenharmony_ci write_c0_perflo2(reg.control2); 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int loongson3_dying_cpu(unsigned int cpu) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci write_c0_perflo1(0xc0000000); 1818c2ecf20Sopenharmony_ci write_c0_perflo2(0x40000000); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int __init loongson3_init(void) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci on_each_cpu(reset_counters, NULL, 1); 1888c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING, 1898c2ecf20Sopenharmony_ci "mips/oprofile/loongson3:starting", 1908c2ecf20Sopenharmony_ci loongson3_starting_cpu, loongson3_dying_cpu); 1918c2ecf20Sopenharmony_ci save_perf_irq = perf_irq; 1928c2ecf20Sopenharmony_ci perf_irq = loongson3_perfcount_handler; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void loongson3_exit(void) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci on_each_cpu(reset_counters, NULL, 1); 2008c2ecf20Sopenharmony_ci cpuhp_remove_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING); 2018c2ecf20Sopenharmony_ci perf_irq = save_perf_irq; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistruct op_mips_model op_model_loongson3_ops = { 2058c2ecf20Sopenharmony_ci .reg_setup = loongson3_reg_setup, 2068c2ecf20Sopenharmony_ci .cpu_setup = loongson3_cpu_setup, 2078c2ecf20Sopenharmony_ci .init = loongson3_init, 2088c2ecf20Sopenharmony_ci .exit = loongson3_exit, 2098c2ecf20Sopenharmony_ci .cpu_start = loongson3_cpu_start, 2108c2ecf20Sopenharmony_ci .cpu_stop = loongson3_cpu_stop, 2118c2ecf20Sopenharmony_ci .cpu_type = "mips/loongson3", 2128c2ecf20Sopenharmony_ci .num_counters = 2 2138c2ecf20Sopenharmony_ci}; 214