162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/debugfs.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
962306a36Sopenharmony_ci#include <drm/drm_file.h>
1062306a36Sopenharmony_ci#include <drm/drm_print.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "a5xx_gpu.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void pfp_print(struct msm_gpu *gpu, struct drm_printer *p)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	int i;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	drm_printf(p, "PFP state:\n");
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	for (i = 0; i < 36; i++) {
2162306a36Sopenharmony_ci		gpu_write(gpu, REG_A5XX_CP_PFP_STAT_ADDR, i);
2262306a36Sopenharmony_ci		drm_printf(p, "  %02x: %08x\n", i,
2362306a36Sopenharmony_ci			gpu_read(gpu, REG_A5XX_CP_PFP_STAT_DATA));
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void me_print(struct msm_gpu *gpu, struct drm_printer *p)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	int i;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	drm_printf(p, "ME state:\n");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	for (i = 0; i < 29; i++) {
3462306a36Sopenharmony_ci		gpu_write(gpu, REG_A5XX_CP_ME_STAT_ADDR, i);
3562306a36Sopenharmony_ci		drm_printf(p, "  %02x: %08x\n", i,
3662306a36Sopenharmony_ci			gpu_read(gpu, REG_A5XX_CP_ME_STAT_DATA));
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void meq_print(struct msm_gpu *gpu, struct drm_printer *p)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int i;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	drm_printf(p, "MEQ state:\n");
4562306a36Sopenharmony_ci	gpu_write(gpu, REG_A5XX_CP_MEQ_DBG_ADDR, 0);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
4862306a36Sopenharmony_ci		drm_printf(p, "  %02x: %08x\n", i,
4962306a36Sopenharmony_ci			gpu_read(gpu, REG_A5XX_CP_MEQ_DBG_DATA));
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void roq_print(struct msm_gpu *gpu, struct drm_printer *p)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	int i;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	drm_printf(p, "ROQ state:\n");
5862306a36Sopenharmony_ci	gpu_write(gpu, REG_A5XX_CP_ROQ_DBG_ADDR, 0);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = 0; i < 512 / 4; i++) {
6162306a36Sopenharmony_ci		uint32_t val[4];
6262306a36Sopenharmony_ci		int j;
6362306a36Sopenharmony_ci		for (j = 0; j < 4; j++)
6462306a36Sopenharmony_ci			val[j] = gpu_read(gpu, REG_A5XX_CP_ROQ_DBG_DATA);
6562306a36Sopenharmony_ci		drm_printf(p, "  %02x: %08x %08x %08x %08x\n", i,
6662306a36Sopenharmony_ci			val[0], val[1], val[2], val[3]);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int show(struct seq_file *m, void *arg)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct drm_info_node *node = m->private;
7362306a36Sopenharmony_ci	struct drm_device *dev = node->minor->dev;
7462306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
7562306a36Sopenharmony_ci	struct drm_printer p = drm_seq_file_printer(m);
7662306a36Sopenharmony_ci	void (*show)(struct msm_gpu *gpu, struct drm_printer *p) =
7762306a36Sopenharmony_ci		node->info_ent->data;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	show(priv->gpu, &p);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define ENT(n) { .name = #n, .show = show, .data = n ##_print }
8462306a36Sopenharmony_cistatic struct drm_info_list a5xx_debugfs_list[] = {
8562306a36Sopenharmony_ci	ENT(pfp),
8662306a36Sopenharmony_ci	ENT(me),
8762306a36Sopenharmony_ci	ENT(meq),
8862306a36Sopenharmony_ci	ENT(roq),
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* for debugfs files that can be written to, we can't use drm helper: */
9262306a36Sopenharmony_cistatic int
9362306a36Sopenharmony_cireset_set(void *data, u64 val)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct drm_device *dev = data;
9662306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
9762306a36Sopenharmony_ci	struct msm_gpu *gpu = priv->gpu;
9862306a36Sopenharmony_ci	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
9962306a36Sopenharmony_ci	struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
10262306a36Sopenharmony_ci		return -EINVAL;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* TODO do we care about trying to make sure the GPU is idle?
10562306a36Sopenharmony_ci	 * Since this is just a debug feature limited to CAP_SYS_ADMIN,
10662306a36Sopenharmony_ci	 * maybe it is fine to let the user keep both pieces if they
10762306a36Sopenharmony_ci	 * try to reset an active GPU.
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	mutex_lock(&gpu->lock);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	release_firmware(adreno_gpu->fw[ADRENO_FW_PM4]);
11362306a36Sopenharmony_ci	adreno_gpu->fw[ADRENO_FW_PM4] = NULL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	release_firmware(adreno_gpu->fw[ADRENO_FW_PFP]);
11662306a36Sopenharmony_ci	adreno_gpu->fw[ADRENO_FW_PFP] = NULL;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (a5xx_gpu->pm4_bo) {
11962306a36Sopenharmony_ci		msm_gem_unpin_iova(a5xx_gpu->pm4_bo, gpu->aspace);
12062306a36Sopenharmony_ci		drm_gem_object_put(a5xx_gpu->pm4_bo);
12162306a36Sopenharmony_ci		a5xx_gpu->pm4_bo = NULL;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (a5xx_gpu->pfp_bo) {
12562306a36Sopenharmony_ci		msm_gem_unpin_iova(a5xx_gpu->pfp_bo, gpu->aspace);
12662306a36Sopenharmony_ci		drm_gem_object_put(a5xx_gpu->pfp_bo);
12762306a36Sopenharmony_ci		a5xx_gpu->pfp_bo = NULL;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	gpu->needs_hw_init = true;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	pm_runtime_get_sync(&gpu->pdev->dev);
13362306a36Sopenharmony_ci	gpu->funcs->recover(gpu);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	pm_runtime_put_sync(&gpu->pdev->dev);
13662306a36Sopenharmony_ci	mutex_unlock(&gpu->lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n");
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_civoid a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct drm_device *dev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!minor)
14962306a36Sopenharmony_ci		return;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dev = minor->dev;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	drm_debugfs_create_files(a5xx_debugfs_list,
15462306a36Sopenharmony_ci				 ARRAY_SIZE(a5xx_debugfs_list),
15562306a36Sopenharmony_ci				 minor->debugfs_root, minor);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	debugfs_create_file_unsafe("reset", S_IWUGO, minor->debugfs_root, dev,
15862306a36Sopenharmony_ci				&reset_fops);
15962306a36Sopenharmony_ci}
160