Commit 18acc6d8 authored by Martin Peres's avatar Martin Peres Committed by Ben Skeggs

drm/nouveau/bios: fetch the vbios from PROM using only aligned 32-bit accesses

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: default avatarChristian Zander <czander@nvidia.com>
Signed-off-by: default avatarMartin Peres <martin.peres@free.fr>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 0e994d64
...@@ -157,6 +157,10 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) ...@@ -157,6 +157,10 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
pcireg = 0x001850; pcireg = 0x001850;
access = nv_mask(bios, pcireg, 0x00000001, 0x00000000); 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 /* bail if no rom signature, with a workaround for a PROM reading
* issue on some chipsets. the first read after a period of * issue on some chipsets. the first read after a period of
* inactivity returns the wrong result, so retry the first header * inactivity returns the wrong result, so retry the first header
...@@ -164,31 +168,32 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) ...@@ -164,31 +168,32 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
*/ */
i = 16; i = 16;
do { do {
if (nv_rd08(bios, 0x300000) == 0x55) if ((nv_rd32(bios, 0x300000) & 0xffff) == 0xaa55)
break; break;
} while (i--); } while (i--);
if (!i || nv_rd08(bios, 0x300001) != 0xaa) if (!i)
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')
goto out; goto out;
/* read entire bios image to system memory */ /* 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) if (!bios->size)
goto out; goto out;
bios->data = kmalloc(bios->size, GFP_KERNEL); bios->data = kmalloc(bios->size, GFP_KERNEL);
if (bios->data) { if (bios->data) {
for (i = 0; i < bios->size; i++) for (i = 0; i < bios->size; i+=4)
nv_wo08(bios, i, nv_rd08(bios, 0x300000 + i)); 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: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment