drm/msm: add a5xx specific debugfs
authorRob Clark <robdclark@gmail.com>
Wed, 13 Dec 2017 20:12:56 +0000 (15:12 -0500)
committerRob Clark <robdclark@gmail.com>
Tue, 20 Feb 2018 15:41:20 +0000 (10:41 -0500)
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: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/adreno/a5xx_debugfs.c [new file with mode: 0644]
drivers/gpu/drm/msm/adreno/a5xx_gpu.c
drivers/gpu/drm/msm/adreno/a5xx_gpu.h
drivers/gpu/drm/msm/adreno/adreno_device.c
drivers/gpu/drm/msm/msm_debugfs.c
drivers/gpu/drm/msm/msm_gpu.h

index 92b3844..ebe0c3d 100644 (file)
@@ -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
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_debugfs.c b/drivers/gpu/drm/msm/adreno/a5xx_debugfs.c
new file mode 100644 (file)
index 0000000..cef0978
--- /dev/null
@@ -0,0 +1,188 @@
+/* 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;
+}
index 7e09d44..579c28c 100644 (file)
@@ -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,
        },
index 6fb8c2f..7d71860 100644 (file)
@@ -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.
index 62bdb73..6263cb9 100644 (file)
@@ -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;
 }
 
index 1855182..ba74cb4 100644 (file)
@@ -161,8 +161,11 @@ int msm_debugfs_init(struct drm_minor *minor)
                return ret;
        }
 
-       if (priv->kms->funcs->debugfs_init)
+       if (priv->kms->funcs->debugfs_init) {
                ret = priv->kms->funcs->debugfs_init(priv->kms, minor);
+               if (ret)
+                       return ret;
+       }
 
        return ret;
 }
index fccfccd..b824117 100644 (file)
@@ -65,6 +65,8 @@ struct msm_gpu_funcs {
 #ifdef CONFIG_DEBUG_FS
        /* show GPU status in debugfs: */
        void (*show)(struct msm_gpu *gpu, struct seq_file *m);
+       /* for generation specific debugfs: */
+       int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);
 #endif
        int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value);
 };