2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
7 #include <linux/init.h>
10 #include <linux/proc_fs.h>
11 #include <linux/oprofile.h>
12 #include <linux/spinlock.h>
13 #include <linux/interrupt.h>
14 #include <linux/uaccess.h>
19 #define LOONGSON3_PERFCNT_OVERFLOW (1ULL << 63)
21 #define LOONGSON3_PERFCTRL_EXL (1UL << 0)
22 #define LOONGSON3_PERFCTRL_KERNEL (1UL << 1)
23 #define LOONGSON3_PERFCTRL_SUPERVISOR (1UL << 2)
24 #define LOONGSON3_PERFCTRL_USER (1UL << 3)
25 #define LOONGSON3_PERFCTRL_ENABLE (1UL << 4)
26 #define LOONGSON3_PERFCTRL_W (1UL << 30)
27 #define LOONGSON3_PERFCTRL_M (1UL << 31)
28 #define LOONGSON3_PERFCTRL_EVENT(idx, event) \
29 (((event) & (idx ? 0x0f : 0x3f)) << 5)
31 /* Loongson-3 PerfCount performance counter1 register */
32 #define read_c0_perflo1() __read_64bit_c0_register($25, 0)
33 #define write_c0_perflo1(val) __write_64bit_c0_register($25, 0, val)
34 #define read_c0_perfhi1() __read_64bit_c0_register($25, 1)
35 #define write_c0_perfhi1(val) __write_64bit_c0_register($25, 1, val)
37 /* Loongson-3 PerfCount performance counter2 register */
38 #define read_c0_perflo2() __read_64bit_c0_register($25, 2)
39 #define write_c0_perflo2(val) __write_64bit_c0_register($25, 2, val)
40 #define read_c0_perfhi2() __read_64bit_c0_register($25, 3)
41 #define write_c0_perfhi2(val) __write_64bit_c0_register($25, 3, val)
43 static int (*save_perf_irq
)(void);
45 static struct loongson3_register_config
{
46 unsigned int control1
;
47 unsigned int control2
;
48 unsigned long long reset_counter1
;
49 unsigned long long reset_counter2
;
50 int ctr1_enable
, ctr2_enable
;
53 static void reset_counters(void *arg
)
57 write_c0_perflo1(0xc0000000);
58 write_c0_perflo2(0x40000000);
61 /* Compute all of the registers in preparation for enabling profiling. */
62 static void loongson3_reg_setup(struct op_counter_config
*ctr
)
64 unsigned int control1
= 0;
65 unsigned int control2
= 0;
67 reg
.reset_counter1
= 0;
68 reg
.reset_counter2
= 0;
69 /* Compute the performance counter control word. */
70 /* For now count kernel and user mode */
72 control1
|= LOONGSON3_PERFCTRL_EVENT(0, ctr
[0].event
) |
73 LOONGSON3_PERFCTRL_ENABLE
;
75 control1
|= LOONGSON3_PERFCTRL_KERNEL
;
77 control1
|= LOONGSON3_PERFCTRL_USER
;
78 reg
.reset_counter1
= 0x8000000000000000ULL
- ctr
[0].count
;
82 control2
|= LOONGSON3_PERFCTRL_EVENT(1, ctr
[1].event
) |
83 LOONGSON3_PERFCTRL_ENABLE
;
85 control2
|= LOONGSON3_PERFCTRL_KERNEL
;
87 control2
|= LOONGSON3_PERFCTRL_USER
;
88 reg
.reset_counter2
= 0x8000000000000000ULL
- ctr
[1].count
;
92 control1
|= LOONGSON3_PERFCTRL_EXL
;
94 control2
|= LOONGSON3_PERFCTRL_EXL
;
96 reg
.control1
= control1
;
97 reg
.control2
= control2
;
98 reg
.ctr1_enable
= ctr
[0].enabled
;
99 reg
.ctr2_enable
= ctr
[1].enabled
;
102 /* Program all of the registers in preparation for enabling profiling. */
103 static void loongson3_cpu_setup(void *args
)
105 uint64_t perfcount1
, perfcount2
;
107 perfcount1
= reg
.reset_counter1
;
108 perfcount2
= reg
.reset_counter2
;
109 write_c0_perfhi1(perfcount1
);
110 write_c0_perfhi2(perfcount2
);
113 static void loongson3_cpu_start(void *args
)
115 /* Start all counters on current CPU */
116 reg
.control1
|= (LOONGSON3_PERFCTRL_W
|LOONGSON3_PERFCTRL_M
);
117 reg
.control2
|= (LOONGSON3_PERFCTRL_W
|LOONGSON3_PERFCTRL_M
);
120 write_c0_perflo1(reg
.control1
);
122 write_c0_perflo2(reg
.control2
);
125 static void loongson3_cpu_stop(void *args
)
127 /* Stop all counters on current CPU */
128 write_c0_perflo1(0xc0000000);
129 write_c0_perflo2(0x40000000);
130 memset(®
, 0, sizeof(reg
));
133 static int loongson3_perfcount_handler(void)
136 uint64_t counter1
, counter2
;
137 uint32_t cause
, handled
= IRQ_NONE
;
138 struct pt_regs
*regs
= get_irq_regs();
140 cause
= read_c0_cause();
141 if (!(cause
& CAUSEF_PCI
))
144 counter1
= read_c0_perfhi1();
145 counter2
= read_c0_perfhi2();
147 local_irq_save(flags
);
149 if (counter1
& LOONGSON3_PERFCNT_OVERFLOW
) {
151 oprofile_add_sample(regs
, 0);
152 counter1
= reg
.reset_counter1
;
154 if (counter2
& LOONGSON3_PERFCNT_OVERFLOW
) {
156 oprofile_add_sample(regs
, 1);
157 counter2
= reg
.reset_counter2
;
160 local_irq_restore(flags
);
162 write_c0_perfhi1(counter1
);
163 write_c0_perfhi2(counter2
);
165 if (!(cause
& CAUSEF_TI
))
166 handled
= IRQ_HANDLED
;
171 static int loongson3_starting_cpu(unsigned int cpu
)
173 write_c0_perflo1(reg
.control1
);
174 write_c0_perflo2(reg
.control2
);
178 static int loongson3_dying_cpu(unsigned int cpu
)
180 write_c0_perflo1(0xc0000000);
181 write_c0_perflo2(0x40000000);
185 static int __init
loongson3_init(void)
187 on_each_cpu(reset_counters
, NULL
, 1);
188 cpuhp_setup_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING
,
189 "mips/oprofile/loongson3:starting",
190 loongson3_starting_cpu
, loongson3_dying_cpu
);
191 save_perf_irq
= perf_irq
;
192 perf_irq
= loongson3_perfcount_handler
;
197 static void loongson3_exit(void)
199 on_each_cpu(reset_counters
, NULL
, 1);
200 cpuhp_remove_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING
);
201 perf_irq
= save_perf_irq
;
204 struct op_mips_model op_model_loongson3_ops
= {
205 .reg_setup
= loongson3_reg_setup
,
206 .cpu_setup
= loongson3_cpu_setup
,
207 .init
= loongson3_init
,
208 .exit
= loongson3_exit
,
209 .cpu_start
= loongson3_cpu_start
,
210 .cpu_stop
= loongson3_cpu_stop
,
211 .cpu_type
= "mips/loongson3",