Commit 91355834 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915: Do not overflow the MMADDR write FIFO



Whilst the GT is powered down (rc6), writes to MMADDR are placed in a
FIFO by the System Agent. This is a limited resource, only 64 entries, of
which 20 are reserved for Display and PCH writes, and so we must take
care not to queue up too many writes. To avoid this, there is counter
which we can poll to ensure there are sufficient free entries in the
fifo.

"Issuing a write to a full FIFO is not supported; at worst it could
result in corruption or a system hang."

Reported-and-Tested-by: default avatarMatt Turner <mattst88@gmail.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=34056


Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent 0ee537ab
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -865,7 +865,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
		int max_freq;
		int max_freq;


		/* RPSTAT1 is in the GT power well */
		/* RPSTAT1 is in the GT power well */
		__gen6_force_wake_get(dev_priv);
		__gen6_gt_force_wake_get(dev_priv);


		seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
		seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
		seq_printf(m, "RPSTAT1: 0x%08x\n", I915_READ(GEN6_RPSTAT1));
		seq_printf(m, "RPSTAT1: 0x%08x\n", I915_READ(GEN6_RPSTAT1));
@@ -888,7 +888,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
		seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
		seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
			   max_freq * 100);
			   max_freq * 100);


		__gen6_force_wake_put(dev_priv);
		__gen6_gt_force_wake_put(dev_priv);
	} else {
	} else {
		seq_printf(m, "no P-state info available\n");
		seq_printf(m, "no P-state info available\n");
	}
	}
+12 −2
Original line number Original line Diff line number Diff line
@@ -254,7 +254,7 @@ void intel_detect_pch (struct drm_device *dev)
	}
	}
}
}


void __gen6_force_wake_get(struct drm_i915_private *dev_priv)
void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
{
{
	int count;
	int count;


@@ -270,12 +270,22 @@ void __gen6_force_wake_get(struct drm_i915_private *dev_priv)
		udelay(10);
		udelay(10);
}
}


void __gen6_force_wake_put(struct drm_i915_private *dev_priv)
void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
{
	I915_WRITE_NOTRACE(FORCEWAKE, 0);
	I915_WRITE_NOTRACE(FORCEWAKE, 0);
	POSTING_READ(FORCEWAKE);
	POSTING_READ(FORCEWAKE);
}
}


void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{
	int loop = 500;
	u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
	while (fifo < 20 && loop--) {
		udelay(10);
		fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
	}
}

static int i915_drm_freeze(struct drm_device *dev)
static int i915_drm_freeze(struct drm_device *dev)
{
{
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct drm_i915_private *dev_priv = dev->dev_private;
+15 −5
Original line number Original line Diff line number Diff line
@@ -1353,22 +1353,32 @@ __i915_write(64, q)
 * must be set to prevent GT core from power down and stale values being
 * must be set to prevent GT core from power down and stale values being
 * returned.
 * returned.
 */
 */
void __gen6_force_wake_get(struct drm_i915_private *dev_priv);
void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
void __gen6_force_wake_put (struct drm_i915_private *dev_priv);
void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
static inline u32 i915_safe_read(struct drm_i915_private *dev_priv, u32 reg)
void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);

static inline u32 i915_gt_read(struct drm_i915_private *dev_priv, u32 reg)
{
{
	u32 val;
	u32 val;


	if (dev_priv->info->gen >= 6) {
	if (dev_priv->info->gen >= 6) {
		__gen6_force_wake_get(dev_priv);
		__gen6_gt_force_wake_get(dev_priv);
		val = I915_READ(reg);
		val = I915_READ(reg);
		__gen6_force_wake_put(dev_priv);
		__gen6_gt_force_wake_put(dev_priv);
	} else
	} else
		val = I915_READ(reg);
		val = I915_READ(reg);


	return val;
	return val;
}
}


static inline void i915_gt_write(struct drm_i915_private *dev_priv,
				u32 reg, u32 val)
{
	if (dev_priv->info->gen >= 6)
		__gen6_gt_wait_for_fifo(dev_priv);
	I915_WRITE(reg, val);
}

static inline void
static inline void
i915_write(struct drm_i915_private *dev_priv, u32 reg, u64 val, int len)
i915_write(struct drm_i915_private *dev_priv, u32 reg, u64 val, int len)
{
{
+2 −0
Original line number Original line Diff line number Diff line
@@ -3261,6 +3261,8 @@
#define  FORCEWAKE				0xA18C
#define  FORCEWAKE				0xA18C
#define  FORCEWAKE_ACK				0x130090
#define  FORCEWAKE_ACK				0x130090


#define  GT_FIFO_FREE_ENTRIES			0x120008

#define GEN6_RPNSWREQ				0xA008
#define GEN6_RPNSWREQ				0xA008
#define   GEN6_TURBO_DISABLE			(1<<31)
#define   GEN6_TURBO_DISABLE			(1<<31)
#define   GEN6_FREQUENCY(x)			((x)<<25)
#define   GEN6_FREQUENCY(x)			((x)<<25)
+4 −4
Original line number Original line Diff line number Diff line
@@ -1219,7 +1219,7 @@ static void sandybridge_blit_fbc_update(struct drm_device *dev)
	u32 blt_ecoskpd;
	u32 blt_ecoskpd;


	/* Make sure blitter notifies FBC of writes */
	/* Make sure blitter notifies FBC of writes */
	__gen6_force_wake_get(dev_priv);
	__gen6_gt_force_wake_get(dev_priv);
	blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD);
	blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD);
	blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY <<
	blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY <<
		GEN6_BLITTER_LOCK_SHIFT;
		GEN6_BLITTER_LOCK_SHIFT;
@@ -1230,7 +1230,7 @@ static void sandybridge_blit_fbc_update(struct drm_device *dev)
			 GEN6_BLITTER_LOCK_SHIFT);
			 GEN6_BLITTER_LOCK_SHIFT);
	I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
	I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
	POSTING_READ(GEN6_BLITTER_ECOSKPD);
	POSTING_READ(GEN6_BLITTER_ECOSKPD);
	__gen6_force_wake_put(dev_priv);
	__gen6_gt_force_wake_put(dev_priv);
}
}


static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
@@ -6282,7 +6282,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
	 * userspace...
	 * userspace...
	 */
	 */
	I915_WRITE(GEN6_RC_STATE, 0);
	I915_WRITE(GEN6_RC_STATE, 0);
	__gen6_force_wake_get(dev_priv);
	__gen6_gt_force_wake_get(dev_priv);


	/* disable the counters and set deterministic thresholds */
	/* disable the counters and set deterministic thresholds */
	I915_WRITE(GEN6_RC_CONTROL, 0);
	I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -6380,7 +6380,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
	/* enable all PM interrupts */
	/* enable all PM interrupts */
	I915_WRITE(GEN6_PMINTRMSK, 0);
	I915_WRITE(GEN6_PMINTRMSK, 0);


	__gen6_force_wake_put(dev_priv);
	__gen6_gt_force_wake_put(dev_priv);
}
}


void intel_enable_clock_gating(struct drm_device *dev)
void intel_enable_clock_gating(struct drm_device *dev)
Loading