Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
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 | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2004 by Ralf Baechle | |
7 | */ | |
cc26b815 | 8 | #include <linux/init.h> |
1da177e4 LT |
9 | #include <linux/oprofile.h> |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/smp.h> | |
12 | ||
13 | #include "op_impl.h" | |
14 | ||
15 | #define RM9K_COUNTER1_EVENT(event) ((event) << 0) | |
16 | #define RM9K_COUNTER1_SUPERVISOR (1ULL << 7) | |
17 | #define RM9K_COUNTER1_KERNEL (1ULL << 8) | |
18 | #define RM9K_COUNTER1_USER (1ULL << 9) | |
19 | #define RM9K_COUNTER1_ENABLE (1ULL << 10) | |
20 | #define RM9K_COUNTER1_OVERFLOW (1ULL << 15) | |
21 | ||
22 | #define RM9K_COUNTER2_EVENT(event) ((event) << 16) | |
23 | #define RM9K_COUNTER2_SUPERVISOR (1ULL << 23) | |
24 | #define RM9K_COUNTER2_KERNEL (1ULL << 24) | |
25 | #define RM9K_COUNTER2_USER (1ULL << 25) | |
26 | #define RM9K_COUNTER2_ENABLE (1ULL << 26) | |
27 | #define RM9K_COUNTER2_OVERFLOW (1ULL << 31) | |
28 | ||
29 | extern unsigned int rm9000_perfcount_irq; | |
30 | ||
31 | static struct rm9k_register_config { | |
32 | unsigned int control; | |
33 | unsigned int reset_counter1; | |
34 | unsigned int reset_counter2; | |
35 | } reg; | |
36 | ||
37 | /* Compute all of the registers in preparation for enabling profiling. */ | |
38 | ||
39 | static void rm9000_reg_setup(struct op_counter_config *ctr) | |
40 | { | |
41 | unsigned int control = 0; | |
42 | ||
43 | /* Compute the performance counter control word. */ | |
44 | /* For now count kernel and user mode */ | |
45 | if (ctr[0].enabled) | |
46 | control |= RM9K_COUNTER1_EVENT(ctr[0].event) | | |
47 | RM9K_COUNTER1_KERNEL | | |
48 | RM9K_COUNTER1_USER | | |
49 | RM9K_COUNTER1_ENABLE; | |
50 | if (ctr[1].enabled) | |
51 | control |= RM9K_COUNTER2_EVENT(ctr[1].event) | | |
52 | RM9K_COUNTER2_KERNEL | | |
53 | RM9K_COUNTER2_USER | | |
54 | RM9K_COUNTER2_ENABLE; | |
55 | reg.control = control; | |
56 | ||
57 | reg.reset_counter1 = 0x80000000 - ctr[0].count; | |
58 | reg.reset_counter2 = 0x80000000 - ctr[1].count; | |
59 | } | |
60 | ||
61 | /* Program all of the registers in preparation for enabling profiling. */ | |
62 | ||
49a89efb | 63 | static void rm9000_cpu_setup(void *args) |
1da177e4 LT |
64 | { |
65 | uint64_t perfcount; | |
66 | ||
67 | perfcount = ((uint64_t) reg.reset_counter2 << 32) | reg.reset_counter1; | |
68 | write_c0_perfcount(perfcount); | |
69 | } | |
70 | ||
71 | static void rm9000_cpu_start(void *args) | |
72 | { | |
73 | /* Start all counters on current CPU */ | |
74 | write_c0_perfcontrol(reg.control); | |
75 | } | |
76 | ||
77 | static void rm9000_cpu_stop(void *args) | |
78 | { | |
79 | /* Stop all counters on current CPU */ | |
80 | write_c0_perfcontrol(0); | |
81 | } | |
82 | ||
25ad2913 | 83 | static irqreturn_t rm9000_perfcount_handler(int irq, void *dev_id) |
1da177e4 LT |
84 | { |
85 | unsigned int control = read_c0_perfcontrol(); | |
c8f4ff9f | 86 | struct pt_regs *regs = get_irq_regs(); |
1da177e4 LT |
87 | uint32_t counter1, counter2; |
88 | uint64_t counters; | |
89 | ||
90 | /* | |
91 | * RM9000 combines two 32-bit performance counters into a single | |
92 | * 64-bit coprocessor zero register. To avoid a race updating the | |
93 | * registers we need to stop the counters while we're messing with | |
94 | * them ... | |
95 | */ | |
96 | write_c0_perfcontrol(0); | |
97 | ||
98 | counters = read_c0_perfcount(); | |
99 | counter1 = counters; | |
100 | counter2 = counters >> 32; | |
101 | ||
102 | if (control & RM9K_COUNTER1_OVERFLOW) { | |
103 | oprofile_add_sample(regs, 0); | |
104 | counter1 = reg.reset_counter1; | |
105 | } | |
106 | if (control & RM9K_COUNTER2_OVERFLOW) { | |
107 | oprofile_add_sample(regs, 1); | |
108 | counter2 = reg.reset_counter2; | |
109 | } | |
110 | ||
111 | counters = ((uint64_t)counter2 << 32) | counter1; | |
112 | write_c0_perfcount(counters); | |
113 | write_c0_perfcontrol(reg.control); | |
114 | ||
115 | return IRQ_HANDLED; | |
116 | } | |
117 | ||
cc26b815 | 118 | static int __init rm9000_init(void) |
1da177e4 LT |
119 | { |
120 | return request_irq(rm9000_perfcount_irq, rm9000_perfcount_handler, | |
121 | 0, "Perfcounter", NULL); | |
122 | } | |
123 | ||
124 | static void rm9000_exit(void) | |
125 | { | |
126 | free_irq(rm9000_perfcount_irq, NULL); | |
127 | } | |
128 | ||
1acf1ca7 | 129 | struct op_mips_model op_model_rm9000_ops = { |
1da177e4 LT |
130 | .reg_setup = rm9000_reg_setup, |
131 | .cpu_setup = rm9000_cpu_setup, | |
132 | .init = rm9000_init, | |
133 | .exit = rm9000_exit, | |
134 | .cpu_start = rm9000_cpu_start, | |
135 | .cpu_stop = rm9000_cpu_stop, | |
136 | .cpu_type = "mips/rm9000", | |
137 | .num_counters = 2 | |
138 | }; |