video: tegra: host: use runtime pm for clock management
[linux-2.6.git] / drivers / video / tegra / host / host1x / host1x_debug.c
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 output *o, int chid)
263 {
264         struct nvhost_channel *channel = m->channels + chid;
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                     atomic_read(&channel->dev->dev.power.usage_count));
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 output *o, int chid)
326 {
327         u32 val, rd_ptr, wr_ptr, start, end;
328         struct nvhost_channel *channel = m->channels + chid;
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 }