We use to have this fixed per generation, but starting with CNL userspace
cannot tell just off the PCI ID. Let's make this information available. This
is particularly useful for performance monitoring where much of the
normalization work is done using those timestamps (this include pipeline
statistics in both GL & Vulkan as well as OA reports).
Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@xxxxxxxxx>
---
drivers/gpu/drm/i915/i915_debugfs.c | 2 +
drivers/gpu/drm/i915/i915_drv.c | 3 +
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/i915_reg.h | 21 +++++++
drivers/gpu/drm/i915/intel_device_info.c | 99 ++++++++++++++++++++++++++++++++
include/uapi/drm/i915_drm.h | 6 ++
6 files changed, 133 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 39883cd915db..0897fd616a1f 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3246,6 +3246,8 @@ static int i915_engine_info(struct seq_file *m, void *unused)
yesno(dev_priv->gt.awake));
seq_printf(m, "Global active requests: %d\n",
dev_priv->gt.active_requests);
+ seq_printf(m, "CS timestamp frequency: %llu\n",
+ dev_priv->info.cs_timestamp_frequency);
p = drm_seq_file_printer(m);
for_each_engine(engine, dev_priv, id)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e7e9e061073b..fdd23e79fb46 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -416,6 +416,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
if (!value)
return -ENODEV;
break;
+ case I915_PARAM_CS_TIMESTAMP_FREQUENCY:
+ value = INTEL_INFO(dev_priv)->cs_timestamp_frequency;
+ break;
default:
DRM_DEBUG("Unknown parameter %d\n", param->param);
return -EINVAL;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6cb7cd7f9420..4e804aaeaae1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -886,6 +886,8 @@ struct intel_device_info {
/* Slice/subslice/EU info */
struct sseu_dev_info sseu;
+ uint64_t cs_timestamp_frequency;
+
struct color_luts {
u16 degamma_lut_size;
u16 gamma_lut_size;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index a2223f01ee2a..f392f28f2cfa 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1119,9 +1119,24 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
/* RPM unit config (Gen8+) */
#define RPM_CONFIG0 _MMIO(0x0D00)
+#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT 3
+#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK (1 << GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT)
+#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ 0
+#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ 1
+#define GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT 1
+#define GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK (0x3 << GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT)
+
#define RPM_CONFIG1 _MMIO(0x0D04)
#define GEN10_GT_NOA_ENABLE (1 << 9)
+/* GPM unit config (assuming Gen8+, documentation is fuzzy...) */
+#define GEN8_CTC_MODE _MMIO(0xA26C)
+#define GEN8_CTC_SOURCE_PARAMETER_MASK 1
+#define GEN8_CTC_SOURCE_CRYSTAL_CLOCK 0
+#define GEN8_CTC_SOURCE_DIVIDE_LOGIC 1
+#define GEN8_CTC_SHIFT_PARAMETER_SHIFT 1
+#define GEN8_CTC_SHIFT_PARAMETER_MASK (0x3 << GEN8_CTC_SHIFT_PARAMETER_SHIFT)
+
/* RPC unit config (Gen8+) */
#define RPC_CONFIG _MMIO(0x0D08)
@@ -8865,6 +8880,12 @@ enum skl_power_gate {
#define ILK_TIMESTAMP_HI _MMIO(0x70070)
#define IVB_TIMESTAMP_CTR _MMIO(0x44070)
+#define GEN8_TIMESTAMP_OVERRIDE _MMIO(0x44074)
+#define GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_SHIFT 0
+#define GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_MASK 0x3ff
+#define GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_SHIFT 12
+#define GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_MASK (0xf << 12)
+
#define _PIPE_FRMTMSTMP_A 0x70048
#define PIPE_FRMTMSTMP(pipe) \
_MMIO_PIPE2(pipe, _PIPE_FRMTMSTMP_A)
diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c
index db03d179fc85..9b71a9b6d80e 100644
--- a/drivers/gpu/drm/i915/intel_device_info.c
+++ b/drivers/gpu/drm/i915/intel_device_info.c
@@ -329,6 +329,100 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
sseu->has_eu_pg = 0;
}
+static u64 read_timestamp_frequency_from_divide(struct drm_i915_private *dev_priv)
+{
+ u32 ts_override = I915_READ(GEN8_TIMESTAMP_OVERRIDE);
+ u64 base_freq, frac_freq;
+
+ base_freq = ((ts_override & GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_MASK) >>
+ GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_SHIFT) + 1;
+ base_freq *= 1000000;
+
+ frac_freq = ((ts_override &
+ GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_MASK) >>
+ GEN8_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_SHIFT);
+ if (frac_freq != 0)
+ frac_freq = 1000000 / (frac_freq + 1);
+
+ return base_freq + frac_freq;
+}
+
+static u64 read_timestamp_frequency(struct drm_i915_private *dev_priv)
+{
+ if (INTEL_GEN(dev_priv) <= 4) {
+ /* PRMs say:
+ *
+ * "The value in this register increments once every 16
+ * hclks." ("CLKCFG" register)
+ *
+ * Since dev_priv->rawclk_freq stores the value in kHz divided
+ * by 4, we just need to divide it again by 4.
+ */
+ return (dev_priv->rawclk_freq * 1000) / 4;
+ } else if (INTEL_GEN(dev_priv) <= 7) {
+ /* PRMs say:
+ *
+ * "The PCU TSC counts 10ns increments; this timestamp
+ * reflects bits 38:3 of the TSC (i.e. 80ns granularity,
+ * rolling over every 1.5 hours).
+ */
+ return 12500000;
+ } else if (INTEL_GEN(dev_priv) <= 9) {
+ u32 ctc_reg = I915_READ(GEN8_CTC_MODE);
+ u64 freq = 0;
+
+ if ((ctc_reg & GEN8_CTC_SOURCE_PARAMETER_MASK) == GEN8_CTC_SOURCE_DIVIDE_LOGIC)
+ freq = read_timestamp_frequency_from_divide(dev_priv);
+ else
+ freq = IS_GEN9_LP(dev_priv) ? 19200000 : 24000000;