blob: c8877a5202b0d504dd0a170a9410ac7ccf90f172 [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>
David S. Miller3eb80572009-01-21 21:30:23 -080020#include <asm/pcr.h>
David S. Miller422e23e2008-11-25 22:29:24 -080021
22static int nmi_enabled;
23
David S. Miller63ef3482008-11-28 02:27:42 -080024/* 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 */
David S. Miller422e23e2008-11-25 22:29:24 -080029static u64 picl_value(void)
30{
31 u32 delta = local_cpu_data().clock_tick / HZ;
32
David S. Miller63ef3482008-11-28 02:27:42 -080033 return ((u64)((0 - delta) & 0xffffffff)) << 32;
David S. Miller422e23e2008-11-25 22:29:24 -080034}
35
David S. Miller63ef3482008-11-28 02:27:42 -080036#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
David S. Miller3eb80572009-01-21 21:30:23 -080042static u64 pcr_enable;
David S. Miller422e23e2008-11-25 22:29:24 -080043
44static void nmi_handler(struct pt_regs *regs)
45{
David S. Miller63ef3482008-11-28 02:27:42 -080046 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -080047
48 if (nmi_enabled) {
49 oprofile_add_sample(regs, 0);
50
51 write_pic(picl_value());
David S. Miller63ef3482008-11-28 02:27:42 -080052 pcr_ops->write(pcr_enable);
David S. Miller422e23e2008-11-25 22:29:24 -080053 }
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 */
60static void cpu_nmi_start(void *_unused)
61{
David S. Miller63ef3482008-11-28 02:27:42 -080062 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -080063 write_pic(picl_value());
64
David S. Miller63ef3482008-11-28 02:27:42 -080065 pcr_ops->write(pcr_enable);
David S. Miller422e23e2008-11-25 22:29:24 -080066}
67
68static void cpu_nmi_stop(void *_unused)
69{
David S. Miller63ef3482008-11-28 02:27:42 -080070 pcr_ops->write(PCR_PIC_PRIV);
David S. Miller422e23e2008-11-25 22:29:24 -080071}
72
73static 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
92static 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
102static int oprofile_nmi_init(struct oprofile_operations *ops)
103{
David S. Miller63ef3482008-11-28 02:27:42 -0800104 switch (tlb_type) {
105 case hypervisor:
David S. Miller63ef3482008-11-28 02:27:42 -0800106 pcr_enable = PCR_N2_ENABLE;
107 break;
108
109 case cheetah:
110 case cheetah_plus:
David S. Miller3eb80572009-01-21 21:30:23 -0800111 pcr_enable = PCR_SUN4U_ENABLE;
David S. Miller63ef3482008-11-28 02:27:42 -0800112 break;
113
114 default:
David S. Miller422e23e2008-11-25 22:29:24 -0800115 return -ENODEV;
David S. Miller63ef3482008-11-28 02:27:42 -0800116 }
David S. Miller422e23e2008-11-25 22:29:24 -0800117
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
Robert Richter25ad2912008-09-05 17:12:36 +0200131int __init oprofile_arch_init(struct oprofile_operations *ops)
Martin Habets9550e592006-10-17 19:21:48 -0700132{
David S. Miller422e23e2008-11-25 22:29:24 -0800133 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;
Martin Habets9550e592006-10-17 19:21:48 -0700142}
143
Martin Habets9550e592006-10-17 19:21:48 -0700144void oprofile_arch_exit(void)
145{
Martin Habets9550e592006-10-17 19:21:48 -0700146}