1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
3  */
4 
5 #include <linux/types.h>
6 #include <linux/debugfs.h>
7 
8 #include <drm/drm_debugfs.h>
9 #include <drm/drm_file.h>
10 #include <drm/drm_print.h>
11 
12 #include "a5xx_gpu.h"
13 
14 static int pfp_print(struct msm_gpu *gpu, struct drm_printer *p)
15 {
16 	int i;
17 
18 	drm_printf(p, "PFP state:\n");
19 
20 	for (i = 0; i < 36; i++) {
21 		gpu_write(gpu, REG_A5XX_CP_PFP_STAT_ADDR, i);
22 		drm_printf(p, "  %02x: %08x\n", i,
23 			gpu_read(gpu, REG_A5XX_CP_PFP_STAT_DATA));
24 	}
25 
26 	return 0;
27 }
28 
29 static int me_print(struct msm_gpu *gpu, struct drm_printer *p)
30 {
31 	int i;
32 
33 	drm_printf(p, "ME state:\n");
34 
35 	for (i = 0; i < 29; i++) {
36 		gpu_write(gpu, REG_A5XX_CP_ME_STAT_ADDR, i);
37 		drm_printf(p, "  %02x: %08x\n", i,
38 			gpu_read(gpu, REG_A5XX_CP_ME_STAT_DATA));
39 	}
40 
41 	return 0;
42 }
43 
44 static int meq_print(struct msm_gpu *gpu, struct drm_printer *p)
45 {
46 	int i;
47 
48 	drm_printf(p, "MEQ state:\n");
49 	gpu_write(gpu, REG_A5XX_CP_MEQ_DBG_ADDR, 0);
50 
51 	for (i = 0; i < 64; i++) {
52 		drm_printf(p, "  %02x: %08x\n", i,
53 			gpu_read(gpu, REG_A5XX_CP_MEQ_DBG_DATA));
54 	}
55 
56 	return 0;
57 }
58 
59 static int roq_print(struct msm_gpu *gpu, struct drm_printer *p)
60 {
61 	int i;
62 
63 	drm_printf(p, "ROQ state:\n");
64 	gpu_write(gpu, REG_A5XX_CP_ROQ_DBG_ADDR, 0);
65 
66 	for (i = 0; i < 512 / 4; i++) {
67 		uint32_t val[4];
68 		int j;
69 		for (j = 0; j < 4; j++)
70 			val[j] = gpu_read(gpu, REG_A5XX_CP_ROQ_DBG_DATA);
71 		drm_printf(p, "  %02x: %08x %08x %08x %08x\n", i,
72 			val[0], val[1], val[2], val[3]);
73 	}
74 
75 	return 0;
76 }
77 
78 static int show(struct seq_file *m, void *arg)
79 {
80 	struct drm_info_node *node = (struct drm_info_node *) m->private;
81 	struct drm_device *dev = node->minor->dev;
82 	struct msm_drm_private *priv = dev->dev_private;
83 	struct drm_printer p = drm_seq_file_printer(m);
84 	int (*show)(struct msm_gpu *gpu, struct drm_printer *p) =
85 		node->info_ent->data;
86 
87 	return show(priv->gpu, &p);
88 }
89 
90 #define ENT(n) { .name = #n, .show = show, .data = n ##_print }
91 static struct drm_info_list a5xx_debugfs_list[] = {
92 	ENT(pfp),
93 	ENT(me),
94 	ENT(meq),
95 	ENT(roq),
96 };
97 
98 /* for debugfs files that can be written to, we can't use drm helper: */
99 static int
100 reset_set(void *data, u64 val)
101 {
102 	struct drm_device *dev = data;
103 	struct msm_drm_private *priv = dev->dev_private;
104 	struct msm_gpu *gpu = priv->gpu;
105 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
106 	struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
107 
108 	if (!capable(CAP_SYS_ADMIN))
109 		return -EINVAL;
110 
111 	/* TODO do we care about trying to make sure the GPU is idle?
112 	 * Since this is just a debug feature limited to CAP_SYS_ADMIN,
113 	 * maybe it is fine to let the user keep both pieces if they
114 	 * try to reset an active GPU.
115 	 */
116 
117 	mutex_lock(&dev->struct_mutex);
118 
119 	release_firmware(adreno_gpu->fw[ADRENO_FW_PM4]);
120 	adreno_gpu->fw[ADRENO_FW_PM4] = NULL;
121 
122 	release_firmware(adreno_gpu->fw[ADRENO_FW_PFP]);
123 	adreno_gpu->fw[ADRENO_FW_PFP] = NULL;
124 
125 	if (a5xx_gpu->pm4_bo) {
126 		msm_gem_unpin_iova(a5xx_gpu->pm4_bo, gpu->aspace);
127 		drm_gem_object_put_locked(a5xx_gpu->pm4_bo);
128 		a5xx_gpu->pm4_bo = NULL;
129 	}
130 
131 	if (a5xx_gpu->pfp_bo) {
132 		msm_gem_unpin_iova(a5xx_gpu->pfp_bo, gpu->aspace);
133 		drm_gem_object_put_locked(a5xx_gpu->pfp_bo);
134 		a5xx_gpu->pfp_bo = NULL;
135 	}
136 
137 	gpu->needs_hw_init = true;
138 
139 	pm_runtime_get_sync(&gpu->pdev->dev);
140 	gpu->funcs->recover(gpu);
141 
142 	pm_runtime_put_sync(&gpu->pdev->dev);
143 	mutex_unlock(&dev->struct_mutex);
144 
145 	return 0;
146 }
147 
148 DEFINE_SIMPLE_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n");
149 
150 
151 void a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
152 {
153 	struct drm_device *dev;
154 
155 	if (!minor)
156 		return;
157 
158 	dev = minor->dev;
159 
160 	drm_debugfs_create_files(a5xx_debugfs_list,
161 				 ARRAY_SIZE(a5xx_debugfs_list),
162 				 minor->debugfs_root, minor);
163 
164 	debugfs_create_file("reset", S_IWUGO, minor->debugfs_root, dev,
165 			    &reset_fops);
166 }
167