1d38ceaf9SAlex Deucher /* 2d38ceaf9SAlex Deucher * Copyright 2008 Advanced Micro Devices, Inc. 3d38ceaf9SAlex Deucher * Copyright 2008 Red Hat Inc. 4d38ceaf9SAlex Deucher * Copyright 2009 Jerome Glisse. 5d38ceaf9SAlex Deucher * 6d38ceaf9SAlex Deucher * Permission is hereby granted, free of charge, to any person obtaining a 7d38ceaf9SAlex Deucher * copy of this software and associated documentation files (the "Software"), 8d38ceaf9SAlex Deucher * to deal in the Software without restriction, including without limitation 9d38ceaf9SAlex Deucher * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10d38ceaf9SAlex Deucher * and/or sell copies of the Software, and to permit persons to whom the 11d38ceaf9SAlex Deucher * Software is furnished to do so, subject to the following conditions: 12d38ceaf9SAlex Deucher * 13d38ceaf9SAlex Deucher * The above copyright notice and this permission notice shall be included in 14d38ceaf9SAlex Deucher * all copies or substantial portions of the Software. 15d38ceaf9SAlex Deucher * 16d38ceaf9SAlex Deucher * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17d38ceaf9SAlex Deucher * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18d38ceaf9SAlex Deucher * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19d38ceaf9SAlex Deucher * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20d38ceaf9SAlex Deucher * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21d38ceaf9SAlex Deucher * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22d38ceaf9SAlex Deucher * OTHER DEALINGS IN THE SOFTWARE. 23d38ceaf9SAlex Deucher * 24d38ceaf9SAlex Deucher * Authors: Dave Airlie 25d38ceaf9SAlex Deucher * Alex Deucher 26d38ceaf9SAlex Deucher * Jerome Glisse 27d38ceaf9SAlex Deucher */ 28fdf2f6c5SSam Ravnborg 29d38ceaf9SAlex Deucher #include "amdgpu.h" 30d38ceaf9SAlex Deucher #include "atom.h" 31d38ceaf9SAlex Deucher 32fdf2f6c5SSam Ravnborg #include <linux/pci.h> 33d38ceaf9SAlex Deucher #include <linux/slab.h> 34d38ceaf9SAlex Deucher #include <linux/acpi.h> 35d38ceaf9SAlex Deucher /* 36d38ceaf9SAlex Deucher * BIOS. 37d38ceaf9SAlex Deucher */ 38d38ceaf9SAlex Deucher 39f930b2e8Smonk.liu #define AMD_VBIOS_SIGNATURE " 761295520" 40f930b2e8Smonk.liu #define AMD_VBIOS_SIGNATURE_OFFSET 0x30 41f930b2e8Smonk.liu #define AMD_VBIOS_SIGNATURE_SIZE sizeof(AMD_VBIOS_SIGNATURE) 42f930b2e8Smonk.liu #define AMD_VBIOS_SIGNATURE_END (AMD_VBIOS_SIGNATURE_OFFSET + AMD_VBIOS_SIGNATURE_SIZE) 43f930b2e8Smonk.liu #define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA) 44f930b2e8Smonk.liu #define AMD_VBIOS_LENGTH(p) ((p)[2] << 9) 45f930b2e8Smonk.liu 46919db4c1SKen Xue /* Check if current bios is an ATOM BIOS. 47919db4c1SKen Xue * Return true if it is ATOM BIOS. Otherwise, return false. 48919db4c1SKen Xue */ 49919db4c1SKen Xue static bool check_atom_bios(uint8_t *bios, size_t size) 50919db4c1SKen Xue { 51919db4c1SKen Xue uint16_t tmp, bios_header_start; 52919db4c1SKen Xue 53919db4c1SKen Xue if (!bios || size < 0x49) { 54919db4c1SKen Xue DRM_INFO("vbios mem is null or mem size is wrong\n"); 55919db4c1SKen Xue return false; 56919db4c1SKen Xue } 57919db4c1SKen Xue 58919db4c1SKen Xue if (!AMD_IS_VALID_VBIOS(bios)) { 59919db4c1SKen Xue DRM_INFO("BIOS signature incorrect %x %x\n", bios[0], bios[1]); 60919db4c1SKen Xue return false; 61919db4c1SKen Xue } 62919db4c1SKen Xue 63919db4c1SKen Xue bios_header_start = bios[0x48] | (bios[0x49] << 8); 64919db4c1SKen Xue if (!bios_header_start) { 65919db4c1SKen Xue DRM_INFO("Can't locate bios header\n"); 66919db4c1SKen Xue return false; 67919db4c1SKen Xue } 68919db4c1SKen Xue 69919db4c1SKen Xue tmp = bios_header_start + 4; 70919db4c1SKen Xue if (size < tmp) { 71919db4c1SKen Xue DRM_INFO("BIOS header is broken\n"); 72919db4c1SKen Xue return false; 73919db4c1SKen Xue } 74919db4c1SKen Xue 75919db4c1SKen Xue if (!memcmp(bios + tmp, "ATOM", 4) || 76919db4c1SKen Xue !memcmp(bios + tmp, "MOTA", 4)) { 77919db4c1SKen Xue DRM_DEBUG("ATOMBIOS detected\n"); 78919db4c1SKen Xue return true; 79919db4c1SKen Xue } 80919db4c1SKen Xue 81919db4c1SKen Xue return false; 82919db4c1SKen Xue } 83919db4c1SKen Xue 84d38ceaf9SAlex Deucher /* If you boot an IGP board with a discrete card as the primary, 85d38ceaf9SAlex Deucher * the IGP rom is not accessible via the rom bar as the IGP rom is 86d38ceaf9SAlex Deucher * part of the system bios. On boot, the system bios puts a 87d38ceaf9SAlex Deucher * copy of the igp rom at the start of vram if a discrete card is 88d38ceaf9SAlex Deucher * present. 89d38ceaf9SAlex Deucher */ 90d38ceaf9SAlex Deucher static bool igp_read_bios_from_vram(struct amdgpu_device *adev) 91d38ceaf9SAlex Deucher { 92d38ceaf9SAlex Deucher uint8_t __iomem *bios; 93d38ceaf9SAlex Deucher resource_size_t vram_base; 94d38ceaf9SAlex Deucher resource_size_t size = 256 * 1024; /* ??? */ 95d38ceaf9SAlex Deucher 962f7d10b3SJammy Zhou if (!(adev->flags & AMD_IS_APU)) 9739c640c0SAlex Deucher if (amdgpu_device_need_post(adev)) 98d38ceaf9SAlex Deucher return false; 99d38ceaf9SAlex Deucher 100f78313faSLijo Lazar /* FB BAR not enabled */ 101f78313faSLijo Lazar if (pci_resource_len(adev->pdev, 0) == 0) 102f78313faSLijo Lazar return false; 103f78313faSLijo Lazar 104d38ceaf9SAlex Deucher adev->bios = NULL; 105d38ceaf9SAlex Deucher vram_base = pci_resource_start(adev->pdev, 0); 106f31c94d1SXiangliang Yu bios = ioremap_wc(vram_base, size); 107*9e690184SSrinivasan Shanmugam if (!bios) 108d38ceaf9SAlex Deucher return false; 109d38ceaf9SAlex Deucher 110d38ceaf9SAlex Deucher adev->bios = kmalloc(size, GFP_KERNEL); 1113f12325aSRavikant B Sharma if (!adev->bios) { 112d38ceaf9SAlex Deucher iounmap(bios); 113d38ceaf9SAlex Deucher return false; 114d38ceaf9SAlex Deucher } 115a9f5db9cSEvan Quan adev->bios_size = size; 116d38ceaf9SAlex Deucher memcpy_fromio(adev->bios, bios, size); 117d38ceaf9SAlex Deucher iounmap(bios); 118919db4c1SKen Xue 119919db4c1SKen Xue if (!check_atom_bios(adev->bios, size)) { 120919db4c1SKen Xue kfree(adev->bios); 121919db4c1SKen Xue return false; 122919db4c1SKen Xue } 123919db4c1SKen Xue 124d38ceaf9SAlex Deucher return true; 125d38ceaf9SAlex Deucher } 126d38ceaf9SAlex Deucher 127d38ceaf9SAlex Deucher bool amdgpu_read_bios(struct amdgpu_device *adev) 128d38ceaf9SAlex Deucher { 129919db4c1SKen Xue uint8_t __iomem *bios; 130d38ceaf9SAlex Deucher size_t size; 131d38ceaf9SAlex Deucher 132d38ceaf9SAlex Deucher adev->bios = NULL; 133d38ceaf9SAlex Deucher /* XXX: some cards may return 0 for rom size? ddx has a workaround */ 134d38ceaf9SAlex Deucher bios = pci_map_rom(adev->pdev, &size); 135*9e690184SSrinivasan Shanmugam if (!bios) 136d38ceaf9SAlex Deucher return false; 137d38ceaf9SAlex Deucher 13818da4340SAlex Deucher adev->bios = kzalloc(size, GFP_KERNEL); 139d38ceaf9SAlex Deucher if (adev->bios == NULL) { 140d38ceaf9SAlex Deucher pci_unmap_rom(adev->pdev, bios); 141d38ceaf9SAlex Deucher return false; 142d38ceaf9SAlex Deucher } 143a9f5db9cSEvan Quan adev->bios_size = size; 14418da4340SAlex Deucher memcpy_fromio(adev->bios, bios, size); 145d38ceaf9SAlex Deucher pci_unmap_rom(adev->pdev, bios); 146919db4c1SKen Xue 147919db4c1SKen Xue if (!check_atom_bios(adev->bios, size)) { 148919db4c1SKen Xue kfree(adev->bios); 149919db4c1SKen Xue return false; 150919db4c1SKen Xue } 151919db4c1SKen Xue 152d38ceaf9SAlex Deucher return true; 153d38ceaf9SAlex Deucher } 154d38ceaf9SAlex Deucher 155f930b2e8Smonk.liu static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) 156f930b2e8Smonk.liu { 157f930b2e8Smonk.liu u8 header[AMD_VBIOS_SIGNATURE_END+1] = {0}; 158f930b2e8Smonk.liu int len; 159f930b2e8Smonk.liu 16035c3c89aSJiawei Gu if (!adev->asic_funcs || !adev->asic_funcs->read_bios_from_rom) 161f930b2e8Smonk.liu return false; 162f930b2e8Smonk.liu 163f930b2e8Smonk.liu /* validate VBIOS signature */ 164f930b2e8Smonk.liu if (amdgpu_asic_read_bios_from_rom(adev, &header[0], sizeof(header)) == false) 165f930b2e8Smonk.liu return false; 166f930b2e8Smonk.liu header[AMD_VBIOS_SIGNATURE_END] = 0; 167f930b2e8Smonk.liu 168f930b2e8Smonk.liu if ((!AMD_IS_VALID_VBIOS(header)) || 169*9e690184SSrinivasan Shanmugam memcmp((char *)&header[AMD_VBIOS_SIGNATURE_OFFSET], 170f930b2e8Smonk.liu AMD_VBIOS_SIGNATURE, 171*9e690184SSrinivasan Shanmugam strlen(AMD_VBIOS_SIGNATURE)) != 0) 172f930b2e8Smonk.liu return false; 173f930b2e8Smonk.liu 174f930b2e8Smonk.liu /* valid vbios, go on */ 175f930b2e8Smonk.liu len = AMD_VBIOS_LENGTH(header); 176f930b2e8Smonk.liu len = ALIGN(len, 4); 177f930b2e8Smonk.liu adev->bios = kmalloc(len, GFP_KERNEL); 178f930b2e8Smonk.liu if (!adev->bios) { 179f930b2e8Smonk.liu DRM_ERROR("no memory to allocate for BIOS\n"); 180f930b2e8Smonk.liu return false; 181f930b2e8Smonk.liu } 182a9f5db9cSEvan Quan adev->bios_size = len; 183f930b2e8Smonk.liu 184f930b2e8Smonk.liu /* read complete BIOS */ 185919db4c1SKen Xue amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); 186919db4c1SKen Xue 187919db4c1SKen Xue if (!check_atom_bios(adev->bios, len)) { 188919db4c1SKen Xue kfree(adev->bios); 189919db4c1SKen Xue return false; 190919db4c1SKen Xue } 191919db4c1SKen Xue 192919db4c1SKen Xue return true; 193f930b2e8Smonk.liu } 194f930b2e8Smonk.liu 195d38ceaf9SAlex Deucher static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) 196d38ceaf9SAlex Deucher { 19772e0ef0eSMikel Rychliski phys_addr_t rom = adev->pdev->rom; 19872e0ef0eSMikel Rychliski size_t romlen = adev->pdev->romlen; 19972e0ef0eSMikel Rychliski void __iomem *bios; 200d38ceaf9SAlex Deucher 201d38ceaf9SAlex Deucher adev->bios = NULL; 202d38ceaf9SAlex Deucher 20372e0ef0eSMikel Rychliski if (!rom || romlen == 0) 204919db4c1SKen Xue return false; 205919db4c1SKen Xue 20672e0ef0eSMikel Rychliski adev->bios = kzalloc(romlen, GFP_KERNEL); 20772e0ef0eSMikel Rychliski if (!adev->bios) 208d38ceaf9SAlex Deucher return false; 209919db4c1SKen Xue 21072e0ef0eSMikel Rychliski bios = ioremap(rom, romlen); 21172e0ef0eSMikel Rychliski if (!bios) 21272e0ef0eSMikel Rychliski goto free_bios; 21372e0ef0eSMikel Rychliski 21472e0ef0eSMikel Rychliski memcpy_fromio(adev->bios, bios, romlen); 21572e0ef0eSMikel Rychliski iounmap(bios); 21672e0ef0eSMikel Rychliski 21772e0ef0eSMikel Rychliski if (!check_atom_bios(adev->bios, romlen)) 21872e0ef0eSMikel Rychliski goto free_bios; 21972e0ef0eSMikel Rychliski 22072e0ef0eSMikel Rychliski adev->bios_size = romlen; 221d38ceaf9SAlex Deucher 222d38ceaf9SAlex Deucher return true; 22372e0ef0eSMikel Rychliski free_bios: 22472e0ef0eSMikel Rychliski kfree(adev->bios); 22572e0ef0eSMikel Rychliski return false; 226d38ceaf9SAlex Deucher } 227d38ceaf9SAlex Deucher 228d38ceaf9SAlex Deucher #ifdef CONFIG_ACPI 229d38ceaf9SAlex Deucher /* ATRM is used to get the BIOS on the discrete cards in 230d38ceaf9SAlex Deucher * dual-gpu systems. 231d38ceaf9SAlex Deucher */ 232d38ceaf9SAlex Deucher /* retrieve the ROM in 4k blocks */ 233d38ceaf9SAlex Deucher #define ATRM_BIOS_PAGE 4096 234d38ceaf9SAlex Deucher /** 235d38ceaf9SAlex Deucher * amdgpu_atrm_call - fetch a chunk of the vbios 236d38ceaf9SAlex Deucher * 237d38ceaf9SAlex Deucher * @atrm_handle: acpi ATRM handle 238d38ceaf9SAlex Deucher * @bios: vbios image pointer 239d38ceaf9SAlex Deucher * @offset: offset of vbios image data to fetch 240d38ceaf9SAlex Deucher * @len: length of vbios image data to fetch 241d38ceaf9SAlex Deucher * 242d38ceaf9SAlex Deucher * Executes ATRM to fetch a chunk of the discrete 243d38ceaf9SAlex Deucher * vbios image on PX systems (all asics). 244d38ceaf9SAlex Deucher * Returns the length of the buffer fetched. 245d38ceaf9SAlex Deucher */ 246d38ceaf9SAlex Deucher static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, 247d38ceaf9SAlex Deucher int offset, int len) 248d38ceaf9SAlex Deucher { 249d38ceaf9SAlex Deucher acpi_status status; 250d38ceaf9SAlex Deucher union acpi_object atrm_arg_elements[2], *obj; 251d38ceaf9SAlex Deucher struct acpi_object_list atrm_arg; 252d38ceaf9SAlex Deucher struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 253d38ceaf9SAlex Deucher 254d38ceaf9SAlex Deucher atrm_arg.count = 2; 255d38ceaf9SAlex Deucher atrm_arg.pointer = &atrm_arg_elements[0]; 256d38ceaf9SAlex Deucher 257d38ceaf9SAlex Deucher atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; 258d38ceaf9SAlex Deucher atrm_arg_elements[0].integer.value = offset; 259d38ceaf9SAlex Deucher 260d38ceaf9SAlex Deucher atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; 261d38ceaf9SAlex Deucher atrm_arg_elements[1].integer.value = len; 262d38ceaf9SAlex Deucher 263d38ceaf9SAlex Deucher status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); 264d38ceaf9SAlex Deucher if (ACPI_FAILURE(status)) { 265*9e690184SSrinivasan Shanmugam DRM_ERROR("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); 266d38ceaf9SAlex Deucher return -ENODEV; 267d38ceaf9SAlex Deucher } 268d38ceaf9SAlex Deucher 269d38ceaf9SAlex Deucher obj = (union acpi_object *)buffer.pointer; 270d38ceaf9SAlex Deucher memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); 271d38ceaf9SAlex Deucher len = obj->buffer.length; 272d38ceaf9SAlex Deucher kfree(buffer.pointer); 273d38ceaf9SAlex Deucher return len; 274d38ceaf9SAlex Deucher } 275d38ceaf9SAlex Deucher 276d38ceaf9SAlex Deucher static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) 277d38ceaf9SAlex Deucher { 278d38ceaf9SAlex Deucher int ret; 279d38ceaf9SAlex Deucher int size = 256 * 1024; 280d38ceaf9SAlex Deucher int i; 281d38ceaf9SAlex Deucher struct pci_dev *pdev = NULL; 282d38ceaf9SAlex Deucher acpi_handle dhandle, atrm_handle; 283d38ceaf9SAlex Deucher acpi_status status; 284d38ceaf9SAlex Deucher bool found = false; 285d38ceaf9SAlex Deucher 286d38ceaf9SAlex Deucher /* ATRM is for the discrete card only */ 2872f7d10b3SJammy Zhou if (adev->flags & AMD_IS_APU) 288d38ceaf9SAlex Deucher return false; 289d38ceaf9SAlex Deucher 290d38ceaf9SAlex Deucher while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 291d38ceaf9SAlex Deucher dhandle = ACPI_HANDLE(&pdev->dev); 292d38ceaf9SAlex Deucher if (!dhandle) 293d38ceaf9SAlex Deucher continue; 294d38ceaf9SAlex Deucher 295d38ceaf9SAlex Deucher status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); 29610e92724SBjorn Helgaas if (ACPI_SUCCESS(status)) { 297d38ceaf9SAlex Deucher found = true; 298d38ceaf9SAlex Deucher break; 299d38ceaf9SAlex Deucher } 300d38ceaf9SAlex Deucher } 301d38ceaf9SAlex Deucher 302d38ceaf9SAlex Deucher if (!found) { 303d38ceaf9SAlex Deucher while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { 304d38ceaf9SAlex Deucher dhandle = ACPI_HANDLE(&pdev->dev); 305d38ceaf9SAlex Deucher if (!dhandle) 306d38ceaf9SAlex Deucher continue; 307d38ceaf9SAlex Deucher 308d38ceaf9SAlex Deucher status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); 30910e92724SBjorn Helgaas if (ACPI_SUCCESS(status)) { 310d38ceaf9SAlex Deucher found = true; 311d38ceaf9SAlex Deucher break; 312d38ceaf9SAlex Deucher } 313d38ceaf9SAlex Deucher } 314d38ceaf9SAlex Deucher } 315d38ceaf9SAlex Deucher 316d38ceaf9SAlex Deucher if (!found) 317d38ceaf9SAlex Deucher return false; 318ca54639cSXiongfeng Wang pci_dev_put(pdev); 319d38ceaf9SAlex Deucher 320d38ceaf9SAlex Deucher adev->bios = kmalloc(size, GFP_KERNEL); 321d38ceaf9SAlex Deucher if (!adev->bios) { 322ec1e80f0SFeifei Xu dev_err(adev->dev, "Unable to allocate bios\n"); 323d38ceaf9SAlex Deucher return false; 324d38ceaf9SAlex Deucher } 325d38ceaf9SAlex Deucher 326d38ceaf9SAlex Deucher for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { 327d38ceaf9SAlex Deucher ret = amdgpu_atrm_call(atrm_handle, 328d38ceaf9SAlex Deucher adev->bios, 329d38ceaf9SAlex Deucher (i * ATRM_BIOS_PAGE), 330d38ceaf9SAlex Deucher ATRM_BIOS_PAGE); 331d38ceaf9SAlex Deucher if (ret < ATRM_BIOS_PAGE) 332d38ceaf9SAlex Deucher break; 333d38ceaf9SAlex Deucher } 334d38ceaf9SAlex Deucher 335919db4c1SKen Xue if (!check_atom_bios(adev->bios, size)) { 336d38ceaf9SAlex Deucher kfree(adev->bios); 337d38ceaf9SAlex Deucher return false; 338d38ceaf9SAlex Deucher } 339a9f5db9cSEvan Quan adev->bios_size = size; 340d38ceaf9SAlex Deucher return true; 341d38ceaf9SAlex Deucher } 342d38ceaf9SAlex Deucher #else 343d38ceaf9SAlex Deucher static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) 344d38ceaf9SAlex Deucher { 345d38ceaf9SAlex Deucher return false; 346d38ceaf9SAlex Deucher } 347d38ceaf9SAlex Deucher #endif 348d38ceaf9SAlex Deucher 349d38ceaf9SAlex Deucher static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) 350d38ceaf9SAlex Deucher { 3512f7d10b3SJammy Zhou if (adev->flags & AMD_IS_APU) 352d38ceaf9SAlex Deucher return igp_read_bios_from_vram(adev); 353d38ceaf9SAlex Deucher else 35435c3c89aSJiawei Gu return (!adev->asic_funcs || !adev->asic_funcs->read_disabled_bios) ? 35535c3c89aSJiawei Gu false : amdgpu_asic_read_disabled_bios(adev); 356d38ceaf9SAlex Deucher } 357d38ceaf9SAlex Deucher 358d38ceaf9SAlex Deucher #ifdef CONFIG_ACPI 359d38ceaf9SAlex Deucher static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) 360d38ceaf9SAlex Deucher { 361d38ceaf9SAlex Deucher struct acpi_table_header *hdr; 362d38ceaf9SAlex Deucher acpi_size tbl_size; 363d38ceaf9SAlex Deucher UEFI_ACPI_VFCT *vfct; 364*9e690184SSrinivasan Shanmugam unsigned int offset; 365d38ceaf9SAlex Deucher 3666b11d1d6SLv Zheng if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) 367d38ceaf9SAlex Deucher return false; 3686b11d1d6SLv Zheng tbl_size = hdr->length; 369d38ceaf9SAlex Deucher if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { 370ec1e80f0SFeifei Xu dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n"); 371919db4c1SKen Xue return false; 372d38ceaf9SAlex Deucher } 373d38ceaf9SAlex Deucher 374d38ceaf9SAlex Deucher vfct = (UEFI_ACPI_VFCT *)hdr; 37517ed9be8SAlex Deucher offset = vfct->VBIOSImageOffset; 37617ed9be8SAlex Deucher 37717ed9be8SAlex Deucher while (offset < tbl_size) { 37817ed9be8SAlex Deucher GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset); 37917ed9be8SAlex Deucher VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader; 38017ed9be8SAlex Deucher 38117ed9be8SAlex Deucher offset += sizeof(VFCT_IMAGE_HEADER); 38217ed9be8SAlex Deucher if (offset > tbl_size) { 383ec1e80f0SFeifei Xu dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n"); 384919db4c1SKen Xue return false; 385d38ceaf9SAlex Deucher } 386d38ceaf9SAlex Deucher 38717ed9be8SAlex Deucher offset += vhdr->ImageLength; 38817ed9be8SAlex Deucher if (offset > tbl_size) { 389ec1e80f0SFeifei Xu dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n"); 390919db4c1SKen Xue return false; 391d38ceaf9SAlex Deucher } 392d38ceaf9SAlex Deucher 39317ed9be8SAlex Deucher if (vhdr->ImageLength && 39417ed9be8SAlex Deucher vhdr->PCIBus == adev->pdev->bus->number && 39517ed9be8SAlex Deucher vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && 39617ed9be8SAlex Deucher vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && 39717ed9be8SAlex Deucher vhdr->VendorID == adev->pdev->vendor && 39817ed9be8SAlex Deucher vhdr->DeviceID == adev->pdev->device) { 399919db4c1SKen Xue adev->bios = kmemdup(&vbios->VbiosContent, 400919db4c1SKen Xue vhdr->ImageLength, 401919db4c1SKen Xue GFP_KERNEL); 402d38ceaf9SAlex Deucher 403919db4c1SKen Xue if (!check_atom_bios(adev->bios, vhdr->ImageLength)) { 404919db4c1SKen Xue kfree(adev->bios); 405919db4c1SKen Xue return false; 406919db4c1SKen Xue } 407919db4c1SKen Xue adev->bios_size = vhdr->ImageLength; 408919db4c1SKen Xue return true; 409d38ceaf9SAlex Deucher } 41017ed9be8SAlex Deucher } 41117ed9be8SAlex Deucher 412ec1e80f0SFeifei Xu dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); 41317ed9be8SAlex Deucher return false; 41417ed9be8SAlex Deucher } 415d38ceaf9SAlex Deucher #else 416d38ceaf9SAlex Deucher static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) 417d38ceaf9SAlex Deucher { 418d38ceaf9SAlex Deucher return false; 419d38ceaf9SAlex Deucher } 420d38ceaf9SAlex Deucher #endif 421d38ceaf9SAlex Deucher 422d38ceaf9SAlex Deucher bool amdgpu_get_bios(struct amdgpu_device *adev) 423d38ceaf9SAlex Deucher { 424bddbacc9SAlex Deucher if (amdgpu_atrm_get_bios(adev)) { 425bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from ATRM\n"); 4260cdd5005SAlex Deucher goto success; 427bddbacc9SAlex Deucher } 428919db4c1SKen Xue 429bddbacc9SAlex Deucher if (amdgpu_acpi_vfct_bios(adev)) { 430bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from VFCT\n"); 4310cdd5005SAlex Deucher goto success; 432bddbacc9SAlex Deucher } 433919db4c1SKen Xue 434bddbacc9SAlex Deucher if (igp_read_bios_from_vram(adev)) { 435bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from VRAM BAR\n"); 4360cdd5005SAlex Deucher goto success; 437bddbacc9SAlex Deucher } 438919db4c1SKen Xue 439bddbacc9SAlex Deucher if (amdgpu_read_bios(adev)) { 440bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from ROM BAR\n"); 4410cdd5005SAlex Deucher goto success; 442bddbacc9SAlex Deucher } 443919db4c1SKen Xue 444bddbacc9SAlex Deucher if (amdgpu_read_bios_from_rom(adev)) { 445bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from ROM\n"); 4460cdd5005SAlex Deucher goto success; 447bddbacc9SAlex Deucher } 448919db4c1SKen Xue 449bddbacc9SAlex Deucher if (amdgpu_read_disabled_bios(adev)) { 450bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from disabled ROM BAR\n"); 4510cdd5005SAlex Deucher goto success; 452bddbacc9SAlex Deucher } 453919db4c1SKen Xue 454bddbacc9SAlex Deucher if (amdgpu_read_platform_bios(adev)) { 455bddbacc9SAlex Deucher dev_info(adev->dev, "Fetched VBIOS from platform\n"); 4560cdd5005SAlex Deucher goto success; 457bddbacc9SAlex Deucher } 458919db4c1SKen Xue 459ec1e80f0SFeifei Xu dev_err(adev->dev, "Unable to locate a BIOS ROM\n"); 460d38ceaf9SAlex Deucher return false; 4610cdd5005SAlex Deucher 4620cdd5005SAlex Deucher success: 4636c885554SAlex Deucher adev->is_atom_fw = (adev->asic_type >= CHIP_VEGA10) ? true : false; 4640cdd5005SAlex Deucher return true; 465d38ceaf9SAlex Deucher } 46604022982SHawking Zhang 46704022982SHawking Zhang /* helper function for soc15 and onwards to read bios from rom */ 46804022982SHawking Zhang bool amdgpu_soc15_read_bios_from_rom(struct amdgpu_device *adev, 46904022982SHawking Zhang u8 *bios, u32 length_bytes) 47004022982SHawking Zhang { 47104022982SHawking Zhang u32 *dw_ptr; 47204022982SHawking Zhang u32 i, length_dw; 473fb1d6835SHawking Zhang u32 rom_offset; 47404022982SHawking Zhang u32 rom_index_offset; 47504022982SHawking Zhang u32 rom_data_offset; 47604022982SHawking Zhang 47704022982SHawking Zhang if (bios == NULL) 47804022982SHawking Zhang return false; 47904022982SHawking Zhang if (length_bytes == 0) 48004022982SHawking Zhang return false; 48104022982SHawking Zhang /* APU vbios image is part of sbios image */ 48204022982SHawking Zhang if (adev->flags & AMD_IS_APU) 48304022982SHawking Zhang return false; 48404022982SHawking Zhang if (!adev->smuio.funcs || 48504022982SHawking Zhang !adev->smuio.funcs->get_rom_index_offset || 48604022982SHawking Zhang !adev->smuio.funcs->get_rom_data_offset) 48704022982SHawking Zhang return false; 48804022982SHawking Zhang 48904022982SHawking Zhang dw_ptr = (u32 *)bios; 49004022982SHawking Zhang length_dw = ALIGN(length_bytes, 4) / 4; 49104022982SHawking Zhang 49204022982SHawking Zhang rom_index_offset = 49304022982SHawking Zhang adev->smuio.funcs->get_rom_index_offset(adev); 49404022982SHawking Zhang rom_data_offset = 49504022982SHawking Zhang adev->smuio.funcs->get_rom_data_offset(adev); 49604022982SHawking Zhang 497fb1d6835SHawking Zhang if (adev->nbio.funcs && 498fb1d6835SHawking Zhang adev->nbio.funcs->get_rom_offset) { 499fb1d6835SHawking Zhang rom_offset = adev->nbio.funcs->get_rom_offset(adev); 500fb1d6835SHawking Zhang rom_offset = rom_offset << 17; 501fb1d6835SHawking Zhang } else { 502fb1d6835SHawking Zhang rom_offset = 0; 503fb1d6835SHawking Zhang } 504fb1d6835SHawking Zhang 505fb1d6835SHawking Zhang /* set rom index to rom_offset */ 506fb1d6835SHawking Zhang WREG32(rom_index_offset, rom_offset); 50704022982SHawking Zhang /* read out the rom data */ 50804022982SHawking Zhang for (i = 0; i < length_dw; i++) 50904022982SHawking Zhang dw_ptr[i] = RREG32(rom_data_offset); 51004022982SHawking Zhang 51104022982SHawking Zhang return true; 51204022982SHawking Zhang } 513