diff options
-rw-r--r-- | arch/sparc/oprofile/init.c | 110 |
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 | |||
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 | |||
15 | int __init oprofile_arch_init(struct oprofile_operations *ops) | 115 | int __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 | ||