aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sparc/oprofile/init.c110
1 files changed, 109 insertions, 1 deletions
diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c
index 17bb6035069b..e979dcd77726 100644
--- a/arch/sparc/oprofile/init.c
+++ b/arch/sparc/oprofile/init.c
@@ -12,9 +12,117 @@
12#include <linux/errno.h> 12#include <linux/errno.h>
13#include <linux/init.h> 13#include <linux/init.h>
14 14
15#ifdef CONFIG_SPARC64
16#include <asm/spitfire.h>
17#include <asm/cpudata.h>
18#include <asm/irq.h>
19
20static int nmi_enabled;
21
22static 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
33static 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 */
49static 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
63static void cpu_nmi_stop(void *_unused)
64{
65 write_pcr(PCR_PIC_PRIV);
66}
67
68static 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
87static 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
97static 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
15int __init oprofile_arch_init(struct oprofile_operations *ops) 115int __init oprofile_arch_init(struct oprofile_operations *ops)
16{ 116{
17 return -ENODEV; 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;
18} 126}
19 127
20 128