blob: 58e3126e5f3a9c17e132d5b93698cb3ca0083b28 [file] [log] [blame]
/* Copyright (c) 2012-2013, 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 <linux/io.h>
#include "kgsl.h"
#include "adreno.h"
#include "kgsl_snapshot.h"
#include "a3xx_reg.h"
#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ sizeof(struct kgsl_snapshot_debug))
/* Shader memory size in words */
#define SHADER_MEMORY_SIZE 0x4000
/**
* _rbbm_debug_bus_read - Helper function to read data from the RBBM
* debug bus.
* @device - GPU device to read/write registers
* @block_id - Debug bus block to read from
* @index - Index in the debug bus block to read
* @ret - Value of the register read
*/
static void _rbbm_debug_bus_read(struct kgsl_device *device,
unsigned int block_id, unsigned int index, unsigned int *val)
{
unsigned int block = (block_id << 8) | 1 << 16;
adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, block | index);
adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, val);
}
/**
* a3xx_snapshot_shader_memory - Helper function to dump the GPU shader
* memory to the snapshot buffer.
* @device - GPU device whose shader memory is to be dumped
* @snapshot - Pointer to binary snapshot data blob being made
* @remain - Number of remaining bytes in the snapshot blob
* @priv - Unused parameter
*/
static int a3xx_snapshot_shader_memory(struct kgsl_device *device,
void *snapshot, int remain, void *priv)
{
struct kgsl_snapshot_debug *header = snapshot;
unsigned int i;
unsigned int *data = snapshot + sizeof(*header);
unsigned int shader_read_len = SHADER_MEMORY_SIZE;
if (SHADER_MEMORY_SIZE > (device->shader_mem_len >> 2))
shader_read_len = (device->shader_mem_len >> 2);
if (remain < DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE)) {
SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY");
return 0;
}
header->type = SNAPSHOT_DEBUG_SHADER_MEMORY;
header->size = SHADER_MEMORY_SIZE;
/* Map shader memory to kernel, for dumping */
if (device->shader_mem_virt == NULL)
device->shader_mem_virt = devm_ioremap(device->dev,
device->shader_mem_phys,
device->shader_mem_len);
if (device->shader_mem_virt == NULL) {
KGSL_DRV_ERR(device,
"Unable to map shader memory region\n");
return 0;
}
/* Now, dump shader memory to snapshot */
for (i = 0; i < shader_read_len; i++)
adreno_shadermem_regread(device, i, &data[i]);
return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE);
}
#define VPC_MEMORY_BANKS 4
#define VPC_MEMORY_SIZE 512
static int a3xx_snapshot_vpc_memory(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int size = VPC_MEMORY_BANKS * VPC_MEMORY_SIZE;
int bank, addr, i = 0;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "VPC MEMORY");
return 0;
}
header->type = SNAPSHOT_DEBUG_VPC_MEMORY;
header->size = size;
for (bank = 0; bank < VPC_MEMORY_BANKS; bank++) {
for (addr = 0; addr < VPC_MEMORY_SIZE; addr++) {
unsigned int val = bank | (addr << 4);
adreno_regwrite(device,
A3XX_VPC_VPC_DEBUG_RAM_SEL, val);
adreno_regread(device,
A3XX_VPC_VPC_DEBUG_RAM_READ, &data[i++]);
}
}
return DEBUG_SECTION_SZ(size);
}
#define CP_MEQ_SIZE 16
static int a3xx_snapshot_cp_meq(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int i;
if (remain < DEBUG_SECTION_SZ(CP_MEQ_SIZE)) {
SNAPSHOT_ERR_NOMEM(device, "CP MEQ DEBUG");
return 0;
}
header->type = SNAPSHOT_DEBUG_CP_MEQ;
header->size = CP_MEQ_SIZE;
adreno_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0);
for (i = 0; i < CP_MEQ_SIZE; i++)
adreno_regread(device, A3XX_CP_MEQ_DATA, &data[i]);
return DEBUG_SECTION_SZ(CP_MEQ_SIZE);
}
static int a3xx_snapshot_cp_pm4_ram(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int i, size = adreno_dev->pm4_fw_size - 1;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG");
return 0;
}
header->type = SNAPSHOT_DEBUG_CP_PM4_RAM;
header->size = size;
/*
* Read the firmware from the GPU rather than use our cache in order to
* try to catch mis-programming or corruption in the hardware. We do
* use the cached version of the size, however, instead of trying to
* maintain always changing hardcoded constants
*/
adreno_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0);
for (i = 0; i < size; i++)
adreno_regread(device, REG_CP_ME_RAM_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
static int a3xx_snapshot_cp_pfp_ram(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int i, size = adreno_dev->pfp_fw_size - 1;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG");
return 0;
}
header->type = SNAPSHOT_DEBUG_CP_PFP_RAM;
header->size = size;
/*
* Read the firmware from the GPU rather than use our cache in order to
* try to catch mis-programming or corruption in the hardware. We do
* use the cached version of the size, however, instead of trying to
* maintain always changing hardcoded constants
*/
kgsl_regwrite(device, A3XX_CP_PFP_UCODE_ADDR, 0x0);
for (i = 0; i < size; i++)
adreno_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
/* This is the ROQ buffer size on both the A305 and A320 */
#define A320_CP_ROQ_SIZE 128
/* This is the ROQ buffer size on the A330 */
#define A330_CP_ROQ_SIZE 512
static int a3xx_snapshot_cp_roq(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int i, size;
/* The size of the ROQ buffer is core dependent */
size = (adreno_is_a330(adreno_dev) ||
adreno_is_a305b(adreno_dev)) ?
A330_CP_ROQ_SIZE : A320_CP_ROQ_SIZE;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP ROQ DEBUG");
return 0;
}
header->type = SNAPSHOT_DEBUG_CP_ROQ;
header->size = size;
adreno_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0);
for (i = 0; i < size; i++)
adreno_regread(device, A3XX_CP_ROQ_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
#define A330_CP_MERCIU_QUEUE_SIZE 32
static int a330_snapshot_cp_merciu(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
{
struct kgsl_snapshot_debug *header = snapshot;
unsigned int *data = snapshot + sizeof(*header);
int i, size;
/* The MERCIU data is two dwords per entry */
size = A330_CP_MERCIU_QUEUE_SIZE << 1;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP MERCIU DEBUG");
return 0;
}
header->type = SNAPSHOT_DEBUG_CP_MERCIU;
header->size = size;
adreno_regwrite(device, A3XX_CP_MERCIU_ADDR, 0x0);
for (i = 0; i < A330_CP_MERCIU_QUEUE_SIZE; i++) {
adreno_regread(device, A3XX_CP_MERCIU_DATA,
&data[(i * 2)]);
adreno_regread(device, A3XX_CP_MERCIU_DATA2,
&data[(i * 2) + 1]);
}
return DEBUG_SECTION_SZ(size);
}
struct debugbus_block {
unsigned int block_id;
unsigned int dwords;
};
static int a3xx_snapshot_debugbus_block(struct kgsl_device *device,
void *snapshot, int remain, void *priv)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debugbus *header = snapshot;
struct debugbus_block *block = priv;
int i;
unsigned int *data = snapshot + sizeof(*header);
unsigned int dwords;
int size;
/*
* For A305 and A320 all debug bus regions are the same size (0x40). For
* A330, they can be different sizes - most are still 0x40, but some
* like CP are larger
*/
dwords = (adreno_is_a330(adreno_dev) ||
adreno_is_a305b(adreno_dev)) ?
block->dwords : 0x40;
size = (dwords * sizeof(unsigned int)) + sizeof(*header);
if (remain < size) {
SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS");
return 0;
}
header->id = block->block_id;
header->count = dwords;
for (i = 0; i < dwords; i++)
_rbbm_debug_bus_read(device, block->block_id, i, &data[i]);
return size;
}
static struct debugbus_block debugbus_blocks[] = {
{ RBBM_BLOCK_ID_CP, 0x52, },
{ RBBM_BLOCK_ID_RBBM, 0x40, },
{ RBBM_BLOCK_ID_VBIF, 0x40, },
{ RBBM_BLOCK_ID_HLSQ, 0x40, },
{ RBBM_BLOCK_ID_UCHE, 0x40, },
{ RBBM_BLOCK_ID_PC, 0x40, },
{ RBBM_BLOCK_ID_VFD, 0x40, },
{ RBBM_BLOCK_ID_VPC, 0x40, },
{ RBBM_BLOCK_ID_TSE, 0x40, },
{ RBBM_BLOCK_ID_RAS, 0x40, },
{ RBBM_BLOCK_ID_VSC, 0x40, },
{ RBBM_BLOCK_ID_SP_0, 0x40, },
{ RBBM_BLOCK_ID_SP_1, 0x40, },
{ RBBM_BLOCK_ID_SP_2, 0x40, },
{ RBBM_BLOCK_ID_SP_3, 0x40, },
{ RBBM_BLOCK_ID_TPL1_0, 0x40, },
{ RBBM_BLOCK_ID_TPL1_1, 0x40, },
{ RBBM_BLOCK_ID_TPL1_2, 0x40, },
{ RBBM_BLOCK_ID_TPL1_3, 0x40, },
{ RBBM_BLOCK_ID_RB_0, 0x40, },
{ RBBM_BLOCK_ID_RB_1, 0x40, },
{ RBBM_BLOCK_ID_RB_2, 0x40, },
{ RBBM_BLOCK_ID_RB_3, 0x40, },
{ RBBM_BLOCK_ID_MARB_0, 0x40, },
{ RBBM_BLOCK_ID_MARB_1, 0x40, },
{ RBBM_BLOCK_ID_MARB_2, 0x40, },
{ RBBM_BLOCK_ID_MARB_3, 0x40, },
};
static void *a3xx_snapshot_debugbus(struct kgsl_device *device,
void *snapshot, int *remain)
{
int i;
for (i = 0; i < ARRAY_SIZE(debugbus_blocks); i++) {
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain,
a3xx_snapshot_debugbus_block,
(void *) &debugbus_blocks[i]);
}
return snapshot;
}
static void _snapshot_a3xx_regs(struct kgsl_snapshot_registers *regs,
struct kgsl_snapshot_registers_list *list)
{
regs[list->count].regs = (unsigned int *) a3xx_registers;
regs[list->count].count = a3xx_registers_count;
list->count++;
}
static void _snapshot_hlsq_regs(struct kgsl_snapshot_registers *regs,
struct kgsl_snapshot_registers_list *list,
struct adreno_device *adreno_dev)
{
struct kgsl_device *device = &adreno_dev->dev;
/*
* Trying to read HLSQ registers when the HLSQ block is busy
* will cause the device to hang. The RBBM_DEBUG_BUS has information
* that will tell us if the HLSQ block is busy or not. Read values
* from the debug bus to ensure the HLSQ block is not busy (this
* is hardware dependent). If the HLSQ block is busy do not
* dump the registers, otherwise dump the HLSQ registers.
*/
if (adreno_is_a330(adreno_dev)) {
/*
* stall_ctxt_full status bit: RBBM_BLOCK_ID_HLSQ index 49 [27]
*
* if (!stall_context_full)
* then dump HLSQ registers
*/
unsigned int stall_context_full = 0;
_rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 49,
&stall_context_full);
stall_context_full &= 0x08000000;
if (stall_context_full)
return;
} else {
/*
* tpif status bits: RBBM_BLOCK_ID_HLSQ index 4 [4:0]
* spif status bits: RBBM_BLOCK_ID_HLSQ index 7 [5:0]
*
* if ((tpif == 0, 1, 28) && (spif == 0, 1, 10))
* then dump HLSQ registers
*/
unsigned int next_pif = 0;
/* check tpif */
_rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 4, &next_pif);
next_pif &= 0x1f;
if (next_pif != 0 && next_pif != 1 && next_pif != 28)
return;
/* check spif */
_rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 7, &next_pif);
next_pif &= 0x3f;
if (next_pif != 0 && next_pif != 1 && next_pif != 10)
return;
}
regs[list->count].regs = (unsigned int *) a3xx_hlsq_registers;
regs[list->count].count = a3xx_hlsq_registers_count;
list->count++;
}
static void _snapshot_a330_regs(struct kgsl_snapshot_registers *regs,
struct kgsl_snapshot_registers_list *list)
{
/* For A330, append the additional list of new registers to grab */
regs[list->count].regs = (unsigned int *) a330_registers;
regs[list->count].count = a330_registers_count;
list->count++;
}
/* A3XX GPU snapshot function - this is where all of the A3XX specific
* bits and pieces are grabbed into the snapshot memory
*/
void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
int *remain, int hang)
{
struct kgsl_device *device = &adreno_dev->dev;
struct kgsl_snapshot_registers_list list;
struct kgsl_snapshot_registers regs[5];
int size;
list.registers = regs;
list.count = 0;
/* Disable Clock gating temporarily for the debug bus to work */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, 0x00);
/* Store relevant registers in list to snapshot */
_snapshot_a3xx_regs(regs, &list);
_snapshot_hlsq_regs(regs, &list, adreno_dev);
if (adreno_is_a330(adreno_dev) || adreno_is_a305b(adreno_dev))
_snapshot_a330_regs(regs, &list);
/* Master set of (non debug) registers */
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
kgsl_snapshot_dump_regs, &list);
/*
* CP_STATE_DEBUG indexed registers - 20 on 305 and 320 and 46 on A330
*/
size = (adreno_is_a330(adreno_dev) ||
adreno_is_a305b(adreno_dev)) ? 0x2E : 0x14;
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
remain, REG_CP_STATE_DEBUG_INDEX,
REG_CP_STATE_DEBUG_DATA, 0x0, size);
/* CP_ME indexed registers */
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
64, 44);
/* VPC memory */
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_vpc_memory, NULL);
/* CP MEQ */
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_meq, NULL);
/* Shader working/shadow memory */
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_shader_memory, NULL);
/* CP PFP and PM4 */
/* Reading these will hang the GPU if it isn't already hung */
if (hang) {
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_pfp_ram, NULL);
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_pm4_ram, NULL);
}
/* CP ROQ */
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_roq, NULL);
if (adreno_is_a330(adreno_dev) ||
adreno_is_a305b(adreno_dev)) {
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a330_snapshot_cp_merciu, NULL);
}
snapshot = a3xx_snapshot_debugbus(device, snapshot, remain);
/* Enable Clock gating */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
return snapshot;
}