aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/subdev/bios/base.c')
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/base.c369
1 files changed, 15 insertions, 354 deletions
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
index d45704a2c2df..7df3a273553d 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
@@ -31,6 +31,8 @@
#include <subdev/bios/bmp.h>
#include <subdev/bios/bit.h>
+#include "priv.h"
+
u8
nvbios_checksum(const u8 *data, int size)
{
@@ -56,362 +58,21 @@ nvbios_findstr(const u8 *data, int size, const char *str, int len)
return 0;
}
-#if defined(__powerpc__)
-static void
-nouveau_bios_shadow_of(struct nouveau_bios *bios)
+int
+nvbios_extend(struct nouveau_bios *bios, u32 length)
{
- struct pci_dev *pdev = nv_device(bios)->pdev;
- struct device_node *dn;
- const u32 *data;
- int size;
-
- dn = pci_device_to_OF_node(pdev);
- if (!dn) {
- nv_info(bios, "Unable to get the OF node\n");
- return;
- }
-
- data = of_get_property(dn, "NVDA,BMP", &size);
- if (data && size) {
- bios->size = size;
- bios->data = kmalloc(bios->size, GFP_KERNEL);
- if (bios->data)
- memcpy(bios->data, data, size);
- }
-}
-#endif
-
-static void
-nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
-{
- struct nouveau_device *device = nv_device(bios);
- u64 addr = 0;
- u32 bar0 = 0;
- int i;
-
- if (device->card_type >= NV_50) {
- if (device->card_type >= NV_C0 && device->card_type < GM100) {
- if (nv_rd32(bios, 0x022500) & 0x00000001)
- return;
- } else
- if (device->card_type >= GM100) {
- if (nv_rd32(bios, 0x021c04) & 0x00000001)
- return;
- }
-
- addr = nv_rd32(bios, 0x619f04);
- if (!(addr & 0x00000008)) {
- nv_debug(bios, "... not enabled\n");
- return;
+ if (bios->size < length) {
+ u8 *prev = bios->data;
+ if (!(bios->data = kmalloc(length, GFP_KERNEL))) {
+ bios->data = prev;
+ return -ENOMEM;
}
- if ( (addr & 0x00000003) != 1) {
- nv_debug(bios, "... not in vram\n");
- return;
- }
-
- addr = (addr & 0xffffff00) << 8;
- if (!addr) {
- addr = (u64)nv_rd32(bios, 0x001700) << 16;
- addr += 0xf0000;
- }
-
- bar0 = nv_mask(bios, 0x001700, 0xffffffff, addr >> 16);
- }
-
- /* bail if no rom signature */
- if (nv_rd08(bios, 0x700000) != 0x55 ||
- nv_rd08(bios, 0x700001) != 0xaa)
- goto out;
-
- bios->size = nv_rd08(bios, 0x700002) * 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, 0x700000 + i));
- }
-
-out:
- if (device->card_type >= NV_50)
- nv_wr32(bios, 0x001700, bar0);
-}
-
-static void
-nouveau_bios_shadow_prom(struct nouveau_bios *bios)
-{
- struct nouveau_device *device = nv_device(bios);
- u32 pcireg, access;
- u16 pcir;
- int i;
-
- /* there is no prom on nv4x IGP's */
- if (device->card_type == NV_40 && device->chipset >= 0x4c)
- return;
-
- /* enable access to rom */
- if (device->card_type >= NV_50)
- pcireg = 0x088050;
- else
- 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
- * byte a few times before giving up as a workaround
- */
- i = 16;
- do {
- u32 data = le32_to_cpu(nv_rd32(bios, 0x300000)) & 0xffff;
- if (data == 0xaa55)
- break;
- } while (i--);
-
- if (!i)
- goto out;
-
- /* read entire bios image to system memory */
- bios->size = (le32_to_cpu(nv_rd32(bios, 0x300000)) >> 16) & 0xff;
- bios->size = bios->size * 512;
- if (!bios->size)
- goto out;
-
- bios->data = kmalloc(bios->size, GFP_KERNEL);
- if (!bios->data)
- goto out;
-
- for (i = 0; i < bios->size; i += 4)
- ((u32 *)bios->data)[i/4] = 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:
- /* disable access to rom */
- nv_wr32(bios, pcireg, access);
-}
-
-#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
-int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
-bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
-#else
-static inline bool
-nouveau_acpi_rom_supported(struct pci_dev *pdev) {
- return false;
-}
-
-static inline int
-nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) {
- return -EINVAL;
-}
-#endif
-
-static void
-nouveau_bios_shadow_acpi(struct nouveau_bios *bios)
-{
- struct pci_dev *pdev = nv_device(bios)->pdev;
- int ret, cnt, i;
-
- if (!nouveau_acpi_rom_supported(pdev)) {
- bios->data = NULL;
- return;
- }
-
- bios->size = 0;
- bios->data = kmalloc(4096, GFP_KERNEL);
- if (bios->data) {
- if (nouveau_acpi_get_bios_chunk(bios->data, 0, 4096) == 4096)
- bios->size = bios->data[2] * 512;
- kfree(bios->data);
+ memcpy(bios->data, prev, bios->size);
+ bios->size = length;
+ kfree(prev);
+ return 1;
}
-
- if (!bios->size)
- return;
-
- bios->data = kmalloc(bios->size, GFP_KERNEL);
- if (bios->data) {
- /* disobey the acpi spec - much faster on at least w530 ... */
- ret = nouveau_acpi_get_bios_chunk(bios->data, 0, bios->size);
- if (ret != bios->size ||
- nvbios_checksum(bios->data, bios->size)) {
- /* ... that didn't work, ok, i'll be good now */
- for (i = 0; i < bios->size; i += cnt) {
- cnt = min((bios->size - i), (u32)4096);
- ret = nouveau_acpi_get_bios_chunk(bios->data, i, cnt);
- if (ret != cnt)
- break;
- }
- }
- }
-}
-
-static void
-nouveau_bios_shadow_pci(struct nouveau_bios *bios)
-{
- struct pci_dev *pdev = nv_device(bios)->pdev;
- size_t size;
-
- if (!pci_enable_rom(pdev)) {
- void __iomem *rom = pci_map_rom(pdev, &size);
- if (rom && size) {
- bios->data = kmalloc(size, GFP_KERNEL);
- if (bios->data) {
- memcpy_fromio(bios->data, rom, size);
- bios->size = size;
- }
- }
- if (rom)
- pci_unmap_rom(pdev, rom);
-
- pci_disable_rom(pdev);
- }
-}
-
-static void
-nouveau_bios_shadow_platform(struct nouveau_bios *bios)
-{
- struct pci_dev *pdev = nv_device(bios)->pdev;
- size_t size;
-
- void __iomem *rom = pci_platform_rom(pdev, &size);
- if (rom && size) {
- bios->data = kmalloc(size, GFP_KERNEL);
- if (bios->data) {
- memcpy_fromio(bios->data, rom, size);
- bios->size = size;
- }
- }
-}
-
-static int
-nouveau_bios_score(struct nouveau_bios *bios, const bool writeable)
-{
- if (bios->size < 3 || !bios->data || bios->data[0] != 0x55 ||
- bios->data[1] != 0xAA) {
- nv_info(bios, "... signature not found\n");
- return 0;
- }
-
- if (nvbios_checksum(bios->data,
- min_t(u32, bios->data[2] * 512, bios->size))) {
- nv_info(bios, "... checksum invalid\n");
- /* if a ro image is somewhat bad, it's probably all rubbish */
- return writeable ? 2 : 1;
- }
-
- nv_info(bios, "... appears to be valid\n");
- return 3;
-}
-
-struct methods {
- const char desc[16];
- void (*shadow)(struct nouveau_bios *);
- const bool rw;
- int score;
- u32 size;
- u8 *data;
-};
-
-static int
-nouveau_bios_shadow(struct nouveau_bios *bios)
-{
- struct methods shadow_methods[] = {
-#if defined(__powerpc__)
- { "OpenFirmware", nouveau_bios_shadow_of, true, 0, 0, NULL },
-#endif
- { "PRAMIN", nouveau_bios_shadow_pramin, true, 0, 0, NULL },
- { "PROM", nouveau_bios_shadow_prom, false, 0, 0, NULL },
- { "ACPI", nouveau_bios_shadow_acpi, true, 0, 0, NULL },
- { "PCIROM", nouveau_bios_shadow_pci, true, 0, 0, NULL },
- { "PLATFORM", nouveau_bios_shadow_platform, true, 0, 0, NULL },
- {}
- };
- struct methods *mthd, *best;
- const struct firmware *fw;
- const char *optarg;
- int optlen, ret;
- char *source;
-
- optarg = nouveau_stropt(nv_device(bios)->cfgopt, "NvBios", &optlen);
- source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
- if (source) {
- /* try to match one of the built-in methods */
- mthd = shadow_methods;
- do {
- if (strcasecmp(source, mthd->desc))
- continue;
- nv_info(bios, "source: %s\n", mthd->desc);
-
- mthd->shadow(bios);
- mthd->score = nouveau_bios_score(bios, mthd->rw);
- if (mthd->score) {
- kfree(source);
- return 0;
- }
- } while ((++mthd)->shadow);
-
- /* attempt to load firmware image */
- ret = request_firmware(&fw, source, &nv_device(bios)->pdev->dev);
- if (ret == 0) {
- bios->size = fw->size;
- bios->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
- release_firmware(fw);
-
- nv_info(bios, "image: %s\n", source);
- if (nouveau_bios_score(bios, 1)) {
- kfree(source);
- return 0;
- }
-
- kfree(bios->data);
- bios->data = NULL;
- }
-
- nv_error(bios, "source \'%s\' invalid\n", source);
- kfree(source);
- }
-
- mthd = shadow_methods;
- do {
- nv_info(bios, "checking %s for image...\n", mthd->desc);
- mthd->shadow(bios);
- mthd->score = nouveau_bios_score(bios, mthd->rw);
- mthd->size = bios->size;
- mthd->data = bios->data;
- bios->data = NULL;
- } while (mthd->score != 3 && (++mthd)->shadow);
-
- mthd = shadow_methods;
- best = mthd;
- do {
- if (mthd->score > best->score) {
- kfree(best->data);
- best = mthd;
- }
- } while ((++mthd)->shadow);
-
- if (best->score) {
- nv_info(bios, "using image from %s\n", best->desc);
- bios->size = best->size;
- bios->data = best->data;
- return 0;
- }
-
- nv_error(bios, "unable to locate usable image\n");
- return -EINVAL;
+ return 0;
}
static u8
@@ -472,7 +133,7 @@ nouveau_bios_ctor(struct nouveau_object *parent,
if (ret)
return ret;
- ret = nouveau_bios_shadow(bios);
+ ret = nvbios_shadow(bios);
if (ret)
return ret;