diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/debug_gk20a.c')
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/debug_gk20a.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/debug_gk20a.c b/drivers/gpu/nvgpu/gk20a/debug_gk20a.c new file mode 100644 index 00000000..c5b6953c --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/debug_gk20a.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/t20/debug_gk20a.c | ||
3 | * | ||
4 | * Copyright (C) 2011-2014 NVIDIA Corporation. All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/nvhost.h> | ||
18 | #include <linux/debugfs.h> | ||
19 | #include <linux/seq_file.h> | ||
20 | |||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include "gk20a.h" | ||
24 | #include "debug_gk20a.h" | ||
25 | |||
26 | #include "hw_ram_gk20a.h" | ||
27 | #include "hw_fifo_gk20a.h" | ||
28 | #include "hw_ccsr_gk20a.h" | ||
29 | #include "hw_pbdma_gk20a.h" | ||
30 | |||
31 | unsigned int gk20a_debug_trace_cmdbuf; | ||
32 | struct platform_device *gk20a_device; | ||
33 | |||
34 | struct gk20a_debug_output { | ||
35 | void (*fn)(void *ctx, const char *str, size_t len); | ||
36 | void *ctx; | ||
37 | char buf[256]; | ||
38 | }; | ||
39 | |||
40 | static const char * const ccsr_chan_status_str[] = { | ||
41 | "idle", | ||
42 | "pending", | ||
43 | "pending_ctx_reload", | ||
44 | "pending_acquire", | ||
45 | "pending_acq_ctx_reload", | ||
46 | "on_pbdma", | ||
47 | "on_pbdma_and_eng", | ||
48 | "on_eng", | ||
49 | "on_eng_pending_acquire", | ||
50 | "on_eng_pending", | ||
51 | "on_pbdma_ctx_reload", | ||
52 | "on_pbdma_and_eng_ctx_reload", | ||
53 | "on_eng_ctx_reload", | ||
54 | "on_eng_pending_ctx_reload", | ||
55 | "on_eng_pending_acq_ctx_reload", | ||
56 | }; | ||
57 | |||
58 | static const char * const chan_status_str[] = { | ||
59 | "invalid", | ||
60 | "valid", | ||
61 | "chsw_load", | ||
62 | "chsw_save", | ||
63 | "chsw_switch", | ||
64 | }; | ||
65 | |||
66 | static const char * const ctx_status_str[] = { | ||
67 | "invalid", | ||
68 | "valid", | ||
69 | NULL, | ||
70 | NULL, | ||
71 | NULL, | ||
72 | "ctxsw_load", | ||
73 | "ctxsw_save", | ||
74 | "ctxsw_switch", | ||
75 | }; | ||
76 | |||
77 | static inline void gk20a_debug_write_printk(void *ctx, const char *str, | ||
78 | size_t len) | ||
79 | { | ||
80 | pr_info("%s", str); | ||
81 | } | ||
82 | |||
83 | static inline void gk20a_debug_write_to_seqfile(void *ctx, const char *str, | ||
84 | size_t len) | ||
85 | { | ||
86 | seq_write((struct seq_file *)ctx, str, len); | ||
87 | } | ||
88 | |||
89 | void gk20a_debug_output(struct gk20a_debug_output *o, const char *fmt, ...) | ||
90 | { | ||
91 | va_list args; | ||
92 | int len; | ||
93 | |||
94 | va_start(args, fmt); | ||
95 | len = vsnprintf(o->buf, sizeof(o->buf), fmt, args); | ||
96 | va_end(args); | ||
97 | o->fn(o->ctx, o->buf, len); | ||
98 | } | ||
99 | |||
100 | static void gk20a_debug_show_channel(struct gk20a *g, | ||
101 | struct gk20a_debug_output *o, | ||
102 | struct channel_gk20a *ch) | ||
103 | { | ||
104 | u32 channel = gk20a_readl(g, ccsr_channel_r(ch->hw_chid)); | ||
105 | u32 status = ccsr_channel_status_v(channel); | ||
106 | u32 syncpointa, syncpointb; | ||
107 | void *inst_ptr; | ||
108 | |||
109 | inst_ptr = ch->inst_block.cpuva; | ||
110 | if (!inst_ptr) | ||
111 | return; | ||
112 | |||
113 | syncpointa = gk20a_mem_rd32(inst_ptr, ram_fc_syncpointa_w()); | ||
114 | syncpointb = gk20a_mem_rd32(inst_ptr, ram_fc_syncpointb_w()); | ||
115 | |||
116 | gk20a_debug_output(o, "%d-%s, pid %d: ", ch->hw_chid, | ||
117 | ch->g->dev->name, | ||
118 | ch->pid); | ||
119 | gk20a_debug_output(o, "%s in use %s %s\n", | ||
120 | ccsr_channel_enable_v(channel) ? "" : "not", | ||
121 | ccsr_chan_status_str[status], | ||
122 | ccsr_channel_busy_v(channel) ? "busy" : "not busy"); | ||
123 | gk20a_debug_output(o, "TOP: %016llx PUT: %016llx GET: %016llx " | ||
124 | "FETCH: %016llx\nHEADER: %08x COUNT: %08x\n" | ||
125 | "SYNCPOINT %08x %08x SEMAPHORE %08x %08x %08x %08x\n", | ||
126 | (u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_top_level_get_w()) + | ||
127 | ((u64)gk20a_mem_rd32(inst_ptr, | ||
128 | ram_fc_pb_top_level_get_hi_w()) << 32ULL), | ||
129 | (u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_put_w()) + | ||
130 | ((u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_put_hi_w()) << 32ULL), | ||
131 | (u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_get_w()) + | ||
132 | ((u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_get_hi_w()) << 32ULL), | ||
133 | (u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_fetch_w()) + | ||
134 | ((u64)gk20a_mem_rd32(inst_ptr, ram_fc_pb_fetch_hi_w()) << 32ULL), | ||
135 | gk20a_mem_rd32(inst_ptr, ram_fc_pb_header_w()), | ||
136 | gk20a_mem_rd32(inst_ptr, ram_fc_pb_count_w()), | ||
137 | syncpointa, | ||
138 | syncpointb, | ||
139 | gk20a_mem_rd32(inst_ptr, ram_fc_semaphorea_w()), | ||
140 | gk20a_mem_rd32(inst_ptr, ram_fc_semaphoreb_w()), | ||
141 | gk20a_mem_rd32(inst_ptr, ram_fc_semaphorec_w()), | ||
142 | gk20a_mem_rd32(inst_ptr, ram_fc_semaphored_w())); | ||
143 | |||
144 | if ((pbdma_syncpointb_op_v(syncpointb) == pbdma_syncpointb_op_wait_v()) | ||
145 | && (pbdma_syncpointb_wait_switch_v(syncpointb) == | ||
146 | pbdma_syncpointb_wait_switch_en_v())) | ||
147 | gk20a_debug_output(o, "Waiting on syncpt %u (%s) val %u\n", | ||
148 | pbdma_syncpointb_syncpt_index_v(syncpointb), | ||
149 | nvhost_syncpt_get_name( | ||
150 | to_platform_device(g->dev->dev.parent), | ||
151 | pbdma_syncpointb_syncpt_index_v(syncpointb)), | ||
152 | pbdma_syncpointa_payload_v(syncpointa)); | ||
153 | |||
154 | gk20a_debug_output(o, "\n"); | ||
155 | } | ||
156 | |||
157 | void gk20a_debug_show_dump(struct platform_device *pdev, | ||
158 | struct gk20a_debug_output *o) | ||
159 | { | ||
160 | struct gk20a_platform *platform = gk20a_get_platform(pdev); | ||
161 | struct gk20a *g = platform->g; | ||
162 | struct fifo_gk20a *f = &g->fifo; | ||
163 | u32 chid; | ||
164 | int i; | ||
165 | |||
166 | gk20a_busy(g->dev); | ||
167 | for (i = 0; i < fifo_pbdma_status__size_1_v(); i++) { | ||
168 | u32 status = gk20a_readl(g, fifo_pbdma_status_r(i)); | ||
169 | u32 chan_status = fifo_pbdma_status_chan_status_v(status); | ||
170 | |||
171 | gk20a_debug_output(o, "%s pbdma %d: ", g->dev->name, i); | ||
172 | gk20a_debug_output(o, | ||
173 | "id: %d (%s), next_id: %d (%s) status: %s\n", | ||
174 | fifo_pbdma_status_id_v(status), | ||
175 | fifo_pbdma_status_id_type_v(status) ? | ||
176 | "tsg" : "channel", | ||
177 | fifo_pbdma_status_next_id_v(status), | ||
178 | fifo_pbdma_status_next_id_type_v(status) ? | ||
179 | "tsg" : "channel", | ||
180 | chan_status_str[chan_status]); | ||
181 | gk20a_debug_output(o, "PUT: %016llx GET: %016llx " | ||
182 | "FETCH: %08x HEADER: %08x\n", | ||
183 | (u64)gk20a_readl(g, pbdma_put_r(i)) + | ||
184 | ((u64)gk20a_readl(g, pbdma_put_hi_r(i)) << 32ULL), | ||
185 | (u64)gk20a_readl(g, pbdma_get_r(i)) + | ||
186 | ((u64)gk20a_readl(g, pbdma_get_hi_r(i)) << 32ULL), | ||
187 | gk20a_readl(g, pbdma_gp_fetch_r(i)), | ||
188 | gk20a_readl(g, pbdma_pb_header_r(i))); | ||
189 | } | ||
190 | gk20a_debug_output(o, "\n"); | ||
191 | |||
192 | for (i = 0; i < fifo_engine_status__size_1_v(); i++) { | ||
193 | u32 status = gk20a_readl(g, fifo_engine_status_r(i)); | ||
194 | u32 ctx_status = fifo_engine_status_ctx_status_v(status); | ||
195 | |||
196 | gk20a_debug_output(o, "%s eng %d: ", g->dev->name, i); | ||
197 | gk20a_debug_output(o, | ||
198 | "id: %d (%s), next_id: %d (%s), ctx: %s ", | ||
199 | fifo_engine_status_id_v(status), | ||
200 | fifo_engine_status_id_type_v(status) ? | ||
201 | "tsg" : "channel", | ||
202 | fifo_engine_status_next_id_v(status), | ||
203 | fifo_engine_status_next_id_type_v(status) ? | ||
204 | "tsg" : "channel", | ||
205 | ctx_status_str[ctx_status]); | ||
206 | |||
207 | if (fifo_engine_status_faulted_v(status)) | ||
208 | gk20a_debug_output(o, "faulted "); | ||
209 | if (fifo_engine_status_engine_v(status)) | ||
210 | gk20a_debug_output(o, "busy "); | ||
211 | gk20a_debug_output(o, "\n"); | ||
212 | } | ||
213 | gk20a_debug_output(o, "\n"); | ||
214 | |||
215 | for (chid = 0; chid < f->num_channels; chid++) { | ||
216 | if (f->channel[chid].in_use) { | ||
217 | struct channel_gk20a *gpu_ch = &f->channel[chid]; | ||
218 | gk20a_debug_show_channel(g, o, gpu_ch); | ||
219 | } | ||
220 | } | ||
221 | gk20a_idle(g->dev); | ||
222 | } | ||
223 | |||
224 | void gk20a_debug_dump(struct platform_device *pdev) | ||
225 | { | ||
226 | struct gk20a_platform *platform = gk20a_get_platform(pdev); | ||
227 | struct gk20a_debug_output o = { | ||
228 | .fn = gk20a_debug_write_printk | ||
229 | }; | ||
230 | |||
231 | if (platform->dump_platform_dependencies) | ||
232 | platform->dump_platform_dependencies(pdev); | ||
233 | |||
234 | gk20a_debug_show_dump(pdev, &o); | ||
235 | } | ||
236 | |||
237 | void gk20a_debug_dump_device(struct platform_device *pdev) | ||
238 | { | ||
239 | struct gk20a_debug_output o = { | ||
240 | .fn = gk20a_debug_write_printk | ||
241 | }; | ||
242 | |||
243 | /* Dump the first device if no info is provided */ | ||
244 | if (!pdev && gk20a_device) | ||
245 | pdev = gk20a_device; | ||
246 | |||
247 | gk20a_debug_show_dump(pdev, &o); | ||
248 | } | ||
249 | EXPORT_SYMBOL(gk20a_debug_dump_device); | ||
250 | |||
251 | static int gk20a_debug_show(struct seq_file *s, void *unused) | ||
252 | { | ||
253 | struct platform_device *pdev = s->private; | ||
254 | struct gk20a_debug_output o = { | ||
255 | .fn = gk20a_debug_write_to_seqfile, | ||
256 | .ctx = s, | ||
257 | }; | ||
258 | gk20a_debug_show_dump(pdev, &o); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int gk20a_debug_open(struct inode *inode, struct file *file) | ||
263 | { | ||
264 | return single_open(file, gk20a_debug_show, inode->i_private); | ||
265 | } | ||
266 | |||
267 | static const struct file_operations gk20a_debug_fops = { | ||
268 | .open = gk20a_debug_open, | ||
269 | .read = seq_read, | ||
270 | .llseek = seq_lseek, | ||
271 | .release = single_release, | ||
272 | }; | ||
273 | |||
274 | void gk20a_debug_init(struct platform_device *pdev) | ||
275 | { | ||
276 | struct gk20a_platform *platform = platform_get_drvdata(pdev); | ||
277 | |||
278 | /* Store the first device */ | ||
279 | if (!gk20a_device) | ||
280 | gk20a_device = pdev; | ||
281 | |||
282 | platform->debugfs = debugfs_create_dir(pdev->name, NULL); | ||
283 | |||
284 | debugfs_create_file("status", S_IRUGO, platform->debugfs, | ||
285 | pdev, &gk20a_debug_fops); | ||
286 | debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, platform->debugfs, | ||
287 | &gk20a_debug_trace_cmdbuf); | ||
288 | |||
289 | #if defined(GK20A_DEBUG) | ||
290 | debugfs_create_u32("dbg_mask", S_IRUGO|S_IWUSR, platform->debugfs, | ||
291 | &gk20a_dbg_mask); | ||
292 | debugfs_create_u32("dbg_ftrace", S_IRUGO|S_IWUSR, platform->debugfs, | ||
293 | &gk20a_dbg_ftrace); | ||
294 | #endif | ||
295 | } | ||