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