branch Attitude Adjustment
[12.09/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 }