xref: /OK3568_Linux_fs/kernel/arch/mips/oprofile/op_model_loongson2.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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(&reg, 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