// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 The Linux Foundation. All rights reserved. */
#include <linux/ascii85.h>
#include "msm_gem.h"
#include "a6xx_gpu.h"
#include "a6xx_gmu.h"
#include "a6xx_gpu_state.h"
#include "a6xx_gmu.xml.h"
struct a6xx_gpu_state_obj {
const void *handle;
u32 *data;
};
struct a6xx_gpu_state {
struct msm_gpu_state base;
struct a6xx_gpu_state_obj *gmu_registers;
int nr_gmu_registers;
struct a6xx_gpu_state_obj *registers;
int nr_registers;
struct a6xx_gpu_state_obj *shaders;
int nr_shaders;
struct a6xx_gpu_state_obj *clusters;
int nr_clusters;
struct a6xx_gpu_state_obj *dbgahb_clusters;
int nr_dbgahb_clusters;
struct a6xx_gpu_state_obj *indexed_regs;
int nr_indexed_regs;
struct a6xx_gpu_state_obj *debugbus;
int nr_debugbus;
struct a6xx_gpu_state_obj *vbif_debugbus;
struct a6xx_gpu_state_obj *cx_debugbus;
int nr_cx_debugbus;
struct list_head objs;
};
static inline int CRASHDUMP_WRITE(u64 *in, u32 reg, u32 val)
{
in[0] = val;
in[1] = (((u64) reg) << 44 | (1 << 21) | 1);
return 2;
}
static inline int CRASHDUMP_READ(u64 *in, u32 reg, u32 dwords, u64 target)
{
in[0] = target;
in[1] = (((u64) reg) << 44 | dwords);
return 2;
}
static inline int CRASHDUMP_FINI(u64 *in)
{
in[0] = 0;
in[1] = 0;
return 2;
}
struct a6xx_crashdumper {
void *ptr;
struct drm_gem_object *bo;
u64 iova;
};
struct a6xx_state_memobj {
struct list_head node;
unsigned long long data[];
};
void *state_kcalloc(struct a6xx_gpu_state *a6xx_state, int nr, size_t objsize)
{
struct a6xx_state_memobj *obj =
kzalloc((nr * objsize) + sizeof(*obj), GFP_KERNEL);
if (!obj)
return NULL;
list_add_tail(&obj->node, &a6xx_state->objs);
return &obj->data;
}
void *state_kmemdup(struct a6xx_gpu_state *a6xx_state, void *src,
size_t size)
{
void *dst = state_kcalloc(a6xx_state, 1, size);
if (dst)
memcpy(dst, src, size);
return dst;
}
/*
* Allocate 1MB for the crashdumper scratch region - 8k for the script and
* the rest for the data
*/
#define A6XX_CD_DATA_OFFSET 8192
#define A6XX_CD_DATA_SIZE (SZ_1M - 8192)
static int a6xx_crashdumper_init(struct msm_gpu *gpu,
struct a6xx_crashdumper *dumper)
{
dumper->ptr = msm_gem_kernel_new_locked(gpu->dev,
SZ_1M, MSM_BO_UNCACHED, gpu->aspace,
&dumper->bo, &dumper->iova);
if (!IS_ERR(dumper->ptr))
msm_gem_object_set_name(dumper->bo, "crashdump");
return PTR_ERR_OR_ZERO(dumper->ptr);
}
static int a6xx_crashdumper_run(struct msm_gpu *gpu,
struct a6xx_crashdumper *dumper)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
u32 val;
int ret;
if (IS_ERR_OR_NULL(dumper->ptr))
return -EINVAL;
if (!a6xx_gmu_sptprac_is_on(&a6xx_gpu->gmu))
return -EINVAL;
/* Make sure all pending memory writes are posted */
wmb();
gpu_write64(gpu, REG_A6XX_CP_CRASH_SCRIPT_BASE_LO,
REG_A6XX_CP_CRASH_SCRIPT_BASE_HI, dumper->iova);
gpu_write(gpu, REG_A6XX_CP_CRASH_DUMP_CNTL, 1);
ret = gpu_poll_timeout(gpu, REG_A6XX_CP_CRASH_DUMP_STATUS, val,
val & 0x02, 100, 10000