blob: e979dcd7772670c64199f29ce9418bd009ef59e1 [file] [log] [blame]
Martin Habets9550e592006-10-17 19:21:48 -07001/**
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
David S. Miller422e23e2008-11-25 22:29:24 -080015#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
Robert Richter25ad2912008-09-05 17:12:36 +0200115int __init oprofile_arch_init(struct oprofile_operations *ops)
Martin Habets9550e592006-10-17 19:21:48 -0700116{
David S. Miller422e23e2008-11-25 22:29:24 -0800117 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;
Martin Habets9550e592006-10-17 19:21:48 -0700126}
127
128
129void oprofile_arch_exit(void)
130{
131}