Back to home page

LXR

 
 

    


0001 /*
0002  * Loongson2 performance counter driver for oprofile
0003  *
0004  * Copyright (C) 2009 Lemote Inc.
0005  * Author: Yanhua <yanh@lemote.com>
0006  * Author: Wu Zhangjin <wuzhangjin@gmail.com>
0007  *
0008  * This file is subject to the terms and conditions of the GNU General Public
0009  * License.  See the file "COPYING" in the main directory of this archive
0010  * for more details.
0011  */
0012 #include <linux/init.h>
0013 #include <linux/oprofile.h>
0014 #include <linux/interrupt.h>
0015 
0016 #include <loongson.h>           /* LOONGSON2_PERFCNT_IRQ */
0017 #include "op_impl.h"
0018 
0019 #define LOONGSON2_CPU_TYPE  "mips/loongson2"
0020 
0021 #define LOONGSON2_PERFCNT_OVERFLOW      (1ULL   << 31)
0022 
0023 #define LOONGSON2_PERFCTRL_EXL          (1UL    <<  0)
0024 #define LOONGSON2_PERFCTRL_KERNEL       (1UL    <<  1)
0025 #define LOONGSON2_PERFCTRL_SUPERVISOR       (1UL    <<  2)
0026 #define LOONGSON2_PERFCTRL_USER         (1UL    <<  3)
0027 #define LOONGSON2_PERFCTRL_ENABLE       (1UL    <<  4)
0028 #define LOONGSON2_PERFCTRL_EVENT(idx, event) \
0029     (((event) & 0x0f) << ((idx) ? 9 : 5))
0030 
0031 #define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
0032 #define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
0033 #define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
0034 #define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
0035 
0036 static struct loongson2_register_config {
0037     unsigned int ctrl;
0038     unsigned long long reset_counter1;
0039     unsigned long long reset_counter2;
0040     int cnt1_enabled, cnt2_enabled;
0041 } reg;
0042 
0043 static char *oprofid = "LoongsonPerf";
0044 static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
0045 
0046 static void reset_counters(void *arg)
0047 {
0048     write_c0_perfctrl(0);
0049     write_c0_perfcnt(0);
0050 }
0051 
0052 static void loongson2_reg_setup(struct op_counter_config *cfg)
0053 {
0054     unsigned int ctrl = 0;
0055 
0056     reg.reset_counter1 = 0;
0057     reg.reset_counter2 = 0;
0058 
0059     /*
0060      * Compute the performance counter ctrl word.
0061      * For now, count kernel and user mode.
0062      */
0063     if (cfg[0].enabled) {
0064         ctrl |= LOONGSON2_PERFCTRL_EVENT(0, cfg[0].event);
0065         reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
0066     }
0067 
0068     if (cfg[1].enabled) {
0069         ctrl |= LOONGSON2_PERFCTRL_EVENT(1, cfg[1].event);
0070         reg.reset_counter2 = 0x80000000ULL - cfg[1].count;
0071     }
0072 
0073     if (cfg[0].enabled || cfg[1].enabled) {
0074         ctrl |= LOONGSON2_PERFCTRL_EXL | LOONGSON2_PERFCTRL_ENABLE;
0075         if (cfg[0].kernel || cfg[1].kernel)
0076             ctrl |= LOONGSON2_PERFCTRL_KERNEL;
0077         if (cfg[0].user || cfg[1].user)
0078             ctrl |= LOONGSON2_PERFCTRL_USER;
0079     }
0080 
0081     reg.ctrl = ctrl;
0082 
0083     reg.cnt1_enabled = cfg[0].enabled;
0084     reg.cnt2_enabled = cfg[1].enabled;
0085 }
0086 
0087 static void loongson2_cpu_setup(void *args)
0088 {
0089     write_c0_perfcnt((reg.reset_counter2 << 32) | reg.reset_counter1);
0090 }
0091 
0092 static void loongson2_cpu_start(void *args)
0093 {
0094     /* Start all counters on current CPU */
0095     if (reg.cnt1_enabled || reg.cnt2_enabled)
0096         write_c0_perfctrl(reg.ctrl);
0097 }
0098 
0099 static void loongson2_cpu_stop(void *args)
0100 {
0101     /* Stop all counters on current CPU */
0102     write_c0_perfctrl(0);
0103     memset(&reg, 0, sizeof(reg));
0104 }
0105 
0106 static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
0107 {
0108     uint64_t counter, counter1, counter2;
0109     struct pt_regs *regs = get_irq_regs();
0110     int enabled;
0111 
0112     /* Check whether the irq belongs to me */
0113     enabled = read_c0_perfctrl() & LOONGSON2_PERFCTRL_ENABLE;
0114     if (!enabled)
0115         return IRQ_NONE;
0116     enabled = reg.cnt1_enabled | reg.cnt2_enabled;
0117     if (!enabled)
0118         return IRQ_NONE;
0119 
0120     counter = read_c0_perfcnt();
0121     counter1 = counter & 0xffffffff;
0122     counter2 = counter >> 32;
0123 
0124     if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
0125         if (reg.cnt1_enabled)
0126             oprofile_add_sample(regs, 0);
0127         counter1 = reg.reset_counter1;
0128     }
0129     if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
0130         if (reg.cnt2_enabled)
0131             oprofile_add_sample(regs, 1);
0132         counter2 = reg.reset_counter2;
0133     }
0134 
0135     write_c0_perfcnt((counter2 << 32) | counter1);
0136 
0137     return IRQ_HANDLED;
0138 }
0139 
0140 static int __init loongson2_init(void)
0141 {
0142     return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
0143                IRQF_SHARED, "Perfcounter", oprofid);
0144 }
0145 
0146 static void loongson2_exit(void)
0147 {
0148     reset_counters(NULL);
0149     free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
0150 }
0151 
0152 struct op_mips_model op_model_loongson2_ops = {
0153     .reg_setup = loongson2_reg_setup,
0154     .cpu_setup = loongson2_cpu_setup,
0155     .init = loongson2_init,
0156     .exit = loongson2_exit,
0157     .cpu_start = loongson2_cpu_start,
0158     .cpu_stop = loongson2_cpu_stop,
0159     .cpu_type = LOONGSON2_CPU_TYPE,
0160     .num_counters = 2
0161 };