Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 331dc0bc authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm: add a5xx specific debugfs



Add some debugfs to dump out PFP and ME microcontroller state, as well
as some of the queues (MEQ and ROQ).  Also add a debugfs file to trigger
a GPU reset (and reloading the firmware on next submit).

Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 3f0689e6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ msm-y := \
	msm_ringbuffer.o \
	msm_submitqueue.o

msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o

msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
+188 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017 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/types.h>
#include <linux/debugfs.h>
#include <drm/drm_print.h>

#include "a5xx_gpu.h"

static int pfp_print(struct msm_gpu *gpu, struct drm_printer *p)
{
	int i;

	drm_printf(p, "PFP state:\n");

	for (i = 0; i < 36; i++) {
		gpu_write(gpu, REG_A5XX_CP_PFP_STAT_ADDR, i);
		drm_printf(p, "  %02x: %08x\n", i,
			gpu_read(gpu, REG_A5XX_CP_PFP_STAT_DATA));
	}

	return 0;
}

static int me_print(struct msm_gpu *gpu, struct drm_printer *p)
{
	int i;

	drm_printf(p, "ME state:\n");

	for (i = 0; i < 29; i++) {
		gpu_write(gpu, REG_A5XX_CP_ME_STAT_ADDR, i);
		drm_printf(p, "  %02x: %08x\n", i,
			gpu_read(gpu, REG_A5XX_CP_ME_STAT_DATA));
	}

	return 0;
}

static int meq_print(struct msm_gpu *gpu, struct drm_printer *p)
{
	int i;

	drm_printf(p, "MEQ state:\n");
	gpu_write(gpu, REG_A5XX_CP_MEQ_DBG_ADDR, 0);

	for (i = 0; i < 64; i++) {
		drm_printf(p, "  %02x: %08x\n", i,
			gpu_read(gpu, REG_A5XX_CP_MEQ_DBG_DATA));
	}

	return 0;
}

static int roq_print(struct msm_gpu *gpu, struct drm_printer *p)
{
	int i;

	drm_printf(p, "ROQ state:\n");
	gpu_write(gpu, REG_A5XX_CP_ROQ_DBG_ADDR, 0);

	for (i = 0; i < 512 / 4; i++) {
		uint32_t val[4];
		int j;
		for (j = 0; j < 4; j++)
			val[j] = gpu_read(gpu, REG_A5XX_CP_ROQ_DBG_DATA);
		drm_printf(p, "  %02x: %08x %08x %08x %08x\n", i,
			val[0], val[1], val[2], val[3]);
	}

	return 0;
}

static int show(struct seq_file *m, void *arg)
{
	struct drm_info_node *node = (struct drm_info_node *) m->private;
	struct drm_device *dev = node->minor->dev;
	struct msm_drm_private *priv = dev->dev_private;
	struct drm_printer p = drm_seq_file_printer(m);
	int (*show)(struct msm_gpu *gpu, struct drm_printer *p) =
		node->info_ent->data;

	return show(priv->gpu, &p);
}

#define ENT(n) { .name = #n, .show = show, .data = n ##_print }
static struct drm_info_list a5xx_debugfs_list[] = {
	ENT(pfp),
	ENT(me),
	ENT(meq),
	ENT(roq),
};

/* for debugfs files that can be written to, we can't use drm helper: */
static int
reset_set(void *data, u64 val)
{
	struct drm_device *dev = data;
	struct msm_drm_private *priv = dev->dev_private;
	struct msm_gpu *gpu = priv->gpu;
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
	struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);

	if (!capable(CAP_SYS_ADMIN))
		return -EINVAL;

	/* TODO do we care about trying to make sure the GPU is idle?
	 * Since this is just a debug feature limited to CAP_SYS_ADMIN,
	 * maybe it is fine to let the user keep both pieces if they
	 * try to reset an active GPU.
	 */

	mutex_lock(&dev->struct_mutex);

	if (adreno_gpu->pm4) {
		release_firmware(adreno_gpu->pm4);
		adreno_gpu->pm4 = NULL;
	}

	if (adreno_gpu->pfp) {
		release_firmware(adreno_gpu->pfp);
		adreno_gpu->pfp = NULL;
	}
	if (a5xx_gpu->pm4_bo) {
		if (a5xx_gpu->pm4_iova)
			msm_gem_put_iova(a5xx_gpu->pm4_bo, gpu->aspace);
		drm_gem_object_unreference(a5xx_gpu->pm4_bo);
		a5xx_gpu->pm4_bo = NULL;
	}

	if (a5xx_gpu->pfp_bo) {
		if (a5xx_gpu->pfp_iova)
			msm_gem_put_iova(a5xx_gpu->pfp_bo, gpu->aspace);
		drm_gem_object_unreference(a5xx_gpu->pfp_bo);
		a5xx_gpu->pfp_bo = NULL;
	}

	gpu->needs_hw_init = true;

	pm_runtime_get_sync(&gpu->pdev->dev);
	gpu->funcs->recover(gpu);

	pm_runtime_put_sync(&gpu->pdev->dev);
	mutex_unlock(&dev->struct_mutex);

	return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n");


int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
{
	struct drm_device *dev = minor->dev;
	struct dentry *ent;
	int ret;

	if (!minor)
		return 0;

	ret = drm_debugfs_create_files(a5xx_debugfs_list,
			ARRAY_SIZE(a5xx_debugfs_list),
			minor->debugfs_root, minor);

	if (ret) {
		dev_err(dev->dev, "could not install a5xx_debugfs_list\n");
		return ret;
	}

	ent = debugfs_create_file("reset", S_IWUGO,
		minor->debugfs_root,
		dev, &reset_fops);
	if (!ent)
		return -ENOMEM;

	return 0;
}
+1 −0
Original line number Diff line number Diff line
@@ -1195,6 +1195,7 @@ static const struct adreno_gpu_funcs funcs = {
		.destroy = a5xx_destroy,
#ifdef CONFIG_DEBUG_FS
		.show = a5xx_show,
		.debugfs_init = a5xx_debugfs_init,
#endif
		.gpu_busy = a5xx_gpu_busy,
	},
+4 −0
Original line number Diff line number Diff line
@@ -49,6 +49,10 @@ struct a5xx_gpu {

#define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base)

#ifdef CONFIG_DEBUG_FS
int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor);
#endif

/*
 * In order to do lockless preemption we use a simple state machine to progress
 * through the process.
+6 −0
Original line number Diff line number Diff line
@@ -150,6 +150,12 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
		return NULL;
	}

	if (gpu->funcs->debugfs_init) {
		gpu->funcs->debugfs_init(gpu, dev->primary);
		gpu->funcs->debugfs_init(gpu, dev->render);
		gpu->funcs->debugfs_init(gpu, dev->control);
	}

	return gpu;
}

Loading