sparc64: Provide oprofile pseudo-NMI on Niagara.
[linux-2.6.git] / arch / sparc / oprofile / init.c
1 /**
2  * @file init.c
3  *
4  * @remark Copyright 2002 OProfile authors
5  * @remark Read the file COPYING
6  *
7  * @author John Levon <levon@movementarian.org>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/oprofile.h>
12 #include <linux/errno.h>
13 #include <linux/init.h>
14  
15 #ifdef CONFIG_SPARC64
16 #include <asm/hypervisor.h>
17 #include <asm/spitfire.h>
18 #include <asm/cpudata.h>
19 #include <asm/irq.h>
20
21 static int nmi_enabled;
22
23 struct pcr_ops {
24         u64 (*read)(void);
25         void (*write)(u64);
26 };
27 static const struct pcr_ops *pcr_ops;
28
29 static u64 direct_pcr_read(void)
30 {
31         u64 val;
32
33         read_pcr(val);
34         return val;
35 }
36
37 static void direct_pcr_write(u64 val)
38 {
39         write_pcr(val);
40 }
41
42 static const struct pcr_ops direct_pcr_ops = {
43         .read   = direct_pcr_read,
44         .write  = direct_pcr_write,
45 };
46
47 static void n2_pcr_write(u64 val)
48 {
49         unsigned long ret;
50
51         ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
52         if (val != HV_EOK)
53                 write_pcr(val);
54 }
55
56 static const struct pcr_ops n2_pcr_ops = {
57         .read   = direct_pcr_read,
58         .write  = n2_pcr_write,
59 };
60
61 /* In order to commonize as much of the implementation as
62  * possible, we use PICH as our counter.  Mostly this is
63  * to accomodate Niagara-1 which can only count insn cycles
64  * in PICH.
65  */
66 static u64 picl_value(void)
67 {
68         u32 delta = local_cpu_data().clock_tick / HZ;
69
70         return ((u64)((0 - delta) & 0xffffffff)) << 32;
71 }
72
73 #define PCR_PIC_PRIV            0x00000001 /* PIC access is privileged */
74 #define PCR_STRACE              0x00000002 /* Trace supervisor events  */
75 #define PCR_UTRACE              0x00000004 /* Trace user events        */
76 #define PCR_N2_HTRACE           0x00000008 /* Trace hypervisor events  */
77 #define PCR_N2_TOE_OV0          0x00000010 /* Trap if PIC 0 overflows  */
78 #define PCR_N2_TOE_OV1          0x00000020 /* Trap if PIC 1 overflows  */
79 #define PCR_N2_MASK0            0x00003fc0
80 #define PCR_N2_MASK0_SHIFT      6
81 #define PCR_N2_SL0              0x0003c000
82 #define PCR_N2_SL0_SHIFT        14
83 #define PCR_N2_OV0              0x00040000
84 #define PCR_N2_MASK1            0x07f80000
85 #define PCR_N2_MASK1_SHIFT      19
86 #define PCR_N2_SL1              0x78000000
87 #define PCR_N2_SL1_SHIFT        27
88 #define PCR_N2_OV1              0x80000000
89
90 #define PCR_SUN4U_ENABLE        (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
91 #define PCR_N2_ENABLE           (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
92                                  PCR_N2_TOE_OV1 | \
93                                  (2 << PCR_N2_SL1_SHIFT) | \
94                                  (0xff << PCR_N2_MASK1_SHIFT))
95
96 static u64 pcr_enable = PCR_SUN4U_ENABLE;
97
98 static void nmi_handler(struct pt_regs *regs)
99 {
100         pcr_ops->write(PCR_PIC_PRIV);
101
102         if (nmi_enabled) {
103                 oprofile_add_sample(regs, 0);
104
105                 write_pic(picl_value());
106                 pcr_ops->write(pcr_enable);
107         }
108 }
109
110 /* We count "clock cycle" events in the lower 32-bit PIC.
111  * Then configure it such that it overflows every HZ, and thus
112  * generates a level 15 interrupt at that frequency.
113  */
114 static void cpu_nmi_start(void *_unused)
115 {
116         pcr_ops->write(PCR_PIC_PRIV);
117         write_pic(picl_value());
118
119         pcr_ops->write(pcr_enable);
120 }
121
122 static void cpu_nmi_stop(void *_unused)
123 {
124         pcr_ops->write(PCR_PIC_PRIV);
125 }
126
127 static int nmi_start(void)
128 {
129         int err = register_perfctr_intr(nmi_handler);
130
131         if (!err) {
132                 nmi_enabled = 1;
133                 wmb();
134                 err = on_each_cpu(cpu_nmi_start, NULL, 1);
135                 if (err) {
136                         nmi_enabled = 0;
137                         wmb();
138                         on_each_cpu(cpu_nmi_stop, NULL, 1);
139                         release_perfctr_intr(nmi_handler);
140                 }
141         }
142
143         return err;
144 }
145
146 static void nmi_stop(void)
147 {
148         nmi_enabled = 0;
149         wmb();
150
151         on_each_cpu(cpu_nmi_stop, NULL, 1);
152         release_perfctr_intr(nmi_handler);
153         synchronize_sched();
154 }
155
156 static unsigned long perf_hsvc_group;
157 static unsigned long perf_hsvc_major;
158 static unsigned long perf_hsvc_minor;
159
160 static int __init register_perf_hsvc(void)
161 {
162         if (tlb_type == hypervisor) {
163                 switch (sun4v_chip_type) {
164                 case SUN4V_CHIP_NIAGARA1:
165                         perf_hsvc_group = HV_GRP_NIAG_PERF;
166                         break;
167
168                 case SUN4V_CHIP_NIAGARA2:
169                         perf_hsvc_group = HV_GRP_N2_CPU;
170                         break;
171
172                 default:
173                         return -ENODEV;
174                 }
175
176
177                 perf_hsvc_major = 1;
178                 perf_hsvc_minor = 0;
179                 if (sun4v_hvapi_register(perf_hsvc_group,
180                                          perf_hsvc_major,
181                                          &perf_hsvc_minor)) {
182                         printk("perfmon: Could not register N2 hvapi.\n");
183                         return -ENODEV;
184                 }
185         }
186         return 0;
187 }
188
189 static void unregister_perf_hsvc(void)
190 {
191         if (tlb_type != hypervisor)
192                 return;
193         sun4v_hvapi_unregister(perf_hsvc_group);
194 }
195
196 static int oprofile_nmi_init(struct oprofile_operations *ops)
197 {
198         int err = register_perf_hsvc();
199
200         if (err)
201                 return err;
202
203         switch (tlb_type) {
204         case hypervisor:
205                 pcr_ops = &n2_pcr_ops;
206                 pcr_enable = PCR_N2_ENABLE;
207                 break;
208
209         case cheetah:
210         case cheetah_plus:
211                 pcr_ops = &direct_pcr_ops;
212                 break;
213
214         default:
215                 return -ENODEV;
216         }
217
218         ops->create_files = NULL;
219         ops->setup = NULL;
220         ops->shutdown = NULL;
221         ops->start = nmi_start;
222         ops->stop = nmi_stop;
223         ops->cpu_type = "timer";
224
225         printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");
226
227         return 0;
228 }
229 #endif
230
231 int __init oprofile_arch_init(struct oprofile_operations *ops)
232 {
233         int ret = -ENODEV;
234
235 #ifdef CONFIG_SPARC64
236         ret = oprofile_nmi_init(ops);
237         if (!ret)
238                 return ret;
239 #endif
240
241         return ret;
242 }
243
244
245 void oprofile_arch_exit(void)
246 {
247 #ifdef CONFIG_SPARC64
248         unregister_perf_hsvc();
249 #endif
250 }