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