+static void bdw_sw_calculate_freq(struct drm_device *dev,
+ struct intel_rps_bdw_cal *c, u32 *cur_time, u32 *c0)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u64 busy = 0;
+ u32 busyness_pct = 0;
+ u32 elapsed_time = 0;
+ u16 new_freq = 0;
+
+ if (!c || !cur_time || !c0)
+ return;
+
+ if (0 == c->last_c0)
+ goto out;
+
+ /* Check Evaluation interval */
+ elapsed_time = *cur_time - c->last_ts;
+ if (elapsed_time < c->eval_interval)
+ return;
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+ /*
+ * c0 unit in 32*1.28 usec, elapsed_time unit in 1 usec.
+ * Whole busyness_pct calculation should be
+ * busy = ((u64)(*c0 - c->last_c0) << 5 << 7) / 100;
+ * busyness_pct = (u32)(busy * 100 / elapsed_time);
+ * The final formula is to simplify CPU calculation
+ */
+ busy = (u64)(*c0 - c->last_c0) << 12;
+ do_div(busy, elapsed_time);
+ busyness_pct = (u32)busy;
+
+ if (c->is_up && busyness_pct >= c->it_threshold_pct)
+ new_freq = (u16)dev_priv->rps.cur_freq + 3;
+ if (!c->is_up && busyness_pct <= c->it_threshold_pct)
+ new_freq = (u16)dev_priv->rps.cur_freq - 1;
+
+ /* Adjust to new frequency busyness and compare with threshold */
+ if (0 != new_freq) {
+ if (new_freq > dev_priv->rps.max_freq_softlimit)
+ new_freq = dev_priv->rps.max_freq_softlimit;
+ else if (new_freq < dev_priv->rps.min_freq_softlimit)
+ new_freq = dev_priv->rps.min_freq_softlimit;
+
+ gen6_set_rps(dev, new_freq);
+ }
+
+ mutex_unlock(&dev_priv->rps.hw_lock);
+
+out:
+ c->last_c0 = *c0;
+ c->last_ts = *cur_time;
+}
+
+static void gen8_set_frequency_RP0(struct work_struct *work)
+{
+ struct intel_rps_bdw_turbo *p_bdw_turbo =
+ container_of(work, struct intel_rps_bdw_turbo, work_max_freq);
+ struct intel_gen6_power_mgmt *p_power_mgmt =
+ container_of(p_bdw_turbo, struct intel_gen6_power_mgmt, sw_turbo);
+ struct drm_i915_private *dev_priv =
+ container_of(p_power_mgmt, struct drm_i915_private, rps);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+ gen6_set_rps(dev_priv->dev, dev_priv->rps.rp0_freq);
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void flip_active_timeout_handler(unsigned long var)
+{
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) var;
+
+ del_timer(&dev_priv->rps.sw_turbo.flip_timer);
+ atomic_set(&dev_priv->rps.sw_turbo.flip_received, false);
+
+ queue_work(dev_priv->wq, &dev_priv->rps.sw_turbo.work_max_freq);
+}
+
+void bdw_software_turbo(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ u32 current_time = I915_READ(TIMESTAMP_CTR); /* unit in usec */
+ u32 current_c0 = I915_READ(MCHBAR_PCU_C0); /* unit in 32*1.28 usec */
+
+ bdw_sw_calculate_freq(dev, &dev_priv->rps.sw_turbo.up,
+ ¤t_time, ¤t_c0);
+ bdw_sw_calculate_freq(dev, &dev_priv->rps.sw_turbo.down,
+ ¤t_time, ¤t_c0);
+}
+