18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Loongson2 performance counter driver for oprofile 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2009 Lemote Inc. 58c2ecf20Sopenharmony_ci * Author: Yanhua <yanh@lemote.com> 68c2ecf20Sopenharmony_ci * Author: Wu Zhangjin <wuzhangjin@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 108c2ecf20Sopenharmony_ci * for more details. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <loongson.h> /* LOONGSON2_PERFCNT_IRQ */ 178c2ecf20Sopenharmony_ci#include "op_impl.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define LOONGSON2_CPU_TYPE "mips/loongson2" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCNT_OVERFLOW (1ULL << 31) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_EXL (1UL << 0) 248c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_KERNEL (1UL << 1) 258c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_SUPERVISOR (1UL << 2) 268c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_USER (1UL << 3) 278c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_ENABLE (1UL << 4) 288c2ecf20Sopenharmony_ci#define LOONGSON2_PERFCTRL_EVENT(idx, event) \ 298c2ecf20Sopenharmony_ci (((event) & 0x0f) << ((idx) ? 9 : 5)) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define read_c0_perfctrl() __read_64bit_c0_register($24, 0) 328c2ecf20Sopenharmony_ci#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val) 338c2ecf20Sopenharmony_ci#define read_c0_perfcnt() __read_64bit_c0_register($25, 0) 348c2ecf20Sopenharmony_ci#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct loongson2_register_config { 378c2ecf20Sopenharmony_ci unsigned int ctrl; 388c2ecf20Sopenharmony_ci unsigned long long reset_counter1; 398c2ecf20Sopenharmony_ci unsigned long long reset_counter2; 408c2ecf20Sopenharmony_ci int cnt1_enabled, cnt2_enabled; 418c2ecf20Sopenharmony_ci} reg; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic char *oprofid = "LoongsonPerf"; 448c2ecf20Sopenharmony_cistatic irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void reset_counters(void *arg) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci write_c0_perfctrl(0); 498c2ecf20Sopenharmony_ci write_c0_perfcnt(0); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void loongson2_reg_setup(struct op_counter_config *cfg) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned int ctrl = 0; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci reg.reset_counter1 = 0; 578c2ecf20Sopenharmony_ci reg.reset_counter2 = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Compute the performance counter ctrl word. 618c2ecf20Sopenharmony_ci * For now, count kernel and user mode. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci if (cfg[0].enabled) { 648c2ecf20Sopenharmony_ci ctrl |= LOONGSON2_PERFCTRL_EVENT(0, cfg[0].event); 658c2ecf20Sopenharmony_ci reg.reset_counter1 = 0x80000000ULL - cfg[0].count; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (cfg[1].enabled) { 698c2ecf20Sopenharmony_ci ctrl |= LOONGSON2_PERFCTRL_EVENT(1, cfg[1].event); 708c2ecf20Sopenharmony_ci reg.reset_counter2 = 0x80000000ULL - cfg[1].count; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (cfg[0].enabled || cfg[1].enabled) { 748c2ecf20Sopenharmony_ci ctrl |= LOONGSON2_PERFCTRL_EXL | LOONGSON2_PERFCTRL_ENABLE; 758c2ecf20Sopenharmony_ci if (cfg[0].kernel || cfg[1].kernel) 768c2ecf20Sopenharmony_ci ctrl |= LOONGSON2_PERFCTRL_KERNEL; 778c2ecf20Sopenharmony_ci if (cfg[0].user || cfg[1].user) 788c2ecf20Sopenharmony_ci ctrl |= LOONGSON2_PERFCTRL_USER; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci reg.ctrl = ctrl; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci reg.cnt1_enabled = cfg[0].enabled; 848c2ecf20Sopenharmony_ci reg.cnt2_enabled = cfg[1].enabled; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void loongson2_cpu_setup(void *args) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci write_c0_perfcnt((reg.reset_counter2 << 32) | reg.reset_counter1); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void loongson2_cpu_start(void *args) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci /* Start all counters on current CPU */ 958c2ecf20Sopenharmony_ci if (reg.cnt1_enabled || reg.cnt2_enabled) 968c2ecf20Sopenharmony_ci write_c0_perfctrl(reg.ctrl); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void loongson2_cpu_stop(void *args) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci /* Stop all counters on current CPU */ 1028c2ecf20Sopenharmony_ci write_c0_perfctrl(0); 1038c2ecf20Sopenharmony_ci memset(®, 0, sizeof(reg)); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci uint64_t counter, counter1, counter2; 1098c2ecf20Sopenharmony_ci struct pt_regs *regs = get_irq_regs(); 1108c2ecf20Sopenharmony_ci int enabled; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Check whether the irq belongs to me */ 1138c2ecf20Sopenharmony_ci enabled = read_c0_perfctrl() & LOONGSON2_PERFCTRL_ENABLE; 1148c2ecf20Sopenharmony_ci if (!enabled) 1158c2ecf20Sopenharmony_ci return IRQ_NONE; 1168c2ecf20Sopenharmony_ci enabled = reg.cnt1_enabled | reg.cnt2_enabled; 1178c2ecf20Sopenharmony_ci if (!enabled) 1188c2ecf20Sopenharmony_ci return IRQ_NONE; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci counter = read_c0_perfcnt(); 1218c2ecf20Sopenharmony_ci counter1 = counter & 0xffffffff; 1228c2ecf20Sopenharmony_ci counter2 = counter >> 32; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) { 1258c2ecf20Sopenharmony_ci if (reg.cnt1_enabled) 1268c2ecf20Sopenharmony_ci oprofile_add_sample(regs, 0); 1278c2ecf20Sopenharmony_ci counter1 = reg.reset_counter1; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) { 1308c2ecf20Sopenharmony_ci if (reg.cnt2_enabled) 1318c2ecf20Sopenharmony_ci oprofile_add_sample(regs, 1); 1328c2ecf20Sopenharmony_ci counter2 = reg.reset_counter2; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci write_c0_perfcnt((counter2 << 32) | counter1); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int __init loongson2_init(void) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler, 1438c2ecf20Sopenharmony_ci IRQF_SHARED, "Perfcounter", oprofid); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void loongson2_exit(void) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci reset_counters(NULL); 1498c2ecf20Sopenharmony_ci free_irq(LOONGSON2_PERFCNT_IRQ, oprofid); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistruct op_mips_model op_model_loongson2_ops = { 1538c2ecf20Sopenharmony_ci .reg_setup = loongson2_reg_setup, 1548c2ecf20Sopenharmony_ci .cpu_setup = loongson2_cpu_setup, 1558c2ecf20Sopenharmony_ci .init = loongson2_init, 1568c2ecf20Sopenharmony_ci .exit = loongson2_exit, 1578c2ecf20Sopenharmony_ci .cpu_start = loongson2_cpu_start, 1588c2ecf20Sopenharmony_ci .cpu_stop = loongson2_cpu_stop, 1598c2ecf20Sopenharmony_ci .cpu_type = LOONGSON2_CPU_TYPE, 1608c2ecf20Sopenharmony_ci .num_counters = 2 1618c2ecf20Sopenharmony_ci}; 162