/* Copyright (c) 2012, The Linux Foundation. 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; phys_addr_t 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); kgsl_mem_entry_put(entry); 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"); kgsl_mem_entry_put(entry); } } } /** * 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; }