ARM: tegra: Add Tegra Profiler
[linux-2.6.git] / drivers / misc / tegra-profiler / pl310.c
1 /*
2  * drivers/misc/tegra-profiler/pl310.c
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/module.h>
20 #include <linux/init.h>
21 #include <linux/io.h>
22 #include <asm/hardware/cache-l2x0.h>
23
24 #include <linux/tegra_profiler.h>
25
26 #include "quadd.h"
27 #include "pl310.h"
28 #include "debug.h"
29
30 DEFINE_PER_CPU(u32, pl310_prev_val);
31
32 static struct l2x0_context l2x0_ctx;
33
34 static void l2x0_enable_event_counters(u32 event0, u32 event1)
35 {
36         u32 reg_val;
37         void __iomem *base = l2x0_ctx.l2x0_base;
38
39         /* configure counter0 */
40         reg_val = event0;
41         writel_relaxed(reg_val, base + L2X0_EVENT_CNT0_CFG);
42
43         /* configure counter1 */
44         reg_val = event1;
45         writel_relaxed(reg_val, base + L2X0_EVENT_CNT1_CFG);
46
47         /* enable event counting */
48         reg_val = L2X0_EVENT_CNT_ENABLE;
49         writel_relaxed(reg_val, base + L2X0_EVENT_CNT_CTRL);
50 }
51
52 static void __maybe_unused l2x0_disable_event_counters(void)
53 {
54         u32 reg_val;
55         void __iomem *base = l2x0_ctx.l2x0_base;
56
57         /* disable event counting */
58         reg_val = 0;
59         writel_relaxed(reg_val, base + L2X0_EVENT_CNT_CTRL);
60 }
61
62 static void l2x0_stop_event_counters(void)
63 {
64         void __iomem *base = l2x0_ctx.l2x0_base;
65
66         writel_relaxed(0, base + L2X0_EVENT_CNT_CTRL);
67
68         writel_relaxed(0, base + L2X0_EVENT_CNT0_CFG);
69         writel_relaxed(0, base + L2X0_EVENT_CNT1_CFG);
70 }
71
72 static void l2x0_reset_event_counters(void)
73 {
74         u32 reg_val;
75         void __iomem *base = l2x0_ctx.l2x0_base;
76
77         reg_val = readl_relaxed(base + L2X0_EVENT_CNT_CTRL);
78         reg_val |= L2X0_EVENT_CNT_RESET_CNT0 | L2X0_EVENT_CNT_RESET_CNT1;
79         writel_relaxed(reg_val, base + L2X0_EVENT_CNT_CTRL);
80 }
81
82 static u32 l2x0_read_event_counter(enum quadd_l2x0_counter counter)
83 {
84         u32 reg_val = 0;
85         void __iomem *base = l2x0_ctx.l2x0_base;
86
87         switch (counter) {
88         case QUADD_L2X0_COUNTER0:
89                 reg_val = readl_relaxed(base + L2X0_EVENT_CNT0_VAL);
90                 break;
91         case QUADD_L2X0_COUNTER1:
92                 reg_val = readl_relaxed(base + L2X0_EVENT_CNT1_VAL);
93                 break;
94         }
95
96         return reg_val;
97 }
98
99 static void l2x0_enable_perf_event(enum quadd_l2x0_event_type type)
100 {
101         l2x0_reset_event_counters();
102
103         switch (type) {
104         case QUADD_L2X0_TYPE_DATA_READ_MISSES:
105                 l2x0_enable_event_counters(L2X0_EVENT_CNT_CFG_DRREQ,
106                                            L2X0_EVENT_CNT_CFG_DRHIT);
107                 break;
108         case QUADD_L2X0_TYPE_DATA_WRITE_MISSES:
109                 l2x0_enable_event_counters(L2X0_EVENT_CNT_CFG_DWREQ,
110                                            L2X0_EVENT_CNT_CFG_DWHIT);
111                 break;
112         case QUADD_L2X0_TYPE_INSTRUCTION_MISSES:
113                 l2x0_enable_event_counters(L2X0_EVENT_CNT_CFG_IRREQ,
114                                            L2X0_EVENT_CNT_CFG_IRHIT);
115                 break;
116         }
117 }
118
119 static u32 l2x0_read_perf_event(void)
120 {
121         u32 count_req, count_hit, count_miss;
122
123         count_req = l2x0_read_event_counter(QUADD_L2X0_COUNTER0);
124         count_hit = l2x0_read_event_counter(QUADD_L2X0_COUNTER1);
125
126         count_miss = count_req - count_hit;
127         if (count_req < count_hit)
128                 return 0;
129
130         return count_miss;
131 }
132
133 static void l2x0_clear_values(void)
134 {
135         int cpu_id;
136         for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++)
137                 per_cpu(pl310_prev_val, cpu_id) = 0;
138 }
139
140 static int l2x0_events_enable(void)
141 {
142         return 0;
143 }
144
145 static void l2x0_events_disable(void)
146 {
147 }
148
149 static void l2x0_events_start(void)
150 {
151         unsigned long flags;
152
153         if (l2x0_ctx.l2x0_event_type < 0)
154                 return;
155
156         spin_lock_irqsave(&l2x0_ctx.lock, flags);
157         l2x0_clear_values();
158         l2x0_enable_perf_event(l2x0_ctx.l2x0_event_type);
159         spin_unlock_irqrestore(&l2x0_ctx.lock, flags);
160
161         qm_debug_start_source(QUADD_EVENT_SOURCE_PL310);
162 }
163
164 static void l2x0_events_stop(void)
165 {
166         unsigned long flags;
167
168         if (l2x0_ctx.l2x0_event_type < 0)
169                 return;
170
171         spin_lock_irqsave(&l2x0_ctx.lock, flags);
172         l2x0_stop_event_counters();
173         l2x0_clear_values();
174         spin_unlock_irqrestore(&l2x0_ctx.lock, flags);
175
176         qm_debug_stop_source(QUADD_EVENT_SOURCE_PL310);
177 }
178
179 static int __maybe_unused l2x0_events_read(struct event_data *events)
180 {
181         unsigned long flags;
182
183         if (l2x0_ctx.l2x0_event_type < 0) {
184                 pr_err_once("pl310 value: %u\n", events[0].val);
185                 return 0;
186         }
187
188         events[0].event_source = QUADD_EVENT_SOURCE_PL310;
189         events[0].event_id = l2x0_ctx.event_id;
190
191         spin_lock_irqsave(&l2x0_ctx.lock, flags);
192         events[0].val = l2x0_read_perf_event();
193         spin_unlock_irqrestore(&l2x0_ctx.lock, flags);
194
195         events[0].prev_val = __get_cpu_var(pl310_prev_val);
196
197         __get_cpu_var(pl310_prev_val) = events[0].val;
198
199         qm_debug_read_counter(l2x0_ctx.event_id, events[0].prev_val,
200                               events[0].val);
201
202         return 1;
203 }
204
205 static int __maybe_unused l2x0_events_read_emulate(struct event_data *events)
206 {
207         static u32 val;
208
209         if (val > 100)
210                 val = 0;
211
212         events[0].event_source = QUADD_EVENT_SOURCE_PL310;
213         events[0].event_id = QUADD_L2X0_TYPE_DATA_READ_MISSES;
214
215         events[0].val = val;
216         events[0].prev_val = __get_cpu_var(pl310_prev_val);
217
218         __get_cpu_var(pl310_prev_val) = val;
219
220         val += 10;
221
222         return 1;
223 }
224
225 static int l2x0_set_events(int *events, int size)
226 {
227         if (!events || size == 0) {
228                 l2x0_ctx.l2x0_event_type = -1;
229                 l2x0_ctx.event_id = -1;
230                 return 0;
231         }
232
233         if (size != 1) {
234                 pr_err("Error: number of events more than one\n");
235                 return -ENOSPC;
236         }
237
238         switch (*events) {
239         case QUADD_EVENT_TYPE_L2_DCACHE_READ_MISSES:
240                 l2x0_ctx.l2x0_event_type = QUADD_L2X0_TYPE_DATA_READ_MISSES;
241                 break;
242         case QUADD_EVENT_TYPE_L2_DCACHE_WRITE_MISSES:
243                 l2x0_ctx.l2x0_event_type = QUADD_L2X0_TYPE_DATA_WRITE_MISSES;
244                 break;
245         case QUADD_EVENT_TYPE_L2_ICACHE_MISSES:
246                 l2x0_ctx.l2x0_event_type = QUADD_L2X0_TYPE_INSTRUCTION_MISSES;
247                 break;
248         default:
249                 pr_err("Error event: %s\n", quadd_get_event_str(*events));
250                 return 1;
251         }
252         l2x0_ctx.event_id = *events;
253
254         pr_info("Event has been added: id/l2x0: %s/%#x\n",
255                 quadd_get_event_str(*events), l2x0_ctx.l2x0_event_type);
256         return 0;
257 }
258
259 static int get_supported_events(int *events)
260 {
261         events[0] = QUADD_EVENT_TYPE_L2_DCACHE_READ_MISSES;
262         events[1] = QUADD_EVENT_TYPE_L2_DCACHE_WRITE_MISSES;
263         events[2] = QUADD_EVENT_TYPE_L2_ICACHE_MISSES;
264         return 3;
265 }
266
267 static struct quadd_event_source_interface l2x0_int = {
268         .enable                 = l2x0_events_enable,
269         .disable                = l2x0_events_disable,
270
271         .start                  = l2x0_events_start,
272         .stop                   = l2x0_events_stop,
273
274 #ifndef QUADD_USE_EMULATE_COUNTERS
275         .read                   = l2x0_events_read,
276 #else
277         .read                   = l2x0_events_read_emulate,
278 #endif
279         .set_events             = l2x0_set_events,
280         .get_supported_events   = get_supported_events,
281 };
282
283 struct quadd_event_source_interface *quadd_l2x0_events_init(void)
284 {
285         void __iomem *base;
286         unsigned long phys_addr;
287
288         l2x0_ctx.l2x0_event_type = -1;
289         l2x0_ctx.event_id = -1;
290
291         l2x0_ctx.l2x0_base = NULL;
292
293         phys_addr = quadd_get_pl310_phys_addr();
294         if (!phys_addr)
295                 return NULL;
296
297         base = ioremap(phys_addr, SZ_4K);
298         if (base) {
299                 u32 cache_id = readl(base + L2X0_CACHE_ID);
300
301                 if ((cache_id & 0xff0003c0) != 0x410000c0) {
302                         iounmap(base);
303                         return NULL;
304                 }
305         }
306
307         if (!base)
308                 return NULL;
309
310         l2x0_ctx.l2x0_base = base;
311
312         l2x0_clear_values();
313         spin_lock_init(&l2x0_ctx.lock);
314
315         pr_debug("pl310 init success, l2x0_base: %p\n", base);
316         return &l2x0_int;
317 }