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

source code of linux/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c