Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Rob Clark <robdclark@gmail.com>

+207 -1
+2
drivers/gpu/drm/msm/Makefile
··· 62 62 msm_ringbuffer.o \ 63 63 msm_submitqueue.o 64 64 65 + msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o 66 + 65 67 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o 66 68 msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o 67 69 msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
+188
drivers/gpu/drm/msm/adreno/a5xx_debugfs.c
··· 1 + /* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. 2 + * 3 + * This program is free software; you can redistribute it and/or modify 4 + * it under the terms of the GNU General Public License version 2 and 5 + * only version 2 as published by the Free Software Foundation. 6 + * 7 + * This program is distributed in the hope that it will be useful, 8 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 + * GNU General Public License for more details. 11 + * 12 + */ 13 + 14 + 15 + #include <linux/types.h> 16 + #include <linux/debugfs.h> 17 + #include <drm/drm_print.h> 18 + 19 + #include "a5xx_gpu.h" 20 + 21 + static int pfp_print(struct msm_gpu *gpu, struct drm_printer *p) 22 + { 23 + int i; 24 + 25 + drm_printf(p, "PFP state:\n"); 26 + 27 + for (i = 0; i < 36; i++) { 28 + gpu_write(gpu, REG_A5XX_CP_PFP_STAT_ADDR, i); 29 + drm_printf(p, " %02x: %08x\n", i, 30 + gpu_read(gpu, REG_A5XX_CP_PFP_STAT_DATA)); 31 + } 32 + 33 + return 0; 34 + } 35 + 36 + static int me_print(struct msm_gpu *gpu, struct drm_printer *p) 37 + { 38 + int i; 39 + 40 + drm_printf(p, "ME state:\n"); 41 + 42 + for (i = 0; i < 29; i++) { 43 + gpu_write(gpu, REG_A5XX_CP_ME_STAT_ADDR, i); 44 + drm_printf(p, " %02x: %08x\n", i, 45 + gpu_read(gpu, REG_A5XX_CP_ME_STAT_DATA)); 46 + } 47 + 48 + return 0; 49 + } 50 + 51 + static int meq_print(struct msm_gpu *gpu, struct drm_printer *p) 52 + { 53 + int i; 54 + 55 + drm_printf(p, "MEQ state:\n"); 56 + gpu_write(gpu, REG_A5XX_CP_MEQ_DBG_ADDR, 0); 57 + 58 + for (i = 0; i < 64; i++) { 59 + drm_printf(p, " %02x: %08x\n", i, 60 + gpu_read(gpu, REG_A5XX_CP_MEQ_DBG_DATA)); 61 + } 62 + 63 + return 0; 64 + } 65 + 66 + static int roq_print(struct msm_gpu *gpu, struct drm_printer *p) 67 + { 68 + int i; 69 + 70 + drm_printf(p, "ROQ state:\n"); 71 + gpu_write(gpu, REG_A5XX_CP_ROQ_DBG_ADDR, 0); 72 + 73 + for (i = 0; i < 512 / 4; i++) { 74 + uint32_t val[4]; 75 + int j; 76 + for (j = 0; j < 4; j++) 77 + val[j] = gpu_read(gpu, REG_A5XX_CP_ROQ_DBG_DATA); 78 + drm_printf(p, " %02x: %08x %08x %08x %08x\n", i, 79 + val[0], val[1], val[2], val[3]); 80 + } 81 + 82 + return 0; 83 + } 84 + 85 + static int show(struct seq_file *m, void *arg) 86 + { 87 + struct drm_info_node *node = (struct drm_info_node *) m->private; 88 + struct drm_device *dev = node->minor->dev; 89 + struct msm_drm_private *priv = dev->dev_private; 90 + struct drm_printer p = drm_seq_file_printer(m); 91 + int (*show)(struct msm_gpu *gpu, struct drm_printer *p) = 92 + node->info_ent->data; 93 + 94 + return show(priv->gpu, &p); 95 + } 96 + 97 + #define ENT(n) { .name = #n, .show = show, .data = n ##_print } 98 + static struct drm_info_list a5xx_debugfs_list[] = { 99 + ENT(pfp), 100 + ENT(me), 101 + ENT(meq), 102 + ENT(roq), 103 + }; 104 + 105 + /* for debugfs files that can be written to, we can't use drm helper: */ 106 + static int 107 + reset_set(void *data, u64 val) 108 + { 109 + struct drm_device *dev = data; 110 + struct msm_drm_private *priv = dev->dev_private; 111 + struct msm_gpu *gpu = priv->gpu; 112 + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); 113 + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); 114 + 115 + if (!capable(CAP_SYS_ADMIN)) 116 + return -EINVAL; 117 + 118 + /* TODO do we care about trying to make sure the GPU is idle? 119 + * Since this is just a debug feature limited to CAP_SYS_ADMIN, 120 + * maybe it is fine to let the user keep both pieces if they 121 + * try to reset an active GPU. 122 + */ 123 + 124 + mutex_lock(&dev->struct_mutex); 125 + 126 + if (adreno_gpu->pm4) { 127 + release_firmware(adreno_gpu->pm4); 128 + adreno_gpu->pm4 = NULL; 129 + } 130 + 131 + if (adreno_gpu->pfp) { 132 + release_firmware(adreno_gpu->pfp); 133 + adreno_gpu->pfp = NULL; 134 + } 135 + if (a5xx_gpu->pm4_bo) { 136 + if (a5xx_gpu->pm4_iova) 137 + msm_gem_put_iova(a5xx_gpu->pm4_bo, gpu->aspace); 138 + drm_gem_object_unreference(a5xx_gpu->pm4_bo); 139 + a5xx_gpu->pm4_bo = NULL; 140 + } 141 + 142 + if (a5xx_gpu->pfp_bo) { 143 + if (a5xx_gpu->pfp_iova) 144 + msm_gem_put_iova(a5xx_gpu->pfp_bo, gpu->aspace); 145 + drm_gem_object_unreference(a5xx_gpu->pfp_bo); 146 + a5xx_gpu->pfp_bo = NULL; 147 + } 148 + 149 + gpu->needs_hw_init = true; 150 + 151 + pm_runtime_get_sync(&gpu->pdev->dev); 152 + gpu->funcs->recover(gpu); 153 + 154 + pm_runtime_put_sync(&gpu->pdev->dev); 155 + mutex_unlock(&dev->struct_mutex); 156 + 157 + return 0; 158 + } 159 + 160 + DEFINE_SIMPLE_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n"); 161 + 162 + 163 + int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor) 164 + { 165 + struct drm_device *dev = minor->dev; 166 + struct dentry *ent; 167 + int ret; 168 + 169 + if (!minor) 170 + return 0; 171 + 172 + ret = drm_debugfs_create_files(a5xx_debugfs_list, 173 + ARRAY_SIZE(a5xx_debugfs_list), 174 + minor->debugfs_root, minor); 175 + 176 + if (ret) { 177 + dev_err(dev->dev, "could not install a5xx_debugfs_list\n"); 178 + return ret; 179 + } 180 + 181 + ent = debugfs_create_file("reset", S_IWUGO, 182 + minor->debugfs_root, 183 + dev, &reset_fops); 184 + if (!ent) 185 + return -ENOMEM; 186 + 187 + return 0; 188 + }
+1
drivers/gpu/drm/msm/adreno/a5xx_gpu.c
··· 1195 1195 .destroy = a5xx_destroy, 1196 1196 #ifdef CONFIG_DEBUG_FS 1197 1197 .show = a5xx_show, 1198 + .debugfs_init = a5xx_debugfs_init, 1198 1199 #endif 1199 1200 .gpu_busy = a5xx_gpu_busy, 1200 1201 },
+4
drivers/gpu/drm/msm/adreno/a5xx_gpu.h
··· 49 49 50 50 #define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base) 51 51 52 + #ifdef CONFIG_DEBUG_FS 53 + int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor); 54 + #endif 55 + 52 56 /* 53 57 * In order to do lockless preemption we use a simple state machine to progress 54 58 * through the process.
+6
drivers/gpu/drm/msm/adreno/adreno_device.c
··· 150 150 return NULL; 151 151 } 152 152 153 + if (gpu->funcs->debugfs_init) { 154 + gpu->funcs->debugfs_init(gpu, dev->primary); 155 + gpu->funcs->debugfs_init(gpu, dev->render); 156 + gpu->funcs->debugfs_init(gpu, dev->control); 157 + } 158 + 153 159 return gpu; 154 160 } 155 161
+4 -1
drivers/gpu/drm/msm/msm_debugfs.c
··· 161 161 return ret; 162 162 } 163 163 164 - if (priv->kms->funcs->debugfs_init) 164 + if (priv->kms->funcs->debugfs_init) { 165 165 ret = priv->kms->funcs->debugfs_init(priv->kms, minor); 166 + if (ret) 167 + return ret; 168 + } 166 169 167 170 return ret; 168 171 }
+2
drivers/gpu/drm/msm/msm_gpu.h
··· 65 65 #ifdef CONFIG_DEBUG_FS 66 66 /* show GPU status in debugfs: */ 67 67 void (*show)(struct msm_gpu *gpu, struct seq_file *m); 68 + /* for generation specific debugfs: */ 69 + int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor); 68 70 #endif 69 71 int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value); 70 72 };