ARM: tegra: Use proper type for physical addresses
[linux-2.6.git] / drivers / video / tegra / host / debug.c
1 /*
2  * drivers/video/tegra/host/debug.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  * Author: Erik Gilling <konkers@android.com>
6  *
7  * Copyright (C) 2011 NVIDIA Corporation
8  *
9  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <linux/debugfs.h>
21 #include <linux/seq_file.h>
22
23 #include <asm/io.h>
24
25 #include "dev.h"
26
27 struct output {
28         void (*fn)(void *ctx, const char* str, size_t len);
29         void *ctx;
30         char buf[256];
31 };
32
33 static void write_to_seqfile(void *ctx, const char* str, size_t len)
34 {
35         seq_write((struct seq_file *)ctx, str, len);
36 }
37
38 static void write_to_printk(void *ctx, const char* str, size_t len)
39 {
40         printk("%s", str);
41 }
42
43 static void output(struct output *o, const char* fmt, ...)
44 {
45         va_list args;
46         int len;
47
48         va_start(args, fmt);
49         len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
50         va_end(args);
51         o->fn(o->ctx, o->buf, len);
52 }
53
54 enum {
55         NVHOST_DBG_STATE_CMD = 0,
56         NVHOST_DBG_STATE_DATA = 1,
57         NVHOST_DBG_STATE_GATHER = 2
58 };
59
60 static int show_channel_command(struct output *o, u32 val, int *count)
61 {
62         unsigned mask;
63         unsigned subop;
64
65         switch (val >> 28) {
66         case 0x0:
67                 mask = val & 0x3f;
68                 if (mask) {
69                         output(o, "SETCL(class=%03x, offset=%03x, mask=%02x, [",
70                                    val >> 6 & 0x3ff, val >> 16 & 0xfff, mask);
71                         *count = hweight8(mask);
72                         return NVHOST_DBG_STATE_DATA;
73                 } else {
74                         output(o, "SETCL(class=%03x)\n", val >> 6 & 0x3ff);
75                         return NVHOST_DBG_STATE_CMD;
76                 }
77
78         case 0x1:
79                 output(o, "INCR(offset=%03x, [", val >> 16 & 0xfff);
80                 *count = val & 0xffff;
81                 return NVHOST_DBG_STATE_DATA;
82
83         case 0x2:
84                 output(o, "NONINCR(offset=%03x, [", val >> 16 & 0xfff);
85                 *count = val & 0xffff;
86                 return NVHOST_DBG_STATE_DATA;
87
88         case 0x3:
89                 mask = val & 0xffff;
90                 output(o, "MASK(offset=%03x, mask=%03x, [",
91                            val >> 16 & 0xfff, mask);
92                 *count = hweight16(mask);
93                 return NVHOST_DBG_STATE_DATA;
94
95         case 0x4:
96                 output(o, "IMM(offset=%03x, data=%03x)\n",
97                            val >> 16 & 0xfff, val & 0xffff);
98                 return NVHOST_DBG_STATE_CMD;
99
100         case 0x5:
101                 output(o, "RESTART(offset=%08x)\n", val << 4);
102                 return NVHOST_DBG_STATE_CMD;
103
104         case 0x6:
105                 output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[",
106                            val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1,
107                            val & 0x3fff);
108                 *count = val & 0x3fff; // TODO: insert
109                 return NVHOST_DBG_STATE_GATHER;
110
111         case 0xe:
112                 subop = val >> 24 & 0xf;
113                 if (subop == 0)
114                         output(o, "ACQUIRE_MLOCK(index=%d)\n", val & 0xff);
115                 else if (subop == 1)
116                         output(o, "RELEASE_MLOCK(index=%d)\n", val & 0xff);
117                 else
118                         output(o, "EXTEND_UNKNOWN(%08x)\n", val);
119                 return NVHOST_DBG_STATE_CMD;
120
121         default:
122                 return NVHOST_DBG_STATE_CMD;
123         }
124 }
125
126 static void show_channel_gather(struct output *o, phys_addr_t phys_addr,
127                                 u32 words);
128
129 static void show_channel_word(struct output *o, int *state, int *count,
130                                      u32 addr, u32 val)
131 {
132         switch (*state) {
133         case NVHOST_DBG_STATE_CMD:
134                 if (addr)
135                         output(o, "%08x: %08x:", addr, val);
136                 else
137                         output(o, "%08x:", val);
138
139                 *state = show_channel_command(o, val, count);
140                 if (*state == NVHOST_DBG_STATE_DATA && *count == 0) {
141                         *state = NVHOST_DBG_STATE_CMD;
142                         output(o, "])\n");
143                 }
144                 break;
145
146         case NVHOST_DBG_STATE_DATA:
147                 (*count)--;
148                 output(o, "%08x%s", val, *count > 0 ? ", " : "])\n");
149                 if (*count == 0)
150                         *state = NVHOST_DBG_STATE_CMD;
151                 break;
152
153         case NVHOST_DBG_STATE_GATHER:
154                 *state = NVHOST_DBG_STATE_CMD;
155                 output(o, "%08x]):\n", val);
156                 show_channel_gather(o, val, *count);
157                 break;
158         }
159 }
160
161 /*
162  * TODO: This uses ioremap_xxx on memory which is deprecated.
163  * Also, it won't work properly with SMMU.
164  */
165 static void show_channel_gather(struct output *o, phys_addr_t phys_addr,
166                                 u32 words)
167 {
168         phys_addr_t map_base = phys_addr & PAGE_MASK;
169         phys_addr_t map_end = (phys_addr + words * 4 + PAGE_SIZE - 1) & PAGE_MASK;
170         phys_addr_t map_size = map_end - map_base;
171         phys_addr_t map_offset = phys_addr - map_base;
172         void *map_addr = ioremap_nocache(map_base, map_size);
173         int state = NVHOST_DBG_STATE_CMD;
174         int count, i;
175
176         if (!map_addr)
177                 return;
178         for (i = 0; i < words; i++)
179                 show_channel_word(o, &state, &count, phys_addr + i * 4,
180                                 readl(map_addr + map_offset + i * 4));
181         iounmap(map_addr);
182 }
183
184 static void show_channel_pair(struct output *o, u32 addr,
185                                 u32 w0, u32 w1)
186 {
187         int state = NVHOST_DBG_STATE_CMD;
188         int count;
189
190         show_channel_word(o, &state, &count, addr, w0);
191         show_channel_word(o, &state, &count, addr, w1);
192 }
193
194 static void show_channel_cdma(struct nvhost_master *m,
195                         struct output *o, int chid)
196 {
197         struct nvhost_channel *channel = m->channels + chid;
198         u32 dmaput, dmaget, dmactrl;
199         u32 cbstat, cbread;
200         u32 val, base, baseval;
201         u32 pbw[2];
202
203         dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT);
204         dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET);
205         dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL);
206         cbread = readl(m->aperture + HOST1X_SYNC_CBREAD(chid));
207         cbstat = readl(m->aperture + HOST1X_SYNC_CBSTAT(chid));
208
209         output(o, "%d-%s (%d): ", chid,
210                 channel->mod.name, atomic_read(&channel->mod.refcount));
211
212         if ((dmactrl & 1) || !channel->cdma.push_buffer.mapped) {
213                 output(o, "inactive\n\n");
214                 return;
215         }
216
217         switch (cbstat) {
218         case 0x00010008:
219                 output(o, "waiting on syncpt %d val %d\n",
220                         cbread >> 24, cbread & 0xffffff);
221                 break;
222
223         case 0x00010009:
224                 base = (cbread >> 16) & 0xff;
225                 val = readl(m->aperture + HOST1X_SYNC_SYNCPT_BASE(base));
226                 baseval = val & 0xffff;
227                 val = cbread & 0xffff;
228                 output(o, "waiting on syncpt %d val %d "
229                           "(base %d = %d; offset = %d)\n",
230                         cbread >> 24, baseval + val,
231                         base, baseval, val);
232                 break;
233
234         default:
235                 output(o, "active class %02x, offset %04x, val %08x\n",
236                         cbstat >> 16, cbstat & 0xffff, cbread);
237                 break;
238         }
239
240         nvhost_cdma_peek(&channel->cdma, dmaget, -1, pbw);
241         show_channel_pair(o, chid, pbw[0], pbw[1]);
242         output(o, "\n");
243 }
244
245 void show_channel_fifo(struct nvhost_master *m,
246                         struct output *o, int chid)
247 {
248         u32 val, rd_ptr, wr_ptr, start, end;
249         int state, count;
250
251         val = readl(m->aperture + HOST1X_CHANNEL_FIFOSTAT);
252         if (val & (1 << 10))
253                 return;
254
255         writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL);
256         writel((1 << 31) | (chid << 16),
257                 m->aperture + HOST1X_SYNC_CFPEEK_CTRL);
258
259         val = readl(m->aperture + HOST1X_SYNC_CFPEEK_PTRS);
260         rd_ptr = val & 0x1ff;
261         wr_ptr = (val >> 16) & 0x1ff;
262
263         val = readl(m->aperture + HOST1X_SYNC_CF_SETUP(chid));
264         start = val & 0x1ff;
265         end = (val >> 16) & 0x1ff;
266
267         state = NVHOST_DBG_STATE_CMD;
268         output(o, "%d: fifo:\n", chid);
269
270         do {
271                 writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL);
272                 writel((1 << 31) | (chid << 16) | rd_ptr,
273                         m->aperture + HOST1X_SYNC_CFPEEK_CTRL);
274                 val = readl(m->aperture + HOST1X_SYNC_CFPEEK_READ);
275
276                 show_channel_word(o, &state, &count, 0, val);
277
278                 if (rd_ptr == end)
279                         rd_ptr = start;
280                 else
281                         rd_ptr++;
282         } while (rd_ptr != wr_ptr);
283
284         if (state == NVHOST_DBG_STATE_DATA)
285                 output(o, ", ...])\n");
286         output(o, "\n");
287
288         writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL);
289 }
290
291 static void show_channels(struct nvhost_master *m, struct output *o)
292 {
293         int i;
294         output(o, "---- channels ----\n");
295         for (i = 0; i < NVHOST_NUMCHANNELS; i++) {
296                 show_channel_cdma(m, o, i);
297                 show_channel_fifo(m, o, i);
298         }
299 }
300
301 static void show_mlocks(struct nvhost_master *m, struct output *o)
302 {
303         u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0;
304         int i;
305
306         output(o, "---- mlocks ----\n");
307         for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) {
308                 u32 owner = readl(mlo_regs + i);
309                 if (owner & 0x1)
310                         output(o, "%d: locked by channel %d\n",
311                                 i, (owner >> 8) & 0xf);
312                 else if (owner & 0x2)
313                         output(o, "%d: locked by cpu\n", i);
314                 else
315                         output(o, "%d: unlocked\n", i);
316         }
317         output(o, "\n");
318 }
319
320 static void show_syncpts(struct nvhost_master *m, struct output *o)
321 {
322         int i;
323
324         output(o, "---- syncpts ----\n");
325         for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) {
326                 u32 max = nvhost_syncpt_read_max(&m->syncpt, i);
327                 if (!max)
328                         continue;
329                 output(o, "id %d (%s) min %d max %d\n",
330                         i, nvhost_syncpt_name(i),
331                         nvhost_syncpt_update_min(&m->syncpt, i), max);
332
333         }
334         output(o, "\n");
335 }
336
337 static void show_all(struct nvhost_master *m, struct output *o)
338 {
339         nvhost_module_busy(&m->mod);
340
341         show_mlocks(m, o);
342         show_syncpts(m, o);
343         show_channels(m, o);
344
345         nvhost_module_idle(&m->mod);
346 }
347
348
349 #ifdef CONFIG_DEBUG_FS
350 static int nvhost_debug_show(struct seq_file *s, void *unused)
351 {
352         struct output o = {
353                 .fn = write_to_seqfile,
354                 .ctx = s
355         };
356         show_all(s->private, &o);
357         return 0;
358 }
359
360 static int nvhost_debug_open(struct inode *inode, struct file *file)
361 {
362         return single_open(file, nvhost_debug_show, inode->i_private);
363 }
364
365 static const struct file_operations nvhost_debug_fops = {
366         .open           = nvhost_debug_open,
367         .read           = seq_read,
368         .llseek         = seq_lseek,
369         .release        = single_release,
370 };
371
372 void nvhost_debug_init(struct nvhost_master *master)
373 {
374         debugfs_create_file("tegra_host", S_IRUGO, NULL,
375                         master, &nvhost_debug_fops);
376 }
377 #else
378 void nvhost_debug_init(struct nvhost_master *master)
379 {
380 }
381 #endif
382
383 void nvhost_debug_dump(struct nvhost_master *master)
384 {
385         struct output o = {
386                 .fn = write_to_printk
387         };
388         show_all(master, &o);
389 }