blob: fbb78117d57fcf7f3530d97c2435613917e31fd1 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14/* #define DEBUG */
15#define ALIGN_CPU
16
17#include <linux/spinlock.h>
18#include <linux/debugfs.h>
19#include <linux/relay.h>
20#include <linux/slab.h>
21#include <linux/time.h>
22#include <linux/sched.h>
Jeremy Gebben6061bc72011-04-18 15:23:16 -060023#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024
25#include "kgsl.h"
26#include "kgsl_cffdump.h"
27#include "kgsl_debugfs.h"
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060028#include "kgsl_log.h"
29#include "kgsl_sharedmem.h"
30#include "adreno_pm4types.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32static struct rchan *chan;
33static struct dentry *dir;
34static int suspended;
35static size_t dropped;
36static size_t subbuf_size = 256*1024;
37static size_t n_subbufs = 64;
38
39/* forward declarations */
40static void destroy_channel(void);
41static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs);
42
43static spinlock_t cffdump_lock;
44static ulong serial_nr;
45static ulong total_bytes;
46static ulong total_syncmem;
47static long last_sec;
48
49#define MEMBUF_SIZE 64
50
51#define CFF_OP_WRITE_REG 0x00000002
52struct cff_op_write_reg {
53 unsigned char op;
54 uint addr;
55 uint value;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060056} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057
58#define CFF_OP_POLL_REG 0x00000004
59struct cff_op_poll_reg {
60 unsigned char op;
61 uint addr;
62 uint value;
63 uint mask;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060064} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
66#define CFF_OP_WAIT_IRQ 0x00000005
67struct cff_op_wait_irq {
68 unsigned char op;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060069} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070
71#define CFF_OP_VERIFY_MEM_FILE 0x00000007
72#define CFF_OP_RMW 0x0000000a
73
74#define CFF_OP_WRITE_MEM 0x0000000b
75struct cff_op_write_mem {
76 unsigned char op;
77 uint addr;
78 uint value;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060079} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080
81#define CFF_OP_WRITE_MEMBUF 0x0000000c
82struct cff_op_write_membuf {
83 unsigned char op;
84 uint addr;
85 ushort count;
86 uint buffer[MEMBUF_SIZE];
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060087} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088
Jeremy Gebben6061bc72011-04-18 15:23:16 -060089#define CFF_OP_MEMORY_BASE 0x0000000d
90struct cff_op_memory_base {
91 unsigned char op;
92 uint base;
93 uint size;
94 uint gmemsize;
95} __packed;
96
97#define CFF_OP_HANG 0x0000000e
98struct cff_op_hang {
99 unsigned char op;
100} __packed;
101
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102#define CFF_OP_EOF 0xffffffff
103struct cff_op_eof {
104 unsigned char op;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600105} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106
107
108static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len)
109{
110 static const char tob64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno"
111 "pqrstuvwxyz0123456789+/";
112
113 out[0] = tob64[in[0] >> 2];
114 out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
115 out[2] = (unsigned char) (len > 1 ? tob64[((in[1] & 0x0f) << 2)
116 | ((in[2] & 0xc0) >> 6)] : '=');
117 out[3] = (unsigned char) (len > 2 ? tob64[in[2] & 0x3f] : '=');
118}
119
120static void b64_encode(const unsigned char *in_buf, int in_size,
121 unsigned char *out_buf, int out_bufsize, int *out_size)
122{
123 unsigned char in[3], out[4];
124 int i, len;
125
126 *out_size = 0;
127 while (in_size > 0) {
128 len = 0;
129 for (i = 0; i < 3; ++i) {
130 if (in_size-- > 0) {
131 in[i] = *in_buf++;
132 ++len;
133 } else
134 in[i] = 0;
135 }
136 if (len) {
137 b64_encodeblock(in, out, len);
138 if (out_bufsize < 4) {
139 pr_warn("kgsl: cffdump: %s: out of buffer\n",
140 __func__);
141 return;
142 }
143 for (i = 0; i < 4; ++i)
144 *out_buf++ = out[i];
145 *out_size += 4;
146 out_bufsize -= 4;
147 }
148 }
149}
150
151#define KLOG_TMPBUF_SIZE (1024)
152static void klog_printk(const char *fmt, ...)
153{
154 /* per-cpu klog formatting temporary buffer */
155 static char klog_buf[NR_CPUS][KLOG_TMPBUF_SIZE];
156
157 va_list args;
158 int len;
159 char *cbuf;
160 unsigned long flags;
161
162 local_irq_save(flags);
163 cbuf = klog_buf[smp_processor_id()];
164 va_start(args, fmt);
165 len = vsnprintf(cbuf, KLOG_TMPBUF_SIZE, fmt, args);
166 total_bytes += len;
167 va_end(args);
168 relay_write(chan, cbuf, len);
169 local_irq_restore(flags);
170}
171
172static struct cff_op_write_membuf cff_op_write_membuf;
173static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize)
174{
175 void *data;
176 int len, out_size;
177 struct cff_op_write_mem cff_op_write_mem;
178
179 uint addr = cff_op_write_membuf.addr
180 - sizeof(uint)*cff_op_write_membuf.count;
181
182 if (!cff_op_write_membuf.count) {
183 pr_warn("kgsl: cffdump: membuf: count == 0, skipping");
184 return;
185 }
186
187 if (cff_op_write_membuf.count != 1) {
188 cff_op_write_membuf.op = CFF_OP_WRITE_MEMBUF;
189 cff_op_write_membuf.addr = addr;
190 len = sizeof(cff_op_write_membuf) -
191 sizeof(uint)*(MEMBUF_SIZE - cff_op_write_membuf.count);
192 data = &cff_op_write_membuf;
193 } else {
194 cff_op_write_mem.op = CFF_OP_WRITE_MEM;
195 cff_op_write_mem.addr = addr;
196 cff_op_write_mem.value = cff_op_write_membuf.buffer[0];
197 data = &cff_op_write_mem;
198 len = sizeof(cff_op_write_mem);
199 }
200 b64_encode(data, len, out_buf, out_bufsize, &out_size);
201 out_buf[out_size] = 0;
202 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
203 cff_op_write_membuf.count = 0;
204 cff_op_write_membuf.addr = 0;
205}
206
207static void cffdump_printline(int id, uint opcode, uint op1, uint op2,
208 uint op3)
209{
210 struct cff_op_write_reg cff_op_write_reg;
211 struct cff_op_poll_reg cff_op_poll_reg;
212 struct cff_op_wait_irq cff_op_wait_irq;
Jeremy Gebben6061bc72011-04-18 15:23:16 -0600213 struct cff_op_memory_base cff_op_memory_base;
214 struct cff_op_hang cff_op_hang;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 struct cff_op_eof cff_op_eof;
216 unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16];
217 void *data;
218 int len = 0, out_size;
219 long cur_secs;
220
221 spin_lock(&cffdump_lock);
222 if (opcode == CFF_OP_WRITE_MEM) {
223 if (op1 < 0x40000000 || op1 >= 0x60000000)
224 KGSL_CORE_ERR("addr out-of-range: op1=%08x", op1);
225 if ((cff_op_write_membuf.addr != op1 &&
226 cff_op_write_membuf.count)
227 || (cff_op_write_membuf.count == MEMBUF_SIZE))
228 cffdump_membuf(id, out_buf, sizeof(out_buf));
229
230 cff_op_write_membuf.buffer[cff_op_write_membuf.count++] = op2;
231 cff_op_write_membuf.addr = op1 + sizeof(uint);
232 spin_unlock(&cffdump_lock);
233 return;
234 } else if (cff_op_write_membuf.count)
235 cffdump_membuf(id, out_buf, sizeof(out_buf));
236 spin_unlock(&cffdump_lock);
237
238 switch (opcode) {
239 case CFF_OP_WRITE_REG:
240 cff_op_write_reg.op = opcode;
241 cff_op_write_reg.addr = op1;
242 cff_op_write_reg.value = op2;
243 data = &cff_op_write_reg;
244 len = sizeof(cff_op_write_reg);
245 break;
246
247 case CFF_OP_POLL_REG:
248 cff_op_poll_reg.op = opcode;
249 cff_op_poll_reg.addr = op1;
250 cff_op_poll_reg.value = op2;
251 cff_op_poll_reg.mask = op3;
252 data = &cff_op_poll_reg;
253 len = sizeof(cff_op_poll_reg);
254 break;
255
256 case CFF_OP_WAIT_IRQ:
257 cff_op_wait_irq.op = opcode;
258 data = &cff_op_wait_irq;
259 len = sizeof(cff_op_wait_irq);
260 break;
261
Jeremy Gebben6061bc72011-04-18 15:23:16 -0600262 case CFF_OP_MEMORY_BASE:
263 cff_op_memory_base.op = opcode;
264 cff_op_memory_base.base = op1;
265 cff_op_memory_base.size = op2;
266 cff_op_memory_base.gmemsize = op3;
267 data = &cff_op_memory_base;
268 len = sizeof(cff_op_memory_base);
269 break;
270
271 case CFF_OP_HANG:
272 cff_op_hang.op = opcode;
273 data = &cff_op_hang;
274 len = sizeof(cff_op_hang);
275 break;
276
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 case CFF_OP_EOF:
278 cff_op_eof.op = opcode;
279 data = &cff_op_eof;
280 len = sizeof(cff_op_eof);
281 break;
282 }
283
284 if (len) {
285 b64_encode(data, len, out_buf, sizeof(out_buf), &out_size);
286 out_buf[out_size] = 0;
287 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
288 } else
289 pr_warn("kgsl: cffdump: unhandled opcode: %d\n", opcode);
290
291 cur_secs = get_seconds();
292 if ((cur_secs - last_sec) > 10 || (last_sec - cur_secs) > 10) {
293 pr_info("kgsl: cffdump: total [bytes:%lu kB, syncmem:%lu kB], "
294 "seq#: %lu\n", total_bytes/1024, total_syncmem/1024,
295 serial_nr);
296 last_sec = cur_secs;
297 }
298}
299
300void kgsl_cffdump_init()
301{
302 struct dentry *debugfs_dir = kgsl_get_debugfs_dir();
303
304#ifdef ALIGN_CPU
305 cpumask_t mask;
306
307 cpumask_clear(&mask);
Lucille Sylvester57996e32011-05-26 19:12:22 -0600308 cpumask_set_cpu(0, &mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 sched_setaffinity(0, &mask);
310#endif
311 if (!debugfs_dir || IS_ERR(debugfs_dir)) {
312 KGSL_CORE_ERR("Debugfs directory is bad\n");
313 return;
314 }
315
316 kgsl_cff_dump_enable = 1;
317
318 spin_lock_init(&cffdump_lock);
319
320 dir = debugfs_create_dir("cff", debugfs_dir);
321 if (!dir) {
322 KGSL_CORE_ERR("debugfs_create_dir failed\n");
323 return;
324 }
325
326 chan = create_channel(subbuf_size, n_subbufs);
327}
328
329void kgsl_cffdump_destroy()
330{
331 if (chan)
332 relay_flush(chan);
333 destroy_channel();
334 if (dir)
335 debugfs_remove(dir);
336}
337
338void kgsl_cffdump_open(enum kgsl_deviceid device_id)
339{
Jeremy Gebben6061bc72011-04-18 15:23:16 -0600340 /*TODO: move this to where we can report correct gmemsize*/
341 unsigned int va_base;
342
343 if (cpu_is_msm8x60() || cpu_is_msm8960())
344 va_base = 0x40000000;
345 else
346 va_base = 0x20000000;
347
348 kgsl_cffdump_memory_base(device_id, va_base,
349 CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K);
350}
351
352void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
353 unsigned int range, unsigned gmemsize)
354{
355 cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base, range, gmemsize);
356}
357
358void kgsl_cffdump_hang(enum kgsl_deviceid device_id)
359{
360 cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361}
362
363void kgsl_cffdump_close(enum kgsl_deviceid device_id)
364{
365 cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0);
366}
367
368void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
369 const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
370 bool clean_cache)
371{
372 const void *src;
373 uint host_size;
374 uint physaddr;
375
376 if (!kgsl_cff_dump_enable)
377 return;
378
379 total_syncmem += sizebytes;
380
381 if (memdesc == NULL) {
382 struct kgsl_mem_entry *entry;
383 spin_lock(&dev_priv->process_priv->mem_lock);
384 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
385 gpuaddr, sizebytes);
386 spin_unlock(&dev_priv->process_priv->mem_lock);
387 if (entry == NULL) {
388 KGSL_CORE_ERR("did not find mapping "
389 "for gpuaddr: 0x%08x\n", gpuaddr);
390 return;
391 }
392 memdesc = &entry->memdesc;
393 }
394 BUG_ON(memdesc->gpuaddr == 0);
395 BUG_ON(gpuaddr == 0);
396 physaddr = kgsl_get_realaddr(memdesc) + (gpuaddr - memdesc->gpuaddr);
397
398 src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
399 if (src == NULL || host_size < sizebytes) {
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600400 KGSL_CORE_ERR("did not find mapping for "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n",
402 gpuaddr, memdesc->hostptr, memdesc->physaddr);
403 return;
404 }
405
406 if (clean_cache) {
407 /* Ensure that this memory region is not read from the
408 * cache but fetched fresh */
409
410 mb();
411
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600412 kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
413 KGSL_CACHE_OP_INV);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 }
415
416 BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff);
417 while (sizebytes > 3) {
418 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
419 0);
420 physaddr += 4;
421 src += 4;
422 sizebytes -= 4;
423 }
424 if (sizebytes > 0)
425 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
426 0);
427}
428
429void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes)
430{
431 if (!kgsl_cff_dump_enable)
432 return;
433
434 BUG_ON(addr > 0x66000000 && addr < 0x66ffffff);
435 while (sizebytes > 3) {
436 /* Use 32bit memory writes as long as there's at least
437 * 4 bytes left */
438 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
439 addr += 4;
440 sizebytes -= 4;
441 }
442 if (sizebytes > 0)
443 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
444}
445
446void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
447 uint value)
448{
449 if (!kgsl_cff_dump_enable)
450 return;
451
452 cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, 0);
453}
454
455void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
456 uint value, uint mask)
457{
458 if (!kgsl_cff_dump_enable)
459 return;
460
461 cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, mask);
462}
463
464void kgsl_cffdump_slavewrite(uint addr, uint value)
465{
466 if (!kgsl_cff_dump_enable)
467 return;
468
469 cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0);
470}
471
472int kgsl_cffdump_waitirq(void)
473{
474 if (!kgsl_cff_dump_enable)
475 return 0;
476
477 cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0);
478
479 return 1;
480}
481EXPORT_SYMBOL(kgsl_cffdump_waitirq);
482
483#define ADDRESS_STACK_SIZE 256
484#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF)
485static unsigned int kgsl_cffdump_addr_count;
486
487static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv,
488 uint *hostaddr, bool check_only)
489{
490 static uint addr_stack[ADDRESS_STACK_SIZE];
491 static uint size_stack[ADDRESS_STACK_SIZE];
492
493 switch (GET_PM4_TYPE3_OPCODE(hostaddr)) {
494 case PM4_INDIRECT_BUFFER_PFD:
495 case PM4_INDIRECT_BUFFER:
496 {
497 /* traverse indirect buffers */
498 int i;
499 uint ibaddr = hostaddr[1];
500 uint ibsize = hostaddr[2];
501
502 /* is this address already in encountered? */
503 for (i = 0;
504 i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr;
505 ++i)
506 ;
507
508 if (kgsl_cffdump_addr_count == i) {
509 addr_stack[kgsl_cffdump_addr_count] = ibaddr;
510 size_stack[kgsl_cffdump_addr_count++] = ibsize;
511
512 if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) {
513 KGSL_CORE_ERR("stack overflow\n");
514 return false;
515 }
516
517 return kgsl_cffdump_parse_ibs(dev_priv, NULL,
518 ibaddr, ibsize, check_only);
519 } else if (size_stack[i] != ibsize) {
520 KGSL_CORE_ERR("gpuaddr: 0x%08x, "
521 "wc: %u, with size wc: %u already on the "
522 "stack\n", ibaddr, ibsize, size_stack[i]);
523 return false;
524 }
525 }
526 break;
527 }
528
529 return true;
530}
531
532/*
533 * Traverse IBs and dump them to test vector. Detect swap by inspecting
534 * register writes, keeping note of the current state, and dump
535 * framebuffer config to test vector
536 */
537bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
538 const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
539 bool check_only)
540{
541 static uint level; /* recursion level */
542 bool ret = true;
543 uint host_size;
544 uint *hostaddr, *hoststart;
545 int dwords_left = sizedwords; /* dwords left in the current command
546 buffer */
547
548 if (level == 0)
549 kgsl_cffdump_addr_count = 0;
550
551 if (memdesc == NULL) {
552 struct kgsl_mem_entry *entry;
553 spin_lock(&dev_priv->process_priv->mem_lock);
554 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
555 gpuaddr, sizedwords * sizeof(uint));
556 spin_unlock(&dev_priv->process_priv->mem_lock);
557 if (entry == NULL) {
558 KGSL_CORE_ERR("did not find mapping "
559 "for gpuaddr: 0x%08x\n", gpuaddr);
560 return true;
561 }
562 memdesc = &entry->memdesc;
563 }
564
565 hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
566 if (hostaddr == NULL) {
567 KGSL_CORE_ERR("did not find mapping for "
568 "gpuaddr: 0x%08x\n", gpuaddr);
569 return true;
570 }
571
572 hoststart = hostaddr;
573
574 level++;
575
576 if (!memdesc->physaddr) {
577 KGSL_CORE_ERR("no physaddr");
578 return true;
579 } else {
580 mb();
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600581 kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
582 KGSL_CACHE_OP_INV);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 }
584
585#ifdef DEBUG
586 pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
587 gpuaddr, sizedwords, hostaddr);
588#endif
589
590 while (dwords_left > 0) {
591 int count = 0; /* dword count including packet header */
592 bool cur_ret = true;
593
594 switch (*hostaddr >> 30) {
595 case 0x0: /* type-0 */
596 count = (*hostaddr >> 16)+2;
597 break;
598 case 0x1: /* type-1 */
599 count = 2;
600 break;
601 case 0x3: /* type-3 */
602 count = ((*hostaddr >> 16) & 0x3fff) + 2;
603 cur_ret = kgsl_cffdump_handle_type3(dev_priv,
604 hostaddr, check_only);
605 break;
606 default:
607 pr_warn("kgsl: cffdump: parse-ib: unexpected type: "
608 "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
609 *hostaddr >> 30, *hostaddr, hostaddr,
610 gpuaddr+4*(sizedwords-dwords_left));
611 cur_ret = false;
612 count = dwords_left;
613 break;
614 }
615
616#ifdef DEBUG
617 if (!cur_ret) {
618 pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x"
619 " @ 0x%p[gb:0x%08x], level:%d\n",
620 sizedwords-dwords_left, sizedwords, *hostaddr,
621 hostaddr, gpuaddr+4*(sizedwords-dwords_left),
622 level);
623
624 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
625 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
626 sizedwords*4, 0);
627 }
628#endif
629 ret = ret && cur_ret;
630
631 /* jump to next packet */
632 dwords_left -= count;
633 hostaddr += count;
634 cur_ret = dwords_left >= 0;
635
636#ifdef DEBUG
637 if (!cur_ret) {
638 pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, "
639 "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
640 count, sizedwords-(dwords_left+count),
641 sizedwords, *(hostaddr-count), hostaddr-count,
642 gpuaddr+4*(sizedwords-(dwords_left+count)),
643 level);
644
645 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
646 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
647 sizedwords*4, 0);
648 }
649#endif
650
651 ret = ret && cur_ret;
652 }
653
654 if (!ret)
655 pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, "
656 "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
657
658 if (!check_only) {
659#ifdef DEBUG
660 uint offset = gpuaddr - memdesc->gpuaddr;
661 pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, "
662 "physaddr:%08x, offset:%d, size:%d", hoststart,
663 gpuaddr, memdesc->physaddr + offset, offset,
664 sizedwords*4);
665#endif
666 kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4,
667 false);
668 }
669
670 level--;
671
672 return ret;
673}
674
675static int subbuf_start_handler(struct rchan_buf *buf,
676 void *subbuf, void *prev_subbuf, uint prev_padding)
677{
678 pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf"
679 "=%p, prev_padding=%08x)\n", subbuf, prev_subbuf, prev_padding);
680
681 if (relay_buf_full(buf)) {
682 if (!suspended) {
683 suspended = 1;
684 pr_warn("kgsl: cffdump: relay: cpu %d buffer full!!!\n",
685 smp_processor_id());
686 }
687 dropped++;
688 return 0;
689 } else if (suspended) {
690 suspended = 0;
691 pr_warn("kgsl: cffdump: relay: cpu %d buffer no longer full.\n",
692 smp_processor_id());
693 }
694
695 subbuf_start_reserve(buf, 0);
696 return 1;
697}
698
699static struct dentry *create_buf_file_handler(const char *filename,
700 struct dentry *parent, int mode, struct rchan_buf *buf,
701 int *is_global)
702{
703 return debugfs_create_file(filename, mode, parent, buf,
704 &relay_file_operations);
705}
706
707/*
708 * file_remove() default callback. Removes relay file in debugfs.
709 */
710static int remove_buf_file_handler(struct dentry *dentry)
711{
712 pr_info("kgsl: cffdump: %s()\n", __func__);
713 debugfs_remove(dentry);
714 return 0;
715}
716
717/*
718 * relay callbacks
719 */
720static struct rchan_callbacks relay_callbacks = {
721 .subbuf_start = subbuf_start_handler,
722 .create_buf_file = create_buf_file_handler,
723 .remove_buf_file = remove_buf_file_handler,
724};
725
726/**
727 * create_channel - creates channel /debug/klog/cpuXXX
728 *
729 * Creates channel along with associated produced/consumed control files
730 *
731 * Returns channel on success, NULL otherwise
732 */
733static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs)
734{
735 struct rchan *chan;
736
737 pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, "
738 "n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir);
739
740 chan = relay_open("cpu", dir, subbuf_size,
741 n_subbufs, &relay_callbacks, NULL);
742 if (!chan) {
743 KGSL_CORE_ERR("relay_open failed\n");
744 return NULL;
745 }
746
747 suspended = 0;
748 dropped = 0;
749
750 return chan;
751}
752
753/**
754 * destroy_channel - destroys channel /debug/kgsl/cff/cpuXXX
755 *
756 * Destroys channel along with associated produced/consumed control files
757 */
758static void destroy_channel(void)
759{
760 pr_info("kgsl: cffdump: relay: destroy_channel\n");
761 if (chan) {
762 relay_close(chan);
763 chan = NULL;
764 }
765}
766