sparc64: Move generic PCR support code to seperate file.
[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 #include <asm/pcr.h>
21
22 static int nmi_enabled;
23
24 /* In order to commonize as much of the implementation as
25  * possible, we use PICH as our counter.  Mostly this is
26  * to accomodate Niagara-1 which can only count insn cycles
27  * in PICH.
28  */
29 static u64 picl_value(void)
30 {
31         u32 delta = local_cpu_data().clock_tick / HZ;
32
33         return ((u64)((0 - delta) & 0xffffffff)) << 32;
34 }
35
36 #define PCR_SUN4U_ENABLE        (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
37 #define PCR_N2_ENABLE           (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
38                                  PCR_N2_TOE_OV1 | \
39                                  (2 << PCR_N2_SL1_SHIFT) | \
40                                  (0xff << PCR_N2_MASK1_SHIFT))
41
42 static u64 pcr_enable;
43
44 static void nmi_handler(struct pt_regs *regs)
45 {
46         pcr_ops->write(PCR_PIC_PRIV);
47
48         if (nmi_enabled) {
49                 oprofile_add_sample(regs, 0);
50
51                 write_pic(picl_value());
52                 pcr_ops->write(pcr_enable);
53         }
54 }
55
56 /* We count "clock cycle" events in the lower 32-bit PIC.
57  * Then configure it such that it overflows every HZ, and thus
58  * generates a level 15 interrupt at that frequency.
59  */
60 static void cpu_nmi_start(void *_unused)
61 {
62         pcr_ops->write(PCR_PIC_PRIV);
63         write_pic(picl_value());
64
65         pcr_ops->write(pcr_enable);
66 }
67
68 static void cpu_nmi_stop(void *_unused)
69 {
70         pcr_ops->write(PCR_PIC_PRIV);
71 }
72
73 static int nmi_start(void)
74 {
75         int err = register_perfctr_intr(nmi_handler);
76
77         if (!err) {
78                 nmi_enabled = 1;
79                 wmb();
80                 err = on_each_cpu(cpu_nmi_start, NULL, 1);
81                 if (err) {
82                         nmi_enabled = 0;
83                         wmb();
84                         on_each_cpu(cpu_nmi_stop, NULL, 1);
85                         release_perfctr_intr(nmi_handler);
86                 }
87         }
88
89         return err;
90 }
91
92 static void nmi_stop(void)
93 {
94         nmi_enabled = 0;
95         wmb();
96
97         on_each_cpu(cpu_nmi_stop, NULL, 1);
98         release_perfctr_intr(nmi_handler);
99         synchronize_sched();
100 }
101
102 static int oprofile_nmi_init(struct oprofile_operations *ops)
103 {
104         switch (tlb_type) {
105         case hypervisor:
106                 pcr_enable = PCR_N2_ENABLE;
107                 break;
108
109         case cheetah:
110         case cheetah_plus:
111                 pcr_enable = PCR_SUN4U_ENABLE;
112                 break;
113
114         default:
115                 return -ENODEV;
116         }
117
118         ops->create_files = NULL;
119         ops->setup = NULL;
120         ops->shutdown = NULL;
121         ops->start = nmi_start;
122         ops->stop = nmi_stop;
123         ops->cpu_type = "timer";
124
125         printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");
126
127         return 0;
128 }
129 #endif
130
131 int __init oprofile_arch_init(struct oprofile_operations *ops)
132 {
133         int ret = -ENODEV;
134
135 #ifdef CONFIG_SPARC64
136         ret = oprofile_nmi_init(ops);
137         if (!ret)
138                 return ret;
139 #endif
140
141         return ret;
142 }
143
144 void oprofile_arch_exit(void)
145 {
146 }