[kernel/f14/master] nouveau: add workaround for nv86 hw bug

Ben Skeggs bskeggs at fedoraproject.org
Tue Nov 2 03:36:55 UTC 2010


commit df57a85a75c4d4d6d62cddd25d12310b93f277b9
Author: Ben Skeggs <bskeggs at redhat.com>
Date:   Tue Nov 2 13:08:11 2010 +1000

    nouveau: add workaround for nv86 hw bug

 drm-nouveau-nv86-bug.patch |  229 ++++++++++++++++++++++++++++++++++++++++++++
 kernel.spec                |    3 +-
 2 files changed, 231 insertions(+), 1 deletions(-)
---
diff --git a/drm-nouveau-nv86-bug.patch b/drm-nouveau-nv86-bug.patch
new file mode 100644
index 0000000..750ebbf
--- /dev/null
+++ b/drm-nouveau-nv86-bug.patch
@@ -0,0 +1,229 @@
+From ad3ff4f72ef7fa1e559cc835535025a4b5a746e6 Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs at redhat.com>
+Date: Fri, 22 Oct 2010 10:26:24 +1000
+Subject: [PATCH] drm/nv50: implement possible workaround for NV86 PGRAPH TLB flush hang
+
+Signed-off-by: Ben Skeggs <bskeggs at redhat.com>
+---
+ drivers/gpu/drm/nouveau/nouveau_drv.h   |    5 +++
+ drivers/gpu/drm/nouveau/nouveau_mem.c   |   14 +++-----
+ drivers/gpu/drm/nouveau/nouveau_sgdma.c |    8 ++--
+ drivers/gpu/drm/nouveau/nouveau_state.c |   10 ++++++
+ drivers/gpu/drm/nouveau/nv50_fifo.c     |    5 +++
+ drivers/gpu/drm/nouveau/nv50_graph.c    |   52 +++++++++++++++++++++++++++++++
+ drivers/gpu/drm/nouveau/nv50_instmem.c  |    1 -
+ 7 files changed, 82 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+index 88b5093..14236b8 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -314,6 +314,7 @@ struct nouveau_fifo_engine {
+ 	void (*destroy_context)(struct nouveau_channel *);
+ 	int  (*load_context)(struct nouveau_channel *);
+ 	int  (*unload_context)(struct drm_device *);
++	void (*tlb_flush)(struct drm_device *dev);
+ };
+ 
+ struct nouveau_pgraph_object_method {
+@@ -346,6 +347,7 @@ struct nouveau_pgraph_engine {
+ 	void (*destroy_context)(struct nouveau_channel *);
+ 	int  (*load_context)(struct nouveau_channel *);
+ 	int  (*unload_context)(struct drm_device *);
++	void (*tlb_flush)(struct drm_device *dev);
+ 
+ 	void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
+ 				  uint32_t size, uint32_t pitch);
+@@ -943,6 +945,7 @@ extern int  nv50_fifo_create_context(struct nouveau_channel *);
+ extern void nv50_fifo_destroy_context(struct nouveau_channel *);
+ extern int  nv50_fifo_load_context(struct nouveau_channel *);
+ extern int  nv50_fifo_unload_context(struct drm_device *);
++extern void nv50_fifo_tlb_flush(struct drm_device *dev);
+ 
+ /* nv04_graph.c */
+ extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
+@@ -1007,6 +1010,8 @@ extern int  nv50_graph_load_context(struct nouveau_channel *);
+ extern int  nv50_graph_unload_context(struct drm_device *);
+ extern void nv50_graph_context_switch(struct drm_device *);
+ extern int  nv50_grctx_init(struct nouveau_grctx *);
++extern void nv50_graph_tlb_flush(struct drm_device *dev);
++extern void nv86_graph_tlb_flush(struct drm_device *dev);
+ 
+ /* nv04_instmem.c */
+ extern int  nv04_instmem_init(struct drm_device *);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
+index 09db6f6..de43b3e 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
+@@ -174,11 +174,10 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ 			}
+ 		}
+ 	}
+-	dev_priv->engine.instmem.flush(dev);
+ 
+-	nv50_vm_flush(dev, 5);
+-	nv50_vm_flush(dev, 0);
+-	nv50_vm_flush(dev, 4);
++	dev_priv->engine.instmem.flush(dev);
++	dev_priv->engine.fifo.tlb_flush(dev);
++	dev_priv->engine.graph.tlb_flush(dev);
+ 	nv50_vm_flush(dev, 6);
+ 	return 0;
+ }
+@@ -206,11 +205,10 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+ 		while (pte < end)
+ 			nv_wo32(dev, pgt, pte++, 0);
+ 	}
+-	dev_priv->engine.instmem.flush(dev);
+ 
+-	nv50_vm_flush(dev, 5);
+-	nv50_vm_flush(dev, 0);
+-	nv50_vm_flush(dev, 4);
++	dev_priv->engine.instmem.flush(dev);
++	dev_priv->engine.fifo.tlb_flush(dev);
++	dev_priv->engine.graph.tlb_flush(dev);
+ 	nv50_vm_flush(dev, 6);
+ }
+ 
+diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+index 491767f..ae9f353 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
++++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+@@ -118,8 +118,8 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
+ 	dev_priv->engine.instmem.flush(nvbe->dev);
+ 
+ 	if (dev_priv->card_type == NV_50) {
+-		nv50_vm_flush(dev, 5); /* PGRAPH */
+-		nv50_vm_flush(dev, 0); /* PFIFO */
++		dev_priv->engine.fifo.tlb_flush(dev);
++		dev_priv->engine.graph.tlb_flush(dev);
+ 	}
+ 
+ 	nvbe->bound = true;
+@@ -158,8 +158,8 @@ nouveau_sgdma_unbind(struct ttm_backend *be)
+ 	dev_priv->engine.instmem.flush(nvbe->dev);
+ 
+ 	if (dev_priv->card_type == NV_50) {
+-		nv50_vm_flush(dev, 5);
+-		nv50_vm_flush(dev, 0);
++		dev_priv->engine.fifo.tlb_flush(dev);
++		dev_priv->engine.graph.tlb_flush(dev);
+ 	}
+ 
+ 	nvbe->bound = false;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index 866f437..32a9d81 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -283,6 +283,15 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ 		engine->graph.destroy_context	= nv50_graph_destroy_context;
+ 		engine->graph.load_context	= nv50_graph_load_context;
+ 		engine->graph.unload_context	= nv50_graph_unload_context;
++		if (dev_priv->chipset != 0x86)
++			engine->graph.tlb_flush	= nv50_graph_tlb_flush;
++		else {
++			/* from what i can see nvidia do this on every
++			 * pre-NVA3 board except NVAC, but, we've only
++			 * ever seen problems on NV86
++			 */
++			engine->graph.tlb_flush	= nv86_graph_tlb_flush;
++		}
+ 		engine->fifo.channels		= 128;
+ 		engine->fifo.init		= nv50_fifo_init;
+ 		engine->fifo.takedown		= nv50_fifo_takedown;
+@@ -294,6 +303,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ 		engine->fifo.destroy_context	= nv50_fifo_destroy_context;
+ 		engine->fifo.load_context	= nv50_fifo_load_context;
+ 		engine->fifo.unload_context	= nv50_fifo_unload_context;
++		engine->fifo.tlb_flush		= nv50_fifo_tlb_flush;
+ 		break;
+ 	default:
+ 		NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
+diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
+index fb0281a..3c1cfde 100644
+--- a/drivers/gpu/drm/nouveau/nv50_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
+@@ -464,3 +464,8 @@ nv50_fifo_unload_context(struct drm_device *dev)
+ 	return 0;
+ }
+ 
++void
++nv50_fifo_tlb_flush(struct drm_device *dev)
++{
++	nv50_vm_flush(dev, 5);
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
+index 1413028..8bdbbe4 100644
+--- a/drivers/gpu/drm/nouveau/nv50_graph.c
++++ b/drivers/gpu/drm/nouveau/nv50_graph.c
+@@ -403,3 +403,55 @@ struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
+ 	{ 0x8597, false, NULL }, /* tesla (nva3, nva5, nva8) */
+ 	{}
+ };
++
++void
++nv50_graph_tlb_flush(struct drm_device *dev)
++{
++	nv50_vm_flush(dev, 0);
++}
++
++void
++nv86_graph_tlb_flush(struct drm_device *dev)
++{
++	struct drm_nouveau_private *dev_priv = dev->dev_private;
++	struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
++	bool idle, timeout = false;
++	unsigned long flags;
++	u64 start;
++	u32 tmp;
++
++	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
++	nv_mask(dev, 0x400500, 0x00000001, 0x00000000);
++
++	start = ptimer->read(dev);
++	do {
++		idle = true;
++
++		for (tmp = nv_rd32(dev, 0x400380); tmp && idle; tmp >>= 3) {
++			if ((tmp & 7) == 1)
++				idle = false;
++		}
++
++		for (tmp = nv_rd32(dev, 0x400384); tmp && idle; tmp >>= 3) {
++			if ((tmp & 7) == 1)
++				idle = false;
++		}
++
++		for (tmp = nv_rd32(dev, 0x400388); tmp && idle; tmp >>= 3) {
++			if ((tmp & 7) == 1)
++				idle = false;
++		}
++	} while (!idle && !(timeout = ptimer->read(dev) - start > 2000000000));
++
++	if (timeout) {
++		NV_ERROR(dev, "PGRAPH TLB flush idle timeout fail: "
++			      "0x%08x 0x%08x 0x%08x 0x%08x\n",
++			 nv_rd32(dev, 0x400700), nv_rd32(dev, 0x400380),
++			 nv_rd32(dev, 0x400384), nv_rd32(dev, 0x400388));
++	}
++
++	nv50_vm_flush(dev, 0);
++
++	nv_mask(dev, 0x400500, 0x00000001, 0x00000001);
++	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
+index 0c8a6f2..42fcc65 100644
+--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
+@@ -453,7 +453,6 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ 	}
+ 	dev_priv->engine.instmem.flush(dev);
+ 
+-	nv50_vm_flush(dev, 4);
+ 	nv50_vm_flush(dev, 6);
+ 
+ 	gpuobj->im_bound = 1;
+-- 
+1.7.3.1
+
diff --git a/kernel.spec b/kernel.spec
index 8093948..4dd7027 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -669,13 +669,13 @@ Patch1808: drm-ttm-fix.patch
 Patch1810: drm-nouveau-updates.patch
 Patch1811: drm-nouveau-race-fix.patch
 Patch1812: drm-nouveau-nva3-noaccel.patch
+Patch1813: drm-nouveau-nv86-bug.patch
 Patch1819: drm-intel-big-hammer.patch
 # intel drm is all merged upstream
 Patch1824: drm-intel-next.patch
 # make sure the lvds comes back on lid open
 Patch1825: drm-intel-make-lvds-work.patch
 Patch1900: linux-2.6-intel-iommu-igfx.patch
-
 Patch2000: efifb-add-more-models.patch
 Patch2001: efifb-check-that-the-base-addr-is-plausible-on-pci-systems.patch
 
@@ -1334,6 +1334,7 @@ ApplyPatch drm-ttm-fix.patch
 ApplyPatch drm-nouveau-updates.patch
 ApplyPatch drm-nouveau-race-fix.patch
 ApplyPatch drm-nouveau-nva3-noaccel.patch
+ApplyPatch drm-nouveau-nv86-bug.patch
 
 ApplyPatch drm-intel-big-hammer.patch
 ApplyOptionalPatch drm-intel-next.patch


More information about the scm-commits mailing list