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