blob: d6e170c074fc5023b1befcbd7e9525e44f7ad96a [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
David S. Miller63ef3482008-11-28 02:27:42 -080016#include <asm/hypervisor.h>
David S. Miller422e23e2008-11-25 22:29:24 -080017#include <asm/spitfire.h>
18#include <asm/cpudata.h>
19#include <asm/irq.h>
20
21static int nmi_enabled;
22
David S. Miller63ef3482008-11-28 02:27:42 -080023struct pcr_ops {
24 u64 (*read)(void);
25 void (*write)(u64);
26};
27static const struct pcr_ops *pcr_ops;
28
29static u64 direct_pcr_read(void)
30{
31 u64 val;
32
33 read_pcr(val);
34 return val;
35}
36
37static void direct_pcr_write(u64 val)
38{
39 write_pcr(val);
40}
41
42static const struct pcr_ops direct_pcr_ops = {
43 .read = direct_pcr_read,
44 .write = direct_pcr_write,
45};
46
47static void n2_pcr_write(u64 val)
48{
49 unsigned long ret;
50
51 ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
52 if (val != HV_EOK)
53 write_pcr(val);
54}
55
56static const struct pcr_ops n2_pcr_ops = {
57 .read = direct_pcr_read,
58 .write = n2_pcr_write,
59};
60
61/* In order to commonize as much of the implementation as
62 * possible, we use PICH as our counter. Mostly this is
63 * to accomodate Niagara-1 which can only count insn cycles
64 * in PICH.
65 */
David S. Miller422e23e2008-11-25 22:29:24 -080066static u64 picl_value(void)
67{
68 u32 delta = local_cpu_data().clock_tick / HZ;
69
David S. Miller63ef3482008-11-28 02:27:42 -080070 return ((u64)((0 - delta) & 0xffffffff)) << 32;
David S. Miller422e23e2008-11-25 22:29:24 -080071}
72
David S. Miller63ef3482008-11-28 02:27:42 -080073#define PCR_PIC_PRIV 0x00000001 /* PIC access is privileged */
74#define PCR_STRACE 0x00000002 /* Trace supervisor events */
75#define PCR_UTRACE 0x00000004 /* Trace user events */
76#define PCR_N2_HTRACE 0x00000008 /* Trace hypervisor events */
77#define PCR_N2_TOE_OV0 0x00000010 /* Trap if PIC 0 overflows */
78#define PCR_N2_TOE_OV1 0x00000020 /* Trap if PIC 1 overflows */
79#define PCR_N2_MASK0 0x00003fc0
80#define PCR_N2_MASK0_SHIFT 6
81#define PCR_N2_SL0 0x0003c000
82#define PCR_N2_SL0_SHIFT 14
83#define PCR_N2_OV0 0x00040000
84#define PCR_N2_MASK1 0x07f80000
85#define PCR_N2_MASK1_SHIFT 19
86#define PCR_N2_SL1 0x78000000
87#define PCR_N2_SL1_SHIFT 27
88#define PCR_N2_OV1 0x80000000
89
90#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
91#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
92 PCR_N2_TOE_OV1 | \
93 (2 << PCR_N2_SL1_SHIFT) | \
94 (0xff << PCR_N2_MASK1_SHIFT))
95
96static u64 pcr_enable = PCR_SUN4U_ENABLE;
David S. Miller422e23e2008-11-25 22:29:24 -080097
98static void nmi_handler(struct pt_regs *regs)
99{
David S. Miller63ef3482008-11-28 02:27:42 -0800100 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -0800101
102 if (nmi_enabled) {
103 oprofile_add_sample(regs, 0);
104
105 write_pic(picl_value());
David S. Miller63ef3482008-11-28 02:27:42 -0800106 pcr_ops->write(pcr_enable);
David S. Miller422e23e2008-11-25 22:29:24 -0800107 }
108}
109
110/* We count "clock cycle" events in the lower 32-bit PIC.
111 * Then configure it such that it overflows every HZ, and thus
112 * generates a level 15 interrupt at that frequency.
113 */
114static void cpu_nmi_start(void *_unused)
115{
David S. Miller63ef3482008-11-28 02:27:42 -0800116 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -0800117 write_pic(picl_value());
118
David S. Miller63ef3482008-11-28 02:27:42 -0800119 pcr_ops->write(pcr_enable);
David S. Miller422e23e2008-11-25 22:29:24 -0800120}
121
122static void cpu_nmi_stop(void *_unused)
123{
David S. Miller63ef3482008-11-28 02:27:42 -0800124 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -0800125}
126
127static int nmi_start(void)
128{
129 int err = register_perfctr_intr(nmi_handler);
130
131 if (!err) {
132 nmi_enabled = 1;
133 wmb();
134 err = on_each_cpu(cpu_nmi_start, NULL, 1);
135 if (err) {
136 nmi_enabled = 0;
137 wmb();
138 on_each_cpu(cpu_nmi_stop, NULL, 1);
139 release_perfctr_intr(nmi_handler);
140 }
141 }
142
143 return err;
144}
145
146static void nmi_stop(void)
147{
148 nmi_enabled = 0;
149 wmb();
150
151 on_each_cpu(cpu_nmi_stop, NULL, 1);
152 release_perfctr_intr(nmi_handler);
153 synchronize_sched();
154}
155
David S. Miller63ef3482008-11-28 02:27:42 -0800156static unsigned long perf_hsvc_group;
157static unsigned long perf_hsvc_major;
158static unsigned long perf_hsvc_minor;
159
160static int __init register_perf_hsvc(void)
161{
162 if (tlb_type == hypervisor) {
163 switch (sun4v_chip_type) {
164 case SUN4V_CHIP_NIAGARA1:
165 perf_hsvc_group = HV_GRP_NIAG_PERF;
166 break;
167
168 case SUN4V_CHIP_NIAGARA2:
169 perf_hsvc_group = HV_GRP_N2_CPU;
170 break;
171
172 default:
173 return -ENODEV;
174 }
175
176
177 perf_hsvc_major = 1;
178 perf_hsvc_minor = 0;
179 if (sun4v_hvapi_register(perf_hsvc_group,
180 perf_hsvc_major,
181 &perf_hsvc_minor)) {
182 printk("perfmon: Could not register N2 hvapi.\n");
183 return -ENODEV;
184 }
185 }
186 return 0;
187}
188
189static void unregister_perf_hsvc(void)
190{
191 if (tlb_type != hypervisor)
192 return;
193 sun4v_hvapi_unregister(perf_hsvc_group);
194}
195
David S. Miller422e23e2008-11-25 22:29:24 -0800196static int oprofile_nmi_init(struct oprofile_operations *ops)
197{
David S. Miller63ef3482008-11-28 02:27:42 -0800198 int err = register_perf_hsvc();
199
200 if (err)
201 return err;
202
203 switch (tlb_type) {
204 case hypervisor:
205 pcr_ops = &n2_pcr_ops;
206 pcr_enable = PCR_N2_ENABLE;
207 break;
208
209 case cheetah:
210 case cheetah_plus:
211 pcr_ops = &direct_pcr_ops;
212 break;
213
214 default:
David S. Miller422e23e2008-11-25 22:29:24 -0800215 return -ENODEV;
David S. Miller63ef3482008-11-28 02:27:42 -0800216 }
David S. Miller422e23e2008-11-25 22:29:24 -0800217
218 ops->create_files = NULL;
219 ops->setup = NULL;
220 ops->shutdown = NULL;
221 ops->start = nmi_start;
222 ops->stop = nmi_stop;
223 ops->cpu_type = "timer";
224
225 printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");
226
227 return 0;
228}
229#endif
230
Robert Richter25ad2912008-09-05 17:12:36 +0200231int __init oprofile_arch_init(struct oprofile_operations *ops)
Martin Habets9550e592006-10-17 19:21:48 -0700232{
David S. Miller422e23e2008-11-25 22:29:24 -0800233 int ret = -ENODEV;
234
235#ifdef CONFIG_SPARC64
236 ret = oprofile_nmi_init(ops);
237 if (!ret)
238 return ret;
239#endif
240
241 return ret;
Martin Habets9550e592006-10-17 19:21:48 -0700242}
243
244
245void oprofile_arch_exit(void)
246{
David S. Miller63ef3482008-11-28 02:27:42 -0800247#ifdef CONFIG_SPARC64
248 unregister_perf_hsvc();
249#endif
Martin Habets9550e592006-10-17 19:21:48 -0700250}