sparc64: Use NMI oprofile profiling on cheetah and derivative cpus.
[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/spitfire.h>
17 #include <asm/cpudata.h>
18 #include <asm/irq.h>
19
20 static int nmi_enabled;
21
22 static u64 picl_value(void)
23 {
24         u32 delta = local_cpu_data().clock_tick / HZ;
25
26         return (0 - delta) & 0xffffffff;
27 }
28
29 #define PCR_PIC_PRIV    0x1 /* PIC access is privileged */
30 #define PCR_STRACE      0x2 /* Trace supervisor events  */
31 #define PCR_UTRACE      0x4 /* Trace user events        */
32
33 static void nmi_handler(struct pt_regs *regs)
34 {
35         write_pcr(PCR_PIC_PRIV);
36
37         if (nmi_enabled) {
38                 oprofile_add_sample(regs, 0);
39
40                 write_pic(picl_value());
41                 write_pcr(PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE);
42         }
43 }
44
45 /* We count "clock cycle" events in the lower 32-bit PIC.
46  * Then configure it such that it overflows every HZ, and thus
47  * generates a level 15 interrupt at that frequency.
48  */
49 static void cpu_nmi_start(void *_unused)
50 {
51         write_pcr(PCR_PIC_PRIV);
52         write_pic(picl_value());
53
54         /* Bit 0: PIC access is privileged
55          * Bit 1: Supervisor Trace
56          * Bit 2: User Trace
57          *
58          * And the event selection code for cpu cycles is zero.
59          */
60         write_pcr(PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE);
61 }
62
63 static void cpu_nmi_stop(void *_unused)
64 {
65         write_pcr(PCR_PIC_PRIV);
66 }
67
68 static int nmi_start(void)
69 {
70         int err = register_perfctr_intr(nmi_handler);
71
72         if (!err) {
73                 nmi_enabled = 1;
74                 wmb();
75                 err = on_each_cpu(cpu_nmi_start, NULL, 1);
76                 if (err) {
77                         nmi_enabled = 0;
78                         wmb();
79                         on_each_cpu(cpu_nmi_stop, NULL, 1);
80                         release_perfctr_intr(nmi_handler);
81                 }
82         }
83
84         return err;
85 }
86
87 static void nmi_stop(void)
88 {
89         nmi_enabled = 0;
90         wmb();
91
92         on_each_cpu(cpu_nmi_stop, NULL, 1);
93         release_perfctr_intr(nmi_handler);
94         synchronize_sched();
95 }
96
97 static int oprofile_nmi_init(struct oprofile_operations *ops)
98 {
99         if (tlb_type != cheetah && tlb_type != cheetah_plus)
100                 return -ENODEV;
101
102         ops->create_files = NULL;
103         ops->setup = NULL;
104         ops->shutdown = NULL;
105         ops->start = nmi_start;
106         ops->stop = nmi_stop;
107         ops->cpu_type = "timer";
108
109         printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");
110
111         return 0;
112 }
113 #endif
114
115 int __init oprofile_arch_init(struct oprofile_operations *ops)
116 {
117         int ret = -ENODEV;
118
119 #ifdef CONFIG_SPARC64
120         ret = oprofile_nmi_init(ops);
121         if (!ret)
122                 return ret;
123 #endif
124
125         return ret;
126 }
127
128
129 void oprofile_arch_exit(void)
130 {
131 }