| /* Copyright (c) 2012, Code Aurora Forum. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include "kgsl.h" |
| #include "kgsl_device.h" |
| #include "z180.h" |
| #include "z180_reg.h" |
| |
| #define Z180_STREAM_PACKET_CALL 0x7C000275 |
| |
| /* Postmortem Dump formatted Output parameters */ |
| |
| /* Number of Words per dump data line */ |
| #define WORDS_PER_LINE 8 |
| |
| /* Number of spaces per dump data line */ |
| #define NUM_SPACES (WORDS_PER_LINE - 1) |
| |
| /* |
| * Output dump data is formatted as string, hence number of chars |
| * per line for line string allocation |
| */ |
| #define CHARS_PER_LINE \ |
| ((WORDS_PER_LINE * (2*sizeof(unsigned int))) + NUM_SPACES + 1) |
| |
| /* Z180 registers (byte offsets) to be dumped */ |
| static const unsigned int regs_to_dump[] = { |
| ADDR_VGC_VERSION, |
| ADDR_VGC_SYSSTATUS, |
| ADDR_VGC_IRQSTATUS, |
| ADDR_VGC_IRQENABLE, |
| ADDR_VGC_IRQ_ACTIVE_CNT, |
| ADDR_VGC_CLOCKEN, |
| ADDR_VGC_MH_DATA_ADDR, |
| ADDR_VGC_GPR0, |
| ADDR_VGC_GPR1, |
| ADDR_VGC_BUSYCNT, |
| ADDR_VGC_FIFOFREE, |
| }; |
| |
| /** |
| * z180_dump_regs - Dumps all of Z180 external registers. Prints the word offset |
| * of the register in each output line. |
| * @device: kgsl_device pointer to the Z180 core |
| */ |
| static void z180_dump_regs(struct kgsl_device *device) |
| { |
| unsigned int i; |
| unsigned int reg_val; |
| |
| KGSL_LOG_DUMP(device, "Z180 Register Dump\n"); |
| for (i = 0; i < ARRAY_SIZE(regs_to_dump); i++) { |
| kgsl_regread(device, |
| regs_to_dump[i]/sizeof(unsigned int), ®_val); |
| KGSL_LOG_DUMP(device, "REG: %04X: %08X\n", |
| regs_to_dump[i]/sizeof(unsigned int), reg_val); |
| } |
| } |
| |
| /** |
| * z180_dump_ringbuffer - Dumps the Z180 core's ringbuffer contents |
| * @device: kgsl_device pointer to the z180 core |
| */ |
| static void z180_dump_ringbuffer(struct kgsl_device *device) |
| { |
| unsigned int rb_size; |
| unsigned int *rb_hostptr; |
| unsigned int rb_words; |
| unsigned int rb_gpuaddr; |
| struct z180_device *z180_dev = Z180_DEVICE(device); |
| unsigned int i; |
| char linebuf[CHARS_PER_LINE]; |
| |
| KGSL_LOG_DUMP(device, "Z180 ringbuffer dump\n"); |
| |
| rb_hostptr = (unsigned int *) z180_dev->ringbuffer.cmdbufdesc.hostptr; |
| |
| rb_size = Z180_RB_SIZE; |
| rb_gpuaddr = z180_dev->ringbuffer.cmdbufdesc.gpuaddr; |
| |
| rb_words = rb_size/sizeof(unsigned int); |
| |
| KGSL_LOG_DUMP(device, "ringbuffer size: %u\n", rb_size); |
| |
| KGSL_LOG_DUMP(device, "rb_words: %d\n", rb_words); |
| |
| for (i = 0; i < rb_words; i += WORDS_PER_LINE) { |
| hex_dump_to_buffer(rb_hostptr+i, |
| rb_size - i*sizeof(unsigned int), |
| WORDS_PER_LINE*sizeof(unsigned int), |
| sizeof(unsigned int), linebuf, |
| sizeof(linebuf), false); |
| KGSL_LOG_DUMP(device, "RB: %04X: %s\n", |
| rb_gpuaddr + i*sizeof(unsigned int), linebuf); |
| } |
| } |
| |
| |
| static void z180_dump_ib(struct kgsl_device *device) |
| { |
| unsigned int rb_size; |
| unsigned int *rb_hostptr; |
| unsigned int rb_words; |
| unsigned int rb_gpuaddr; |
| unsigned int ib_gpuptr = 0; |
| unsigned int ib_size = 0; |
| void *ib_hostptr = NULL; |
| int rb_slot_num = -1; |
| struct z180_device *z180_dev = Z180_DEVICE(device); |
| struct kgsl_mem_entry *entry = NULL; |
| unsigned int pt_base; |
| unsigned int i; |
| unsigned int j; |
| char linebuf[CHARS_PER_LINE]; |
| unsigned int current_ib_slot; |
| unsigned int len; |
| unsigned int rowsize; |
| KGSL_LOG_DUMP(device, "Z180 IB dump\n"); |
| |
| rb_hostptr = (unsigned int *) z180_dev->ringbuffer.cmdbufdesc.hostptr; |
| |
| rb_size = Z180_RB_SIZE; |
| rb_gpuaddr = z180_dev->ringbuffer.cmdbufdesc.gpuaddr; |
| |
| rb_words = rb_size/sizeof(unsigned int); |
| |
| KGSL_LOG_DUMP(device, "Ringbuffer size (bytes): %u\n", rb_size); |
| |
| KGSL_LOG_DUMP(device, "rb_words: %d\n", rb_words); |
| |
| pt_base = kgsl_mmu_get_current_ptbase(&device->mmu); |
| |
| /* Dump the current IB */ |
| for (i = 0; i < rb_words; i++) { |
| if (rb_hostptr[i] == Z180_STREAM_PACKET_CALL) { |
| |
| rb_slot_num++; |
| current_ib_slot = |
| z180_dev->current_timestamp % Z180_PACKET_COUNT; |
| if (rb_slot_num != current_ib_slot) |
| continue; |
| |
| ib_gpuptr = rb_hostptr[i+1]; |
| |
| entry = kgsl_get_mem_entry(device, pt_base, ib_gpuptr, |
| 1); |
| |
| if (entry == NULL) { |
| KGSL_LOG_DUMP(device, |
| "IB mem entry not found for ringbuffer slot#: %d\n", |
| rb_slot_num); |
| continue; |
| } |
| |
| ib_hostptr = kgsl_memdesc_map(&entry->memdesc); |
| |
| if (ib_hostptr == NULL) { |
| KGSL_LOG_DUMP(device, |
| "Could not map IB to kernel memory, Ringbuffer Slot: %d\n", |
| rb_slot_num); |
| continue; |
| } |
| |
| ib_size = entry->memdesc.size; |
| KGSL_LOG_DUMP(device, |
| "IB size: %dbytes, IB size in words: %d\n", |
| ib_size, |
| ib_size/sizeof(unsigned int)); |
| |
| for (j = 0; j < ib_size; j += WORDS_PER_LINE) { |
| len = ib_size - j*sizeof(unsigned int); |
| rowsize = WORDS_PER_LINE*sizeof(unsigned int); |
| hex_dump_to_buffer(ib_hostptr+j, len, rowsize, |
| sizeof(unsigned int), linebuf, |
| sizeof(linebuf), false); |
| KGSL_LOG_DUMP(device, "IB%d: %04X: %s\n", |
| rb_slot_num, |
| (rb_gpuaddr + |
| j*sizeof(unsigned int)), |
| linebuf); |
| } |
| KGSL_LOG_DUMP(device, "IB Dump Finished\n"); |
| } |
| } |
| } |
| |
| |
| /** |
| * z180_dump - Dumps the Z180 ringbuffer and registers (and IBs if asked for) |
| * for postmortem |
| * analysis. |
| * @device: kgsl_device pointer to the Z180 core |
| */ |
| int z180_dump(struct kgsl_device *device, int manual) |
| { |
| struct z180_device *z180_dev = Z180_DEVICE(device); |
| |
| mb(); |
| |
| KGSL_LOG_DUMP(device, "Retired Timestamp: %d\n", z180_dev->timestamp); |
| KGSL_LOG_DUMP(device, |
| "Current Timestamp: %d\n", z180_dev->current_timestamp); |
| |
| /* Dump ringbuffer */ |
| z180_dump_ringbuffer(device); |
| |
| /* Dump registers */ |
| z180_dump_regs(device); |
| |
| /* Dump IBs, if asked for */ |
| if (device->pm_ib_enabled) |
| z180_dump_ib(device); |
| |
| /* Get the stack trace if the dump was automatic */ |
| if (!manual) |
| BUG_ON(1); |
| |
| return 0; |
| } |
| |