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, f: "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, f: " %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, f: "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, f: " %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, f: "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, f: " %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, f: "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, f: " %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 = 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(f: 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(fw: adreno_gpu->fw[ADRENO_FW_PM4]); |
113 | adreno_gpu->fw[ADRENO_FW_PM4] = NULL; |
114 | |
115 | release_firmware(fw: 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(obj: 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(obj: 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(lock: &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(files: a5xx_debugfs_list, |
154 | ARRAY_SIZE(a5xx_debugfs_list), |
155 | root: minor->debugfs_root, minor); |
156 | |
157 | debugfs_create_file_unsafe(name: "reset" , S_IWUGO, parent: minor->debugfs_root, data: dev, |
158 | fops: &reset_fops); |
159 | } |
160 | |