diff options
Diffstat (limited to 'drivers/video/tegra/host/host1x/host1x_debug.c')
-rw-r--r-- | drivers/video/tegra/host/host1x/host1x_debug.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c new file mode 100644 index 00000000000..1a1d764bbd6 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_debug.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/host1x/host1x_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 | #include <linux/mm.h> | ||
23 | |||
24 | #include <linux/io.h> | ||
25 | |||
26 | #include "dev.h" | ||
27 | #include "debug.h" | ||
28 | #include "nvhost_cdma.h" | ||
29 | #include "../../nvmap/nvmap.h" | ||
30 | |||
31 | #include "host1x_hardware.h" | ||
32 | #include "host1x_cdma.h" | ||
33 | |||
34 | #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 | ||
35 | |||
36 | enum { | ||
37 | NVHOST_DBG_STATE_CMD = 0, | ||
38 | NVHOST_DBG_STATE_DATA = 1, | ||
39 | NVHOST_DBG_STATE_GATHER = 2 | ||
40 | }; | ||
41 | |||
42 | static int show_channel_command(struct output *o, u32 addr, u32 val, int *count) | ||
43 | { | ||
44 | unsigned mask; | ||
45 | unsigned subop; | ||
46 | |||
47 | switch (val >> 28) { | ||
48 | case 0x0: | ||
49 | mask = val & 0x3f; | ||
50 | if (mask) { | ||
51 | nvhost_debug_output(o, | ||
52 | "SETCL(class=%03x, offset=%03x, mask=%02x, [", | ||
53 | val >> 6 & 0x3ff, val >> 16 & 0xfff, mask); | ||
54 | *count = hweight8(mask); | ||
55 | return NVHOST_DBG_STATE_DATA; | ||
56 | } else { | ||
57 | nvhost_debug_output(o, "SETCL(class=%03x)\n", | ||
58 | val >> 6 & 0x3ff); | ||
59 | return NVHOST_DBG_STATE_CMD; | ||
60 | } | ||
61 | |||
62 | case 0x1: | ||
63 | nvhost_debug_output(o, "INCR(offset=%03x, [", | ||
64 | val >> 16 & 0xfff); | ||
65 | *count = val & 0xffff; | ||
66 | return NVHOST_DBG_STATE_DATA; | ||
67 | |||
68 | case 0x2: | ||
69 | nvhost_debug_output(o, "NONINCR(offset=%03x, [", | ||
70 | val >> 16 & 0xfff); | ||
71 | *count = val & 0xffff; | ||
72 | return NVHOST_DBG_STATE_DATA; | ||
73 | |||
74 | case 0x3: | ||
75 | mask = val & 0xffff; | ||
76 | nvhost_debug_output(o, "MASK(offset=%03x, mask=%03x, [", | ||
77 | val >> 16 & 0xfff, mask); | ||
78 | *count = hweight16(mask); | ||
79 | return NVHOST_DBG_STATE_DATA; | ||
80 | |||
81 | case 0x4: | ||
82 | nvhost_debug_output(o, "IMM(offset=%03x, data=%03x)\n", | ||
83 | val >> 16 & 0xfff, val & 0xffff); | ||
84 | return NVHOST_DBG_STATE_CMD; | ||
85 | |||
86 | case 0x5: | ||
87 | nvhost_debug_output(o, "RESTART(offset=%08x)\n", val << 4); | ||
88 | return NVHOST_DBG_STATE_CMD; | ||
89 | |||
90 | case 0x6: | ||
91 | nvhost_debug_output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", | ||
92 | val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1, | ||
93 | val & 0x3fff); | ||
94 | *count = val & 0x3fff; /* TODO: insert */ | ||
95 | return NVHOST_DBG_STATE_GATHER; | ||
96 | |||
97 | case 0xe: | ||
98 | subop = val >> 24 & 0xf; | ||
99 | if (subop == 0) | ||
100 | nvhost_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n", | ||
101 | val & 0xff); | ||
102 | else if (subop == 1) | ||
103 | nvhost_debug_output(o, "RELEASE_MLOCK(index=%d)\n", | ||
104 | val & 0xff); | ||
105 | else | ||
106 | nvhost_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val); | ||
107 | return NVHOST_DBG_STATE_CMD; | ||
108 | |||
109 | default: | ||
110 | return NVHOST_DBG_STATE_CMD; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static void show_channel_gather(struct output *o, u32 addr, | ||
115 | phys_addr_t phys_addr, u32 words, struct nvhost_cdma *cdma); | ||
116 | |||
117 | static void show_channel_word(struct output *o, int *state, int *count, | ||
118 | u32 addr, u32 val, struct nvhost_cdma *cdma) | ||
119 | { | ||
120 | static int start_count, dont_print; | ||
121 | |||
122 | switch (*state) { | ||
123 | case NVHOST_DBG_STATE_CMD: | ||
124 | if (addr) | ||
125 | nvhost_debug_output(o, "%08x: %08x:", addr, val); | ||
126 | else | ||
127 | nvhost_debug_output(o, "%08x:", val); | ||
128 | |||
129 | *state = show_channel_command(o, addr, val, count); | ||
130 | dont_print = 0; | ||
131 | start_count = *count; | ||
132 | if (*state == NVHOST_DBG_STATE_DATA && *count == 0) { | ||
133 | *state = NVHOST_DBG_STATE_CMD; | ||
134 | nvhost_debug_output(o, "])\n"); | ||
135 | } | ||
136 | break; | ||
137 | |||
138 | case NVHOST_DBG_STATE_DATA: | ||
139 | (*count)--; | ||
140 | if (start_count - *count < 64) | ||
141 | nvhost_debug_output(o, "%08x%s", | ||
142 | val, *count > 0 ? ", " : "])\n"); | ||
143 | else if (!dont_print && (*count > 0)) { | ||
144 | nvhost_debug_output(o, "[truncated; %d more words]\n", | ||
145 | *count); | ||
146 | dont_print = 1; | ||
147 | } | ||
148 | if (*count == 0) | ||
149 | *state = NVHOST_DBG_STATE_CMD; | ||
150 | break; | ||
151 | |||
152 | case NVHOST_DBG_STATE_GATHER: | ||
153 | *state = NVHOST_DBG_STATE_CMD; | ||
154 | nvhost_debug_output(o, "%08x]):\n", val); | ||
155 | if (cdma) { | ||
156 | show_channel_gather(o, addr, val, | ||
157 | *count, cdma); | ||
158 | } | ||
159 | break; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | static void show_channel_gather(struct output *o, u32 addr, | ||
164 | phys_addr_t phys_addr, | ||
165 | u32 words, struct nvhost_cdma *cdma) | ||
166 | { | ||
167 | #if defined(CONFIG_TEGRA_NVMAP) | ||
168 | /* Map dmaget cursor to corresponding nvmap_handle */ | ||
169 | struct push_buffer *pb = &cdma->push_buffer; | ||
170 | u32 cur = addr - pb->phys; | ||
171 | struct nvmap_client_handle *nvmap = &pb->nvmap[cur/8]; | ||
172 | struct nvmap_handle_ref ref; | ||
173 | u32 *map_addr, offset; | ||
174 | phys_addr_t pin_addr; | ||
175 | int state, count, i; | ||
176 | |||
177 | if ((u32)nvmap->handle == NVHOST_CDMA_PUSH_GATHER_CTXSAVE) { | ||
178 | nvhost_debug_output(o, "[context save]\n"); | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | if (!nvmap->handle || !nvmap->client | ||
183 | || atomic_read(&nvmap->handle->ref) < 1) { | ||
184 | nvhost_debug_output(o, "[already deallocated]\n"); | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | /* Create a fake nvmap_handle_ref - nvmap requires it | ||
189 | * but accesses only the first field - nvmap_handle */ | ||
190 | ref.handle = nvmap->handle; | ||
191 | |||
192 | map_addr = nvmap_mmap(&ref); | ||
193 | if (!map_addr) { | ||
194 | nvhost_debug_output(o, "[could not mmap]\n"); | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | /* Get base address from nvmap */ | ||
199 | pin_addr = nvmap_pin(nvmap->client, &ref); | ||
200 | if (IS_ERR_VALUE(pin_addr)) { | ||
201 | nvhost_debug_output(o, "[couldn't pin]\n"); | ||
202 | nvmap_munmap(&ref, map_addr); | ||
203 | return; | ||
204 | } | ||
205 | |||
206 | offset = phys_addr - pin_addr; | ||
207 | /* | ||
208 | * Sometimes we're given different hardware address to the same | ||
209 | * page - in these cases the offset will get an invalid number and | ||
210 | * we just have to bail out. | ||
211 | */ | ||
212 | if (offset > NVHOST_DEBUG_MAX_PAGE_OFFSET) { | ||
213 | nvhost_debug_output(o, "[address mismatch]\n"); | ||
214 | } else { | ||
215 | /* GATHER buffer starts always with commands */ | ||
216 | state = NVHOST_DBG_STATE_CMD; | ||
217 | for (i = 0; i < words; i++) | ||
218 | show_channel_word(o, &state, &count, | ||
219 | phys_addr + i * 4, | ||
220 | *(map_addr + offset/4 + i), | ||
221 | cdma); | ||
222 | } | ||
223 | nvmap_unpin(nvmap->client, &ref); | ||
224 | nvmap_munmap(&ref, map_addr); | ||
225 | #endif | ||
226 | } | ||
227 | |||
228 | static void show_channel_pair(struct output *o, u32 addr, | ||
229 | u32 w0, u32 w1, struct nvhost_cdma *cdma) | ||
230 | { | ||
231 | int state = NVHOST_DBG_STATE_CMD; | ||
232 | int count; | ||
233 | |||
234 | show_channel_word(o, &state, &count, addr, w0, cdma); | ||
235 | show_channel_word(o, &state, &count, addr+4, w1, cdma); | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * Retrieve the op pair at a slot offset from a DMA address | ||
240 | */ | ||
241 | static void cdma_peek(struct nvhost_cdma *cdma, | ||
242 | u32 dmaget, int slot, u32 *out) | ||
243 | { | ||
244 | u32 offset = dmaget - cdma->push_buffer.phys; | ||
245 | u32 *p = cdma->push_buffer.mapped; | ||
246 | |||
247 | offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; | ||
248 | out[0] = p[offset]; | ||
249 | out[1] = p[offset + 1]; | ||
250 | } | ||
251 | |||
252 | u32 previous_oppair(struct nvhost_cdma *cdma, u32 cur) | ||
253 | { | ||
254 | u32 pb = cdma->push_buffer.phys; | ||
255 | u32 prev = cur-8; | ||
256 | if (prev < pb) | ||
257 | prev += PUSH_BUFFER_SIZE; | ||
258 | return prev; | ||
259 | } | ||
260 | |||
261 | static void t20_debug_show_channel_cdma(struct nvhost_master *m, | ||
262 | struct nvhost_channel *ch, struct output *o, int chid) | ||
263 | { | ||
264 | struct nvhost_channel *channel = ch; | ||
265 | struct nvhost_cdma *cdma = &channel->cdma; | ||
266 | u32 dmaput, dmaget, dmactrl; | ||
267 | u32 cbstat, cbread; | ||
268 | u32 val, base, baseval; | ||
269 | u32 pbw[2]; | ||
270 | |||
271 | dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); | ||
272 | dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); | ||
273 | dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL); | ||
274 | cbread = readl(m->sync_aperture + HOST1X_SYNC_CBREAD_x(chid)); | ||
275 | cbstat = readl(m->sync_aperture + HOST1X_SYNC_CBSTAT_x(chid)); | ||
276 | |||
277 | nvhost_debug_output(o, "%d-%s (%d): ", chid, | ||
278 | channel->dev->name, | ||
279 | channel->dev->refcount); | ||
280 | |||
281 | if (HOST1X_VAL(CHANNEL_DMACTRL, DMASTOP, dmactrl) | ||
282 | || !channel->cdma.push_buffer.mapped) { | ||
283 | nvhost_debug_output(o, "inactive\n\n"); | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | switch (cbstat) { | ||
288 | case 0x00010008: | ||
289 | nvhost_debug_output(o, "waiting on syncpt %d val %d\n", | ||
290 | cbread >> 24, cbread & 0xffffff); | ||
291 | break; | ||
292 | |||
293 | case 0x00010009: | ||
294 | base = (cbread >> 16) & 0xff; | ||
295 | val = readl(m->sync_aperture + | ||
296 | HOST1X_SYNC_SYNCPT_BASE_x(base)); | ||
297 | baseval = HOST1X_VAL(SYNC_SYNCPT_BASE_0, BASE, val); | ||
298 | val = cbread & 0xffff; | ||
299 | nvhost_debug_output(o, "waiting on syncpt %d val %d " | ||
300 | "(base %d = %d; offset = %d)\n", | ||
301 | cbread >> 24, baseval + val, | ||
302 | base, baseval, val); | ||
303 | break; | ||
304 | |||
305 | default: | ||
306 | nvhost_debug_output(o, | ||
307 | "active class %02x, offset %04x, val %08x\n", | ||
308 | HOST1X_VAL(SYNC_CBSTAT_0, CBCLASS0, cbstat), | ||
309 | HOST1X_VAL(SYNC_CBSTAT_0, CBOFFSET0, cbstat), | ||
310 | cbread); | ||
311 | break; | ||
312 | } | ||
313 | |||
314 | nvhost_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", | ||
315 | dmaput, dmaget, dmactrl); | ||
316 | nvhost_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); | ||
317 | |||
318 | cdma_peek(cdma, dmaget, -1, pbw); | ||
319 | show_channel_pair(o, previous_oppair(cdma, dmaget), | ||
320 | pbw[0], pbw[1], &channel->cdma); | ||
321 | nvhost_debug_output(o, "\n"); | ||
322 | } | ||
323 | |||
324 | void t20_debug_show_channel_fifo(struct nvhost_master *m, | ||
325 | struct nvhost_channel *ch, struct output *o, int chid) | ||
326 | { | ||
327 | u32 val, rd_ptr, wr_ptr, start, end; | ||
328 | struct nvhost_channel *channel = ch; | ||
329 | int state, count; | ||
330 | |||
331 | nvhost_debug_output(o, "%d: fifo:\n", chid); | ||
332 | |||
333 | val = readl(channel->aperture + HOST1X_CHANNEL_FIFOSTAT); | ||
334 | nvhost_debug_output(o, "FIFOSTAT %08x\n", val); | ||
335 | if (HOST1X_VAL(CHANNEL_FIFOSTAT, CFEMPTY, val)) { | ||
336 | nvhost_debug_output(o, "[empty]\n"); | ||
337 | return; | ||
338 | } | ||
339 | |||
340 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
341 | writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) | ||
342 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid), | ||
343 | m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
344 | |||
345 | val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_PTRS); | ||
346 | rd_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_RD_PTR, val); | ||
347 | wr_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_WR_PTR, val); | ||
348 | |||
349 | val = readl(m->sync_aperture + HOST1X_SYNC_CFx_SETUP(chid)); | ||
350 | start = HOST1X_VAL(SYNC_CF0_SETUP, BASE, val); | ||
351 | end = HOST1X_VAL(SYNC_CF0_SETUP, LIMIT, val); | ||
352 | |||
353 | state = NVHOST_DBG_STATE_CMD; | ||
354 | |||
355 | do { | ||
356 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
357 | writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) | ||
358 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid) | ||
359 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, ADDR, rd_ptr), | ||
360 | m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
361 | val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_READ); | ||
362 | |||
363 | show_channel_word(o, &state, &count, 0, val, NULL); | ||
364 | |||
365 | if (rd_ptr == end) | ||
366 | rd_ptr = start; | ||
367 | else | ||
368 | rd_ptr++; | ||
369 | } while (rd_ptr != wr_ptr); | ||
370 | |||
371 | if (state == NVHOST_DBG_STATE_DATA) | ||
372 | nvhost_debug_output(o, ", ...])\n"); | ||
373 | nvhost_debug_output(o, "\n"); | ||
374 | |||
375 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
376 | } | ||
377 | |||
378 | static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) | ||
379 | { | ||
380 | u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0; | ||
381 | int i; | ||
382 | |||
383 | nvhost_debug_output(o, "---- mlocks ----\n"); | ||
384 | for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { | ||
385 | u32 owner = readl(mlo_regs + i); | ||
386 | if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CH_OWNS, owner)) | ||
387 | nvhost_debug_output(o, "%d: locked by channel %d\n", | ||
388 | i, HOST1X_VAL(SYNC_MLOCK_OWNER_0, CHID, owner)); | ||
389 | else if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CPU_OWNS, owner)) | ||
390 | nvhost_debug_output(o, "%d: locked by cpu\n", i); | ||
391 | else | ||
392 | nvhost_debug_output(o, "%d: unlocked\n", i); | ||
393 | } | ||
394 | nvhost_debug_output(o, "\n"); | ||
395 | } | ||
396 | |||
397 | int nvhost_init_t20_debug_support(struct nvhost_master *host) | ||
398 | { | ||
399 | host->op.debug.show_channel_cdma = t20_debug_show_channel_cdma; | ||
400 | host->op.debug.show_channel_fifo = t20_debug_show_channel_fifo; | ||
401 | host->op.debug.show_mlocks = t20_debug_show_mlocks; | ||
402 | |||
403 | return 0; | ||
404 | } | ||