/* 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 (Gen9+) */
+#define CTC_MODE                       _MMIO(0xA26C)
+#define  CTC_SOURCE_PARAMETER_MASK 1
+#define  CTC_SOURCE_CRYSTAL_CLOCK      0
+#define  CTC_SOURCE_DIVIDE_LOGIC       1
+#define  CTC_SHIFT_PARAMETER_SHIFT     1
+#define  CTC_SHIFT_PARAMETER_MASK      (0x3 << CTC_SHIFT_PARAMETER_SHIFT)
+
 /* RCP unit config (Gen8+) */
 #define RCP_CONFIG         _MMIO(0x0D08)
 
 #define ILK_TIMESTAMP_HI       _MMIO(0x70070)
 #define IVB_TIMESTAMP_CTR      _MMIO(0x44070)
 
+#define GEN9_TIMESTAMP_OVERRIDE                                _MMIO(0x44074)
+#define  GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DIVIDER_SHIFT      0
+#define  GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DIVIDER_MASK       0x3ff
+#define  GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_SHIFT  12
+#define  GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_MASK   (0xf << 12)
+
 #define _PIPE_FRMTMSTMP_A              0x70048
 #define PIPE_FRMTMSTMP(pipe)           \
                        _MMIO_PIPE2(pipe, _PIPE_FRMTMSTMP_A)
 
        sseu->has_eu_pg = 0;
 }
 
+static u64 read_reference_ts_freq(struct drm_i915_private *dev_priv)
+{
+       u32 ts_override = I915_READ(GEN9_TIMESTAMP_OVERRIDE);
+       u64 base_freq, frac_freq;
+
+       base_freq = ((ts_override & GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DIVIDER_MASK) >>
+                    GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DIVIDER_SHIFT) + 1;
+       base_freq *= 1000000;
+
+       frac_freq = ((ts_override &
+                     GEN9_TIMESTAMP_OVERRIDE_US_COUNTER_DENOMINATOR_MASK) >>
+                    GEN9_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)
+{
+       u64 f12_5_mhz = 12500000;
+       u64 f19_2_mhz = 19200000;
+       u64 f24_mhz = 24000000;
+
+       if (INTEL_GEN(dev_priv) <= 4) {
+               /* PRMs say:
+                *
+                *     "The value in this register increments once every 16
+                *      hclks." (through the “Clocking Configuration”
+                *      (“CLKCFG”) MCHBAR register)
+                */
+               return (dev_priv->rawclk_freq * 1000) / 16;
+       } else if (INTEL_GEN(dev_priv) <= 8) {
+               /* 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 f12_5_mhz;
+       } else if (INTEL_GEN(dev_priv) <= 9) {
+               u32 ctc_reg = I915_READ(CTC_MODE);
+               u64 freq = 0;
+
+               if ((ctc_reg & CTC_SOURCE_PARAMETER_MASK) == CTC_SOURCE_DIVIDE_LOGIC) {
+                       freq = read_reference_ts_freq(dev_priv);
+               } else {
+                       freq = IS_GEN9_LP(dev_priv) ? f19_2_mhz : f24_mhz;
+
+                       /* Now figure out how the command stream's timestamp
+                        * register increments from this frequency (it might
+                        * increment only every few clock cycle).
+                        */
+                       freq >>= 3 - ((ctc_reg & CTC_SHIFT_PARAMETER_MASK) >>
+                                     CTC_SHIFT_PARAMETER_SHIFT);
+               }
+
+               return freq;
+       } else if (INTEL_GEN(dev_priv) <= 10) {
+               u32 ctc_reg = I915_READ(CTC_MODE);
+               u64 freq = 0;
+               u32 rpm_config_reg = 0;
+
+               /* First figure out the reference frequency. There are 2 ways
+                * we can compute the frequency, either through the
+                * TIMESTAMP_OVERRIDE register or through RPM_CONFIG. CTC_MODE
+                * tells us which one we should use.
+                */
+               if ((ctc_reg & CTC_SOURCE_PARAMETER_MASK) == CTC_SOURCE_DIVIDE_LOGIC) {
+                       freq = read_reference_ts_freq(dev_priv);
+               } else {
+                       u32 crystal_clock;
+
+                       rpm_config_reg = I915_READ(RPM_CONFIG0);
+                       crystal_clock = (rpm_config_reg &
+                                        GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK) >>
+                               GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT;
+                       switch (crystal_clock) {
+                       case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ:
+                               freq = f19_2_mhz;
+                               break;
+                       case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ:
+                               freq = f24_mhz;
+                               break;
+                       }
+               }
+
+               /* Now figure out how the command stream's timestamp register
+                * increments from this frequency (it might increment only
+                * every few clock cycle).
+                */
+               freq >>= 3 - ((rpm_config_reg &
+                              GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK) >>
+                             GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT);
+
+               return freq;
+       }
+
+       DRM_ERROR("Unknown gen, unable to compute command stream timestamp frequency\n");
+       return 0;
+}
+
 /*
  * Determine various intel_device_info fields at runtime.
  *
        else if (INTEL_GEN(dev_priv) >= 10)
                gen10_sseu_info_init(dev_priv);
 
+       /* Initialize command stream timestamp frequency */
+       info->cs_timestamp_frequency = read_timestamp_frequency(dev_priv);
+
        DRM_DEBUG_DRIVER("slice mask: %04x\n", info->sseu.slice_mask);
        DRM_DEBUG_DRIVER("slice total: %u\n", hweight8(info->sseu.slice_mask));
        DRM_DEBUG_DRIVER("subslice total: %u\n",
                         info->sseu.has_subslice_pg ? "y" : "n");
        DRM_DEBUG_DRIVER("has EU power gating: %s\n",
                         info->sseu.has_eu_pg ? "y" : "n");
+       DRM_DEBUG_DRIVER("CS timestamp frequency: %llu\n",
+                        info->cs_timestamp_frequency);
 }