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 gen11_sseu_device_status(struct intel_gt *gt, 54 struct sseu_dev_info *sseu) 55 { 56 #define SS_MAX 8 57 struct intel_uncore *uncore = gt->uncore; 58 const struct intel_gt_info *info = >->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 = >->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 = >->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 = >->info; 248 struct sseu_dev_info sseu; 249 intel_wakeref_t wakeref; 250 251 if (GRAPHICS_VER(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 (GRAPHICS_VER(i915) == 9) 269 gen9_sseu_device_status(gt, &sseu); 270 else if (GRAPHICS_VER(i915) >= 11) 271 gen11_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(>->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