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