1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Loongson2 performance counter driver for oprofile
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2009 Lemote Inc.
5*4882a593Smuzhiyun * Author: Yanhua <yanh@lemote.com>
6*4882a593Smuzhiyun * Author: Wu Zhangjin <wuzhangjin@gmail.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
9*4882a593Smuzhiyun * License. See the file "COPYING" in the main directory of this archive
10*4882a593Smuzhiyun * for more details.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/oprofile.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <loongson.h> /* LOONGSON2_PERFCNT_IRQ */
17*4882a593Smuzhiyun #include "op_impl.h"
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define LOONGSON2_CPU_TYPE "mips/loongson2"
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #define LOONGSON2_PERFCNT_OVERFLOW (1ULL << 31)
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_EXL (1UL << 0)
24*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_KERNEL (1UL << 1)
25*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_SUPERVISOR (1UL << 2)
26*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_USER (1UL << 3)
27*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_ENABLE (1UL << 4)
28*4882a593Smuzhiyun #define LOONGSON2_PERFCTRL_EVENT(idx, event) \
29*4882a593Smuzhiyun (((event) & 0x0f) << ((idx) ? 9 : 5))
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
32*4882a593Smuzhiyun #define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
33*4882a593Smuzhiyun #define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
34*4882a593Smuzhiyun #define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static struct loongson2_register_config {
37*4882a593Smuzhiyun unsigned int ctrl;
38*4882a593Smuzhiyun unsigned long long reset_counter1;
39*4882a593Smuzhiyun unsigned long long reset_counter2;
40*4882a593Smuzhiyun int cnt1_enabled, cnt2_enabled;
41*4882a593Smuzhiyun } reg;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static char *oprofid = "LoongsonPerf";
44*4882a593Smuzhiyun static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
45*4882a593Smuzhiyun
reset_counters(void * arg)46*4882a593Smuzhiyun static void reset_counters(void *arg)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun write_c0_perfctrl(0);
49*4882a593Smuzhiyun write_c0_perfcnt(0);
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
loongson2_reg_setup(struct op_counter_config * cfg)52*4882a593Smuzhiyun static void loongson2_reg_setup(struct op_counter_config *cfg)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun unsigned int ctrl = 0;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun reg.reset_counter1 = 0;
57*4882a593Smuzhiyun reg.reset_counter2 = 0;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun /*
60*4882a593Smuzhiyun * Compute the performance counter ctrl word.
61*4882a593Smuzhiyun * For now, count kernel and user mode.
62*4882a593Smuzhiyun */
63*4882a593Smuzhiyun if (cfg[0].enabled) {
64*4882a593Smuzhiyun ctrl |= LOONGSON2_PERFCTRL_EVENT(0, cfg[0].event);
65*4882a593Smuzhiyun reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun if (cfg[1].enabled) {
69*4882a593Smuzhiyun ctrl |= LOONGSON2_PERFCTRL_EVENT(1, cfg[1].event);
70*4882a593Smuzhiyun reg.reset_counter2 = 0x80000000ULL - cfg[1].count;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun if (cfg[0].enabled || cfg[1].enabled) {
74*4882a593Smuzhiyun ctrl |= LOONGSON2_PERFCTRL_EXL | LOONGSON2_PERFCTRL_ENABLE;
75*4882a593Smuzhiyun if (cfg[0].kernel || cfg[1].kernel)
76*4882a593Smuzhiyun ctrl |= LOONGSON2_PERFCTRL_KERNEL;
77*4882a593Smuzhiyun if (cfg[0].user || cfg[1].user)
78*4882a593Smuzhiyun ctrl |= LOONGSON2_PERFCTRL_USER;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun reg.ctrl = ctrl;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun reg.cnt1_enabled = cfg[0].enabled;
84*4882a593Smuzhiyun reg.cnt2_enabled = cfg[1].enabled;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
loongson2_cpu_setup(void * args)87*4882a593Smuzhiyun static void loongson2_cpu_setup(void *args)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun write_c0_perfcnt((reg.reset_counter2 << 32) | reg.reset_counter1);
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
loongson2_cpu_start(void * args)92*4882a593Smuzhiyun static void loongson2_cpu_start(void *args)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun /* Start all counters on current CPU */
95*4882a593Smuzhiyun if (reg.cnt1_enabled || reg.cnt2_enabled)
96*4882a593Smuzhiyun write_c0_perfctrl(reg.ctrl);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
loongson2_cpu_stop(void * args)99*4882a593Smuzhiyun static void loongson2_cpu_stop(void *args)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun /* Stop all counters on current CPU */
102*4882a593Smuzhiyun write_c0_perfctrl(0);
103*4882a593Smuzhiyun memset(®, 0, sizeof(reg));
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
loongson2_perfcount_handler(int irq,void * dev_id)106*4882a593Smuzhiyun static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun uint64_t counter, counter1, counter2;
109*4882a593Smuzhiyun struct pt_regs *regs = get_irq_regs();
110*4882a593Smuzhiyun int enabled;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun /* Check whether the irq belongs to me */
113*4882a593Smuzhiyun enabled = read_c0_perfctrl() & LOONGSON2_PERFCTRL_ENABLE;
114*4882a593Smuzhiyun if (!enabled)
115*4882a593Smuzhiyun return IRQ_NONE;
116*4882a593Smuzhiyun enabled = reg.cnt1_enabled | reg.cnt2_enabled;
117*4882a593Smuzhiyun if (!enabled)
118*4882a593Smuzhiyun return IRQ_NONE;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun counter = read_c0_perfcnt();
121*4882a593Smuzhiyun counter1 = counter & 0xffffffff;
122*4882a593Smuzhiyun counter2 = counter >> 32;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
125*4882a593Smuzhiyun if (reg.cnt1_enabled)
126*4882a593Smuzhiyun oprofile_add_sample(regs, 0);
127*4882a593Smuzhiyun counter1 = reg.reset_counter1;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
130*4882a593Smuzhiyun if (reg.cnt2_enabled)
131*4882a593Smuzhiyun oprofile_add_sample(regs, 1);
132*4882a593Smuzhiyun counter2 = reg.reset_counter2;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun write_c0_perfcnt((counter2 << 32) | counter1);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun return IRQ_HANDLED;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
loongson2_init(void)140*4882a593Smuzhiyun static int __init loongson2_init(void)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
143*4882a593Smuzhiyun IRQF_SHARED, "Perfcounter", oprofid);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
loongson2_exit(void)146*4882a593Smuzhiyun static void loongson2_exit(void)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun reset_counters(NULL);
149*4882a593Smuzhiyun free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun struct op_mips_model op_model_loongson2_ops = {
153*4882a593Smuzhiyun .reg_setup = loongson2_reg_setup,
154*4882a593Smuzhiyun .cpu_setup = loongson2_cpu_setup,
155*4882a593Smuzhiyun .init = loongson2_init,
156*4882a593Smuzhiyun .exit = loongson2_exit,
157*4882a593Smuzhiyun .cpu_start = loongson2_cpu_start,
158*4882a593Smuzhiyun .cpu_stop = loongson2_cpu_stop,
159*4882a593Smuzhiyun .cpu_type = LOONGSON2_CPU_TYPE,
160*4882a593Smuzhiyun .num_counters = 2
161*4882a593Smuzhiyun };
162