1 // SPDX-License-Identifier: MIT
2
3 /*
4 * Copyright © 2020 Intel Corporation
5 */
6
7 #include "i915_drv.h"
8 #include "intel_gt_debugfs.h"
9 #include "intel_sseu_debugfs.h"
10
sseu_copy_subslices(const struct sseu_dev_info * sseu,int slice,u8 * to_mask)11 static void sseu_copy_subslices(const struct sseu_dev_info *sseu,
12 int slice, u8 *to_mask)
13 {
14 int offset = slice * sseu->ss_stride;
15
16 memcpy(&to_mask[offset], &sseu->subslice_mask[offset], sseu->ss_stride);
17 }
18
cherryview_sseu_device_status(struct intel_gt * gt,struct sseu_dev_info * sseu)19 static void cherryview_sseu_device_status(struct intel_gt *gt,
20 struct sseu_dev_info *sseu)
21 {
22 #define SS_MAX 2
23 struct intel_uncore *uncore = gt->uncore;
24 const int ss_max = SS_MAX;
25 u32 sig1[SS_MAX], sig2[SS_MAX];
26 int ss;
27
28 sig1[0] = intel_uncore_read(uncore, CHV_POWER_SS0_SIG1);
29 sig1[1] = intel_uncore_read(uncore, CHV_POWER_SS1_SIG1);
30 sig2[0] = intel_uncore_read(uncore, CHV_POWER_SS0_SIG2);
31 sig2[1] = intel_uncore_read(uncore, CHV_POWER_SS1_SIG2);
32
33 for (ss = 0; ss < ss_max; ss++) {
34 unsigned int eu_cnt;
35
36 if (sig1[ss] & CHV_SS_PG_ENABLE)
37 /* skip disabled subslice */
38 continue;
39
40 sseu->slice_mask = BIT(0);
41 sseu->subslice_mask[0] |= BIT(ss);
42 eu_cnt = ((sig1[ss] & CHV_EU08_PG_ENABLE) ? 0 : 2) +
43 ((sig1[ss] & CHV_EU19_PG_ENABLE) ? 0 : 2) +
44 ((sig1[ss] & CHV_EU210_PG_ENABLE) ? 0 : 2) +
45 ((sig2[ss] & CHV_EU311_PG_ENABLE) ? 0 : 2);
46 sseu->eu_total += eu_cnt;
47 sseu->eu_per_subslice = max_t(unsigned int,
48 sseu->eu_per_subslice, eu_cnt);
49 }
50 #undef SS_MAX
51 }
52
gen11_sseu_device_status(struct intel_gt * gt,struct sseu_dev_info * sseu)53 static void gen11_sseu_device_status(struct intel_gt *gt,
54 struct sseu_dev_info *sseu)
55 {
56 #define SS_MAX 8
57 struct intel_uncore *uncore = gt->uncore;
58 const struct intel_gt_info *info = >->info;
59 u32 s_reg[SS_MAX], eu_reg[2 * SS_MAX], eu_mask[2];
60 int s, ss;
61
62 for (s = 0; s < info->sseu.max_slices; s++) {
63 /*
64 * FIXME: Valid SS Mask respects the spec and read
65 * only valid bits for those registers, excluding reserved
66 * although this seems wrong because it would leave many
67 * subslices without ACK.
68 */
69 s_reg[s] = intel_uncore_read(uncore, GEN10_SLICE_PGCTL_ACK(s)) &
70 GEN10_PGCTL_VALID_SS_MASK(s);
71 eu_reg[2 * s] = intel_uncore_read(uncore,
72 GEN10_SS01_EU_PGCTL_ACK(s));
73 eu_reg[2 * s + 1] = intel_uncore_read(uncore,
74 GEN10_SS23_EU_PGCTL_ACK(s));
75 }
76
77 eu_mask[0] = GEN9_PGCTL_SSA_EU08_ACK |
78 GEN9_PGCTL_SSA_EU19_ACK |
79 GEN9_PGCTL_SSA_EU210_ACK |
80 GEN9_PGCTL_SSA_EU311_ACK;
81 eu_mask[1] = GEN9_PGCTL_SSB_EU08_ACK |
82 GEN9_PGCTL_SSB_EU19_ACK |
83 GEN9_PGCTL_SSB_EU210_ACK |
84 GEN9_PGCTL_SSB_EU311_ACK;
85
86 for (s = 0; s < info->sseu.max_slices; s++) {
87 if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0)
88 /* skip disabled slice */
89 continue;
90
91 sseu->slice_mask |= BIT(s);
92 sseu_copy_subslices(&info->sseu, s, sseu->subslice_mask);
93
94 for (ss = 0; ss < info->sseu.max_subslices; ss++) {
95 unsigned int eu_cnt;
96
97 if (info->sseu.has_subslice_pg &&
98 !(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss))))
99 /* skip disabled subslice */
100 continue;
101
102 eu_cnt = 2 * hweight32(eu_reg[2 * s + ss / 2] &
103 eu_mask[ss % 2]);
104 sseu->eu_total += eu_cnt;
105 sseu->eu_per_subslice = max_t(unsigned int,
106 sseu->eu_per_subslice,
107 eu_cnt);
108 }
109 }
110 #undef SS_MAX
111 }
112
gen9_sseu_device_status(struct intel_gt * gt,struct sseu_dev_info * sseu)113 static void gen9_sseu_device_status(struct intel_gt *gt,
114 struct sseu_dev_info *sseu)
115 {
116 #define SS_MAX 3
117 struct intel_uncore *uncore = gt->uncore;
118 const struct intel_gt_info *info = >->info;
119 u32 s_reg[SS_MAX], eu_reg[2 * SS_MAX], eu_mask[2];
120 int s, ss;
121
122 for (s = 0; s < info->sseu.max_slices; s++) {
123 s_reg[s] = intel_uncore_read(uncore, GEN9_SLICE_PGCTL_ACK(s));
124 eu_reg[2 * s] =
125 intel_uncore_read(uncore, GEN9_SS01_EU_PGCTL_ACK(s));
126 eu_reg[2 * s + 1] =
127 intel_uncore_read(uncore, GEN9_SS23_EU_PGCTL_ACK(s));
128 }
129
130 eu_mask[0] = GEN9_PGCTL_SSA_EU08_ACK |
131 GEN9_PGCTL_SSA_EU19_ACK |
132 GEN9_PGCTL_SSA_EU210_ACK |
133 GEN9_PGCTL_SSA_EU311_ACK;
134 eu_mask[1] = GEN9_PGCTL_SSB_EU08_ACK |
135 GEN9_PGCTL_SSB_EU19_ACK |
136 GEN9_PGCTL_SSB_EU210_ACK |
137 GEN9_PGCTL_SSB_EU311_ACK;
138
139 for (s = 0; s < info->sseu.max_slices; s++) {
140 if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0)
141 /* skip disabled slice */
142 continue;
143
144 sseu->slice_mask |= BIT(s);
145
146 if (IS_GEN9_BC(gt->i915))
147 sseu_copy_subslices(&info->sseu, s,
148 sseu->subslice_mask);
149
150 for (ss = 0; ss < info->sseu.max_subslices; ss++) {
151 unsigned int eu_cnt;
152 u8 ss_idx = s * info->sseu.ss_stride +
153 ss / BITS_PER_BYTE;
154
155 if (IS_GEN9_LP(gt->i915)) {
156 if (!(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss))))
157 /* skip disabled subslice */
158 continue;
159
160 sseu->subslice_mask[ss_idx] |=
161 BIT(ss % BITS_PER_BYTE);
162 }
163
164 eu_cnt = eu_reg[2 * s + ss / 2] & eu_mask[ss % 2];
165 eu_cnt = 2 * hweight32(eu_cnt);
166
167 sseu->eu_total += eu_cnt;
168 sseu->eu_per_subslice = max_t(unsigned int,
169 sseu->eu_per_subslice,
170 eu_cnt);
171 }
172 }
173 #undef SS_MAX
174 }
175
bdw_sseu_device_status(struct intel_gt * gt,struct sseu_dev_info * sseu)176 static void bdw_sseu_device_status(struct intel_gt *gt,
177 struct sseu_dev_info *sseu)
178 {
179 const struct intel_gt_info *info = >->info;
180 u32 slice_info = intel_uncore_read(gt->uncore, GEN8_GT_SLICE_INFO);
181 int s;
182
183 sseu->slice_mask = slice_info & GEN8_LSLICESTAT_MASK;
184
185 if (sseu->slice_mask) {
186 sseu->eu_per_subslice = info->sseu.eu_per_subslice;
187 for (s = 0; s < fls(sseu->slice_mask); s++)
188 sseu_copy_subslices(&info->sseu, s,
189 sseu->subslice_mask);
190 sseu->eu_total = sseu->eu_per_subslice *
191 intel_sseu_subslice_total(sseu);
192
193 /* subtract fused off EU(s) from enabled slice(s) */
194 for (s = 0; s < fls(sseu->slice_mask); s++) {
195 u8 subslice_7eu = info->sseu.subslice_7eu[s];
196
197 sseu->eu_total -= hweight8(subslice_7eu);
198 }
199 }
200 }
201
i915_print_sseu_info(struct seq_file * m,bool is_available_info,bool has_pooled_eu,const struct sseu_dev_info * sseu)202 static void i915_print_sseu_info(struct seq_file *m,
203 bool is_available_info,
204 bool has_pooled_eu,
205 const struct sseu_dev_info *sseu)
206 {
207 const char *type = is_available_info ? "Available" : "Enabled";
208 int s;
209
210 seq_printf(m, " %s Slice Mask: %04x\n", type,
211 sseu->slice_mask);
212 seq_printf(m, " %s Slice Total: %u\n", type,
213 hweight8(sseu->slice_mask));
214 seq_printf(m, " %s Subslice Total: %u\n", type,
215 intel_sseu_subslice_total(sseu));
216 for (s = 0; s < fls(sseu->slice_mask); s++) {
217 seq_printf(m, " %s Slice%i subslices: %u\n", type,
218 s, intel_sseu_subslices_per_slice(sseu, s));
219 }
220 seq_printf(m, " %s EU Total: %u\n", type,
221 sseu->eu_total);
222 seq_printf(m, " %s EU Per Subslice: %u\n", type,
223 sseu->eu_per_subslice);
224
225 if (!is_available_info)
226 return;
227
228 seq_printf(m, " Has Pooled EU: %s\n", yesno(has_pooled_eu));
229 if (has_pooled_eu)
230 seq_printf(m, " Min EU in pool: %u\n", sseu->min_eu_in_pool);
231
232 seq_printf(m, " Has Slice Power Gating: %s\n",
233 yesno(sseu->has_slice_pg));
234 seq_printf(m, " Has Subslice Power Gating: %s\n",
235 yesno(sseu->has_subslice_pg));
236 seq_printf(m, " Has EU Power Gating: %s\n",
237 yesno(sseu->has_eu_pg));
238 }
239
240 /*
241 * this is called from top-level debugfs as well, so we can't get the gt from
242 * the seq_file.
243 */
intel_sseu_status(struct seq_file * m,struct intel_gt * gt)244 int intel_sseu_status(struct seq_file *m, struct intel_gt *gt)
245 {
246 struct drm_i915_private *i915 = gt->i915;
247 const struct intel_gt_info *info = >->info;
248 struct sseu_dev_info sseu;
249 intel_wakeref_t wakeref;
250
251 if (GRAPHICS_VER(i915) < 8)
252 return -ENODEV;
253
254 seq_puts(m, "SSEU Device Info\n");
255 i915_print_sseu_info(m, true, HAS_POOLED_EU(i915), &info->sseu);
256
257 seq_puts(m, "SSEU Device Status\n");
258 memset(&sseu, 0, sizeof(sseu));
259 intel_sseu_set_info(&sseu, info->sseu.max_slices,
260 info->sseu.max_subslices,
261 info->sseu.max_eus_per_subslice);
262
263 with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
264 if (IS_CHERRYVIEW(i915))
265 cherryview_sseu_device_status(gt, &sseu);
266 else if (IS_BROADWELL(i915))
267 bdw_sseu_device_status(gt, &sseu);
268 else if (GRAPHICS_VER(i915) == 9)
269 gen9_sseu_device_status(gt, &sseu);
270 else if (GRAPHICS_VER(i915) >= 11)
271 gen11_sseu_device_status(gt, &sseu);
272 }
273
274 i915_print_sseu_info(m, false, HAS_POOLED_EU(i915), &sseu);
275
276 return 0;
277 }
278
sseu_status_show(struct seq_file * m,void * unused)279 static int sseu_status_show(struct seq_file *m, void *unused)
280 {
281 struct intel_gt *gt = m->private;
282
283 return intel_sseu_status(m, gt);
284 }
285 DEFINE_INTEL_GT_DEBUGFS_ATTRIBUTE(sseu_status);
286
rcs_topology_show(struct seq_file * m,void * unused)287 static int rcs_topology_show(struct seq_file *m, void *unused)
288 {
289 struct intel_gt *gt = m->private;
290 struct drm_printer p = drm_seq_file_printer(m);
291
292 intel_sseu_print_topology(>->info.sseu, &p);
293
294 return 0;
295 }
296 DEFINE_INTEL_GT_DEBUGFS_ATTRIBUTE(rcs_topology);
297
intel_sseu_debugfs_register(struct intel_gt * gt,struct dentry * root)298 void intel_sseu_debugfs_register(struct intel_gt *gt, struct dentry *root)
299 {
300 static const struct intel_gt_debugfs_file files[] = {
301 { "sseu_status", &sseu_status_fops, NULL },
302 { "rcs_topology", &rcs_topology_fops, NULL },
303 };
304
305 intel_gt_debugfs_register_files(root, files, ARRAY_SIZE(files), gt);
306 }
307