drm/i915/skl: Determine SKL slice/subslice/EU info
authorJeff McGee <jeff.mcgee@intel.com>
Fri, 13 Feb 2015 16:27:54 +0000 (10:27 -0600)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 23 Feb 2015 22:56:59 +0000 (23:56 +0100)
Read fuse registers to determine the available slice total,
subslice total, subslice per slice, EU total, and EU per subslice
counts of the SKL device. The EU per subslice attribute is more
precisely defined as the maximum EU available on any one subslice,
since available EU counts may vary across subslices due to fusing.
Set flags indicating the SKL device's slice/subslice/EU (SSEU)
power gating capability. Make all values available via debugfs
entry 'i915_sseu_status'.

v2: Several small clean-ups suggested by Damien. Most notably,
    used smaller types for the new device info fields to reduce
    memory usage and improved the clarity/readability of the
    method used to extract attribute values from the fuse
    registers.

Signed-off-by: Jeff McGee <jeff.mcgee@intel.com>
Reviewed-by: Damien Lespiau <damien.lespiau@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_reg.h

index 3a08684a7af33710536ecd87dcb5916643a67fb8..238cd6fa48af64636d634ab758ae2eeba71f8203 100644 (file)
@@ -4358,6 +4358,35 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops,
                        i915_cache_sharing_get, i915_cache_sharing_set,
                        "%llu\n");
 
+static int i915_sseu_status(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+
+       if (INTEL_INFO(dev)->gen < 9)
+               return -ENODEV;
+
+       seq_puts(m, "SSEU Device Info\n");
+       seq_printf(m, "  Available Slice Total: %u\n",
+                  INTEL_INFO(dev)->slice_total);
+       seq_printf(m, "  Available Subslice Total: %u\n",
+                  INTEL_INFO(dev)->subslice_total);
+       seq_printf(m, "  Available Subslice Per Slice: %u\n",
+                  INTEL_INFO(dev)->subslice_per_slice);
+       seq_printf(m, "  Available EU Total: %u\n",
+                  INTEL_INFO(dev)->eu_total);
+       seq_printf(m, "  Available EU Per Subslice: %u\n",
+                  INTEL_INFO(dev)->eu_per_subslice);
+       seq_printf(m, "  Has Slice Power Gating: %s\n",
+                  yesno(INTEL_INFO(dev)->has_slice_pg));
+       seq_printf(m, "  Has Subslice Power Gating: %s\n",
+                  yesno(INTEL_INFO(dev)->has_subslice_pg));
+       seq_printf(m, "  Has EU Power Gating: %s\n",
+                  yesno(INTEL_INFO(dev)->has_eu_pg));
+
+       return 0;
+}
+
 static int i915_forcewake_open(struct inode *inode, struct file *file)
 {
        struct drm_device *dev = inode->i_private;
@@ -4471,6 +4500,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
        {"i915_dp_mst_info", i915_dp_mst_info, 0},
        {"i915_wa_registers", i915_wa_registers, 0},
        {"i915_ddb_info", i915_ddb_info, 0},
+       {"i915_sseu_status", i915_sseu_status, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
index 5804aa5f9df074c007782fa89963dba47b3f5cc4..9a365b40b50eeccf189a2a2875375cec73398ef4 100644 (file)
@@ -606,6 +606,7 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
                }
        }
 
+       /* Initialize slice/subslice/EU info */
        if (IS_CHERRYVIEW(dev)) {
                u32 fuse, mask_eu;
 
@@ -615,7 +616,79 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
                                  CHV_FGT_EU_DIS_SS1_R0_MASK |
                                  CHV_FGT_EU_DIS_SS1_R1_MASK);
                info->eu_total = 16 - hweight32(mask_eu);
+       } else if (IS_SKYLAKE(dev)) {
+               const int s_max = 3, ss_max = 4, eu_max = 8;
+               int s, ss;
+               u32 fuse2, eu_disable[s_max], s_enable, ss_disable;
+
+               fuse2 = I915_READ(GEN8_FUSE2);
+               s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >>
+                          GEN8_F2_S_ENA_SHIFT;
+               ss_disable = (fuse2 & GEN9_F2_SS_DIS_MASK) >>
+                            GEN9_F2_SS_DIS_SHIFT;
+
+               eu_disable[0] = I915_READ(GEN8_EU_DISABLE0);
+               eu_disable[1] = I915_READ(GEN8_EU_DISABLE1);
+               eu_disable[2] = I915_READ(GEN8_EU_DISABLE2);
+
+               info->slice_total = hweight32(s_enable);
+               /*
+                * The subslice disable field is global, i.e. it applies
+                * to each of the enabled slices.
+               */
+               info->subslice_per_slice = ss_max - hweight32(ss_disable);
+               info->subslice_total = info->slice_total *
+                                      info->subslice_per_slice;
+
+               /*
+                * Iterate through enabled slices and subslices to
+                * count the total enabled EU.
+               */
+               for (s = 0; s < s_max; s++) {
+                       if (!(s_enable & (0x1 << s)))
+                               /* skip disabled slice */
+                               continue;
+
+                       for (ss = 0; ss < ss_max; ss++) {
+                               if (ss_disable & (0x1 << ss))
+                                       /* skip disabled subslice */
+                                       continue;
+
+                               info->eu_total += eu_max -
+                                                 hweight8(eu_disable[s] >>
+                                                          (ss * eu_max));
+                       }
+               }
+
+               /*
+                * SKL is expected to always have a uniform distribution
+                * of EU across subslices with the exception that any one
+                * EU in any one subslice may be fused off for die
+                * recovery.
+               */
+               info->eu_per_subslice = info->subslice_total ?
+                                       DIV_ROUND_UP(info->eu_total,
+                                                    info->subslice_total) : 0;
+               /*
+                * SKL supports slice power gating on devices with more than
+                * one slice, and supports EU power gating on devices with
+                * more than one EU pair per subslice.
+               */
+               info->has_slice_pg = (info->slice_total > 1) ? 1 : 0;
+               info->has_subslice_pg = 0;
+               info->has_eu_pg = (info->eu_per_subslice > 2) ? 1 : 0;
        }
+       DRM_DEBUG_DRIVER("slice total: %u\n", info->slice_total);
+       DRM_DEBUG_DRIVER("subslice total: %u\n", info->subslice_total);
+       DRM_DEBUG_DRIVER("subslice per slice: %u\n", info->subslice_per_slice);
+       DRM_DEBUG_DRIVER("EU total: %u\n", info->eu_total);
+       DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->eu_per_subslice);
+       DRM_DEBUG_DRIVER("has slice power gating: %s\n",
+                        info->has_slice_pg ? "y" : "n");
+       DRM_DEBUG_DRIVER("has subslice power gating: %s\n",
+                        info->has_subslice_pg ? "y" : "n");
+       DRM_DEBUG_DRIVER("has EU power gating: %s\n",
+                        info->has_eu_pg ? "y" : "n");
 }
 
 /**
index 3f210aa7652e67bed015c2edd5dbf45cc9978187..61d41abde2e94730ccac0890540f9034b78e84d5 100644 (file)
@@ -693,7 +693,16 @@ struct intel_device_info {
        int trans_offsets[I915_MAX_TRANSCODERS];
        int palette_offsets[I915_MAX_PIPES];
        int cursor_offsets[I915_MAX_PIPES];
-       unsigned int eu_total;
+
+       /* Slice/subslice/EU info */
+       u8 slice_total;
+       u8 subslice_total;
+       u8 subslice_per_slice;
+       u8 eu_total;
+       u8 eu_per_subslice;
+       u8 has_slice_pg:1;
+       u8 has_subslice_pg:1;
+       u8 has_eu_pg:1;
 };
 
 #undef DEFINE_FLAG
index f67e290b5475908278a5d6f406128fe84ae3f5d3..f9d4367ee8d4f536f4c9e86fc1258915e75a89f9 100644 (file)
@@ -1516,6 +1516,17 @@ enum skl_disp_power_wells {
 #define   CHV_FGT_EU_DIS_SS1_R1_SHIFT  28
 #define   CHV_FGT_EU_DIS_SS1_R1_MASK   (0xf << CHV_FGT_EU_DIS_SS1_R1_SHIFT)
 
+#define GEN8_FUSE2                     0x9120
+#define   GEN8_F2_S_ENA_SHIFT          25
+#define   GEN8_F2_S_ENA_MASK           (0x7 << GEN8_F2_S_ENA_SHIFT)
+
+#define   GEN9_F2_SS_DIS_SHIFT         20
+#define   GEN9_F2_SS_DIS_MASK          (0xf << GEN9_F2_SS_DIS_SHIFT)
+
+#define GEN8_EU_DISABLE0               0x9134
+#define GEN8_EU_DISABLE1               0x9138
+#define GEN8_EU_DISABLE2               0x913c
+
 #define GEN6_BSD_SLEEP_PSMI_CONTROL    0x12050
 #define   GEN6_BSD_SLEEP_MSG_DISABLE   (1 << 0)
 #define   GEN6_BSD_SLEEP_FLUSH_DISABLE (1 << 2)