drm/nouveau/bios: fetch the vbios from PROM using only aligned 32-bit accesses
authorMartin Peres <martin.peres@free.fr>
Tue, 25 Mar 2014 21:23:23 +0000 (22:23 +0100)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 26 Mar 2014 04:08:26 +0000 (14:08 +1000)
Other kind of accesses are unreliable on Kepler cards. As advised by NVIDIA,
let's only use 32-bit accesses to fetch the vbios from PROM.

This fixes vbios fetching on my nve7 which failed in certain specific
conditions.

I suggest we Cc stable, for all kernels they still maintain after the big
rewrite.

Suggested-by: Christian Zander <czander@nvidia.com>
Signed-off-by: Martin Peres <martin.peres@free.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/subdev/bios/base.c

index 5608530..e9df94f 100644 (file)
@@ -157,6 +157,10 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
                pcireg = 0x001850;
        access = nv_mask(bios, pcireg, 0x00000001, 0x00000000);
 
+       /* WARNING: PROM accesses should always be 32-bits aligned. Other
+        * accesses work on most chipset but do not on Kepler chipsets
+        */
+
        /* bail if no rom signature, with a workaround for a PROM reading
         * issue on some chipsets.  the first read after a period of
         * inactivity returns the wrong result, so retry the first header
@@ -164,31 +168,32 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
         */
        i = 16;
        do {
-               if (nv_rd08(bios, 0x300000) == 0x55)
+               if ((nv_rd32(bios, 0x300000) & 0xffff) == 0xaa55)
                        break;
        } while (i--);
 
-       if (!i || nv_rd08(bios, 0x300001) != 0xaa)
-               goto out;
-
-       /* additional check (see note below) - read PCI record header */
-       pcir = nv_rd08(bios, 0x300018) |
-              nv_rd08(bios, 0x300019) << 8;
-       if (nv_rd08(bios, 0x300000 + pcir) != 'P' ||
-           nv_rd08(bios, 0x300001 + pcir) != 'C' ||
-           nv_rd08(bios, 0x300002 + pcir) != 'I' ||
-           nv_rd08(bios, 0x300003 + pcir) != 'R')
+       if (!i)
                goto out;
 
        /* read entire bios image to system memory */
-       bios->size = nv_rd08(bios, 0x300002) * 512;
+       bios->size = ((nv_rd32(bios, 0x300000) >> 16) & 0xff) * 512;
        if (!bios->size)
                goto out;
 
        bios->data = kmalloc(bios->size, GFP_KERNEL);
        if (bios->data) {
-               for (i = 0; i < bios->size; i++)
-                       nv_wo08(bios, i, nv_rd08(bios, 0x300000 + i));
+               for (i = 0; i < bios->size; i+=4)
+                       nv_wo32(bios, i, nv_rd32(bios, 0x300000 + i));
+       }
+
+       /* check the PCI record header */
+       pcir = nv_ro16(bios, 0x0018);
+       if (bios->data[pcir + 0] != 'P' ||
+           bios->data[pcir + 1] != 'C' ||
+           bios->data[pcir + 2] != 'I' ||
+           bios->data[pcir + 3] != 'R') {
+               bios->size = 0;
+               kfree(bios->data);
        }
 
 out: