aeac3c6667dcdfd682d1ed17d99db05e17ce9a00
[openwrt/openwrt.git] / target / linux / ubicom32 / files / arch / ubicom32 / oprofile / profile.c
1 /*
2 * arch/ubicom32/oprofile/profile.c
3 * Oprofile support for arch Ubicom32
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option)
13 * any later version.
14 *
15 * The Ubicom32 Linux Kernel Port is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with the Ubicom32 Linux Kernel Port. If not, see
22 * <http://www.gnu.org/licenses/>.
23 *
24 * Ubicom32 implementation derived from (with many thanks):
25 * arch/m68knommu
26 * arch/blackfin
27 * arch/parisc
28 */
29
30 /**
31 * @file profile.c
32 *
33 * @remark Copyright 2002 OProfile authors
34 * @remark Read the file COPYING
35 *
36 * @author Hunyue Yau <hy@hy-research.com>
37 */
38
39 #include <linux/oprofile.h>
40 #include <linux/init.h>
41 #include <linux/errno.h>
42 #include <linux/interrupt.h>
43 #include <linux/module.h>
44 #include <linux/kernel.h>
45
46 #include <asm/devtree.h>
47 #include <asm/thread.h>
48
49 /* For identifying userland vs kernel address */
50 #include <asm/stacktrace.h>
51 #include "ipProf.h"
52
53 /* For communications with the backend */
54 static struct profilenode *profile_node;
55
56 /* Bitmask containing all Linux threads - as seen by the ROSR reg */
57 static unsigned long th_all_mask;
58
59 /* Lookup table to translate a hardware thread into a CPU identifier
60 * Table is indexed by the ROSR value which is assumed to be
61 * relatively small (0...15).
62 */
63 unsigned int cpu_map[THREAD_ARCHITECTURAL_MAX];
64
65 static struct pt_regs regs;
66
67 /*
68 * For each sample returned, checked to see if they are relevant to
69 * us. This is necessary as the ubicom32 architecture has other software
70 * running outside of Linux. Only then, put the sample into the relevant
71 * cpu bins.
72 *
73 * To minimize overhead, a global mask with all possible threads of in
74 * interest to us is used as a first check. Then a second mask identifying
75 * the thread is used to obtain an identifier for that "CPU".
76 */
77
78 /*
79 * ubicom32_build_cpu_th_mask()
80 *
81 * Build a lookup table for translation between hardware thread
82 * "ROSR" values and Linux CPU ids
83 *
84 * *** This gets executed on all CPUs at once! ***
85 */
86 static void ubicom32_build_cpu_th_mask(void *mask)
87 {
88 thread_t self = thread_get_self();
89 unsigned long *th_m = mask;
90
91 BUG_ON(self <= 0 || self >= THREAD_ARCHITECTURAL_MAX);
92 cpu_map[self] = smp_processor_id();
93
94 set_bit(self, th_m);
95 }
96
97 /*
98 * profile_interrupt()
99 *
100 * Process samples returned from the profiler backend. The backend
101 * may return samples that are irrelevant to us or may even return
102 * multiple samples for the same CPU. Note that the sames may be
103 * for ANY cpu. At this time, this is unique and to support this requires
104 * Oprofile to expose an interface to accept the CPU that the same came
105 * frome.
106 */
107 static irqreturn_t profile_interrupt(int irq, void *arg)
108 {
109 int i, buf_entry;
110 int is_kernel;
111 unsigned int bit_th;
112 unsigned int th;
113
114 if (!(profile_node->enabled) || profile_node->count < 0) {
115 printk(KERN_WARNING
116 "Unexpected interrupt, no samples or not enabled!\n");
117 return IRQ_HANDLED;
118 }
119
120 profile_node->busy = 1; /* Keep backend out */
121
122 for (i = 0; i < profile_node->count; i++) {
123 buf_entry = profile_node->tail;
124 profile_node->tail++;
125 profile_node->tail %= IPPROFILETIO_MAX_SAMPLES;
126
127 /* Note - the "thread" ID is only the lower 4 bits */
128 th = (0x0f & profile_node->samples[buf_entry].thread);
129 bit_th = (1 << th);
130
131 if ((bit_th & th_all_mask) == 0)
132 continue;
133
134 regs.pc = profile_node->samples[buf_entry].pc;
135
136 is_kernel = ubicom32_is_kernel(regs.pc);
137
138 oprofile_add_ext_sample_cpu(regs.pc, &regs, 0, is_kernel,
139 cpu_map[th]);
140 }
141 profile_node->count = 0;
142 profile_node->busy = 0;
143
144 return IRQ_HANDLED;
145 }
146
147 /*
148 * profile_start()
149 *
150 * Notification from oprofile to start the profiler
151 */
152 static int profile_start(void)
153 {
154 if (!profile_node)
155 return -1;
156
157 profile_node->enabled = 1;
158
159 return 0;
160 }
161
162 /*
163 * profile_stop()
164 *
165 * Notification from oprofile to stop the profiler
166 */
167 static void profile_stop(void)
168 {
169 if (profile_node)
170 profile_node->enabled = 0;
171 }
172
173 /*
174 * oprofile_arch_init()
175 *
176 * Attach to Oprofile after qualify the availability of the backend
177 * profiler support.
178 */
179 int __init oprofile_arch_init(struct oprofile_operations *ops)
180 {
181 int r = -ENODEV;
182
183 profile_node = (struct profilenode *)devtree_find_node("profiler");
184
185 if (profile_node == NULL) {
186 printk(KERN_WARNING "Cannot find profiler node\n");
187 return r;
188 }
189
190 r = request_irq(profile_node->dn.recvirq, profile_interrupt,
191 IRQF_DISABLED, "profiler", NULL);
192
193 if (r < 0) {
194 profile_node = NULL;
195 printk(KERN_WARNING "Cannot get profiler IRQ\n");
196 return r;
197 }
198
199 ops->start = profile_start;
200 ops->stop = profile_stop;
201 ops->cpu_type = "timer";
202
203 memset(cpu_map, 0, sizeof(cpu_map));
204
205 on_each_cpu(ubicom32_build_cpu_th_mask, &th_all_mask, 1);
206
207 memset(&regs, 0, sizeof(regs));
208
209 return r;
210 }
211
212 /*
213 * oprofile_arch_exit()
214 *
215 * External call to take outselves out.
216 * Make sure backend is not running.
217 */
218 void oprofile_arch_exit(void)
219 {
220 BUG_ON(profile_node->enabled);
221 }