sparc64: Move generic PCR support code to seperate file.
[deliverable/linux.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 }
This page took 0.047998 seconds and 5 git commands to generate.