xref: /linux/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c (revision 26fbb4c8c7c3ee9a4c3b4de555a8587b5a19154e)
1 // SPDX-License-Identifier: MIT
2 
3 /*
4  * Copyright © 2020 Intel Corporation
5  */
6 
7 #include "debugfs_gt.h"
8 #include "intel_sseu_debugfs.h"
9 #include "i915_drv.h"
10 
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 
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 
53 static void gen10_sseu_device_status(struct intel_gt *gt,
54 				     struct sseu_dev_info *sseu)
55 {
56 #define SS_MAX 6
57 	struct intel_uncore *uncore = gt->uncore;
58 	const struct intel_gt_info *info = &gt->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 
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 = &gt->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 
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 = &gt->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 
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  */
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 = &gt->info;
248 	struct sseu_dev_info sseu;
249 	intel_wakeref_t wakeref;
250 
251 	if (INTEL_GEN(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 (IS_GEN(i915, 9))
269 			gen9_sseu_device_status(gt, &sseu);
270 		else if (INTEL_GEN(i915) >= 10)
271 			gen10_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 
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_GT_DEBUGFS_ATTRIBUTE(sseu_status);
286 
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(&gt->info.sseu, &p);
293 
294 	return 0;
295 }
296 DEFINE_GT_DEBUGFS_ATTRIBUTE(rcs_topology);
297 
298 void intel_sseu_debugfs_register(struct intel_gt *gt, struct dentry *root)
299 {
300 	static const struct debugfs_gt_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