162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * Copyright 2009 Jerome Glisse. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1462306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1762306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1862306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1962306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 2062306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2162306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2262306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Authors: Dave Airlie 2562306a36Sopenharmony_ci * Alex Deucher 2662306a36Sopenharmony_ci * Jerome Glisse 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "amdgpu.h" 3062306a36Sopenharmony_ci#include "atom.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/device.h> 3362306a36Sopenharmony_ci#include <linux/pci.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/acpi.h> 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * BIOS. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define AMD_VBIOS_SIGNATURE " 761295520" 4162306a36Sopenharmony_ci#define AMD_VBIOS_SIGNATURE_OFFSET 0x30 4262306a36Sopenharmony_ci#define AMD_VBIOS_SIGNATURE_SIZE sizeof(AMD_VBIOS_SIGNATURE) 4362306a36Sopenharmony_ci#define AMD_VBIOS_SIGNATURE_END (AMD_VBIOS_SIGNATURE_OFFSET + AMD_VBIOS_SIGNATURE_SIZE) 4462306a36Sopenharmony_ci#define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA) 4562306a36Sopenharmony_ci#define AMD_VBIOS_LENGTH(p) ((p)[2] << 9) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Check if current bios is an ATOM BIOS. 4862306a36Sopenharmony_ci * Return true if it is ATOM BIOS. Otherwise, return false. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic bool check_atom_bios(uint8_t *bios, size_t size) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci uint16_t tmp, bios_header_start; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!bios || size < 0x49) { 5562306a36Sopenharmony_ci DRM_INFO("vbios mem is null or mem size is wrong\n"); 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!AMD_IS_VALID_VBIOS(bios)) { 6062306a36Sopenharmony_ci DRM_INFO("BIOS signature incorrect %x %x\n", bios[0], bios[1]); 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci bios_header_start = bios[0x48] | (bios[0x49] << 8); 6562306a36Sopenharmony_ci if (!bios_header_start) { 6662306a36Sopenharmony_ci DRM_INFO("Can't locate bios header\n"); 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci tmp = bios_header_start + 4; 7162306a36Sopenharmony_ci if (size < tmp) { 7262306a36Sopenharmony_ci DRM_INFO("BIOS header is broken\n"); 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!memcmp(bios + tmp, "ATOM", 4) || 7762306a36Sopenharmony_ci !memcmp(bios + tmp, "MOTA", 4)) { 7862306a36Sopenharmony_ci DRM_DEBUG("ATOMBIOS detected\n"); 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return false; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* If you boot an IGP board with a discrete card as the primary, 8662306a36Sopenharmony_ci * the IGP rom is not accessible via the rom bar as the IGP rom is 8762306a36Sopenharmony_ci * part of the system bios. On boot, the system bios puts a 8862306a36Sopenharmony_ci * copy of the igp rom at the start of vram if a discrete card is 8962306a36Sopenharmony_ci * present. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic bool igp_read_bios_from_vram(struct amdgpu_device *adev) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci uint8_t __iomem *bios; 9462306a36Sopenharmony_ci resource_size_t vram_base; 9562306a36Sopenharmony_ci resource_size_t size = 256 * 1024; /* ??? */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!(adev->flags & AMD_IS_APU)) 9862306a36Sopenharmony_ci if (amdgpu_device_need_post(adev)) 9962306a36Sopenharmony_ci return false; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* FB BAR not enabled */ 10262306a36Sopenharmony_ci if (pci_resource_len(adev->pdev, 0) == 0) 10362306a36Sopenharmony_ci return false; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci adev->bios = NULL; 10662306a36Sopenharmony_ci vram_base = pci_resource_start(adev->pdev, 0); 10762306a36Sopenharmony_ci bios = ioremap_wc(vram_base, size); 10862306a36Sopenharmony_ci if (!bios) 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci adev->bios = kmalloc(size, GFP_KERNEL); 11262306a36Sopenharmony_ci if (!adev->bios) { 11362306a36Sopenharmony_ci iounmap(bios); 11462306a36Sopenharmony_ci return false; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci adev->bios_size = size; 11762306a36Sopenharmony_ci memcpy_fromio(adev->bios, bios, size); 11862306a36Sopenharmony_ci iounmap(bios); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, size)) { 12162306a36Sopenharmony_ci kfree(adev->bios); 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return true; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cibool amdgpu_read_bios(struct amdgpu_device *adev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci uint8_t __iomem *bios; 13162306a36Sopenharmony_ci size_t size; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci adev->bios = NULL; 13462306a36Sopenharmony_ci /* XXX: some cards may return 0 for rom size? ddx has a workaround */ 13562306a36Sopenharmony_ci bios = pci_map_rom(adev->pdev, &size); 13662306a36Sopenharmony_ci if (!bios) 13762306a36Sopenharmony_ci return false; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci adev->bios = kzalloc(size, GFP_KERNEL); 14062306a36Sopenharmony_ci if (adev->bios == NULL) { 14162306a36Sopenharmony_ci pci_unmap_rom(adev->pdev, bios); 14262306a36Sopenharmony_ci return false; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci adev->bios_size = size; 14562306a36Sopenharmony_ci memcpy_fromio(adev->bios, bios, size); 14662306a36Sopenharmony_ci pci_unmap_rom(adev->pdev, bios); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, size)) { 14962306a36Sopenharmony_ci kfree(adev->bios); 15062306a36Sopenharmony_ci return false; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return true; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u8 header[AMD_VBIOS_SIGNATURE_END+1] = {0}; 15962306a36Sopenharmony_ci int len; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!adev->asic_funcs || !adev->asic_funcs->read_bios_from_rom) 16262306a36Sopenharmony_ci return false; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* validate VBIOS signature */ 16562306a36Sopenharmony_ci if (amdgpu_asic_read_bios_from_rom(adev, &header[0], sizeof(header)) == false) 16662306a36Sopenharmony_ci return false; 16762306a36Sopenharmony_ci header[AMD_VBIOS_SIGNATURE_END] = 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if ((!AMD_IS_VALID_VBIOS(header)) || 17062306a36Sopenharmony_ci memcmp((char *)&header[AMD_VBIOS_SIGNATURE_OFFSET], 17162306a36Sopenharmony_ci AMD_VBIOS_SIGNATURE, 17262306a36Sopenharmony_ci strlen(AMD_VBIOS_SIGNATURE)) != 0) 17362306a36Sopenharmony_ci return false; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* valid vbios, go on */ 17662306a36Sopenharmony_ci len = AMD_VBIOS_LENGTH(header); 17762306a36Sopenharmony_ci len = ALIGN(len, 4); 17862306a36Sopenharmony_ci adev->bios = kmalloc(len, GFP_KERNEL); 17962306a36Sopenharmony_ci if (!adev->bios) { 18062306a36Sopenharmony_ci DRM_ERROR("no memory to allocate for BIOS\n"); 18162306a36Sopenharmony_ci return false; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci adev->bios_size = len; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* read complete BIOS */ 18662306a36Sopenharmony_ci amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, len)) { 18962306a36Sopenharmony_ci kfree(adev->bios); 19062306a36Sopenharmony_ci return false; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return true; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic bool amdgpu_read_platform_bios(struct amdgpu_device *adev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci phys_addr_t rom = adev->pdev->rom; 19962306a36Sopenharmony_ci size_t romlen = adev->pdev->romlen; 20062306a36Sopenharmony_ci void __iomem *bios; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci adev->bios = NULL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!rom || romlen == 0) 20562306a36Sopenharmony_ci return false; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci adev->bios = kzalloc(romlen, GFP_KERNEL); 20862306a36Sopenharmony_ci if (!adev->bios) 20962306a36Sopenharmony_ci return false; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci bios = ioremap(rom, romlen); 21262306a36Sopenharmony_ci if (!bios) 21362306a36Sopenharmony_ci goto free_bios; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci memcpy_fromio(adev->bios, bios, romlen); 21662306a36Sopenharmony_ci iounmap(bios); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, romlen)) 21962306a36Sopenharmony_ci goto free_bios; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci adev->bios_size = romlen; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return true; 22462306a36Sopenharmony_cifree_bios: 22562306a36Sopenharmony_ci kfree(adev->bios); 22662306a36Sopenharmony_ci return false; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#ifdef CONFIG_ACPI 23062306a36Sopenharmony_ci/* ATRM is used to get the BIOS on the discrete cards in 23162306a36Sopenharmony_ci * dual-gpu systems. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci/* retrieve the ROM in 4k blocks */ 23462306a36Sopenharmony_ci#define ATRM_BIOS_PAGE 4096 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * amdgpu_atrm_call - fetch a chunk of the vbios 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * @atrm_handle: acpi ATRM handle 23962306a36Sopenharmony_ci * @bios: vbios image pointer 24062306a36Sopenharmony_ci * @offset: offset of vbios image data to fetch 24162306a36Sopenharmony_ci * @len: length of vbios image data to fetch 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Executes ATRM to fetch a chunk of the discrete 24462306a36Sopenharmony_ci * vbios image on PX systems (all asics). 24562306a36Sopenharmony_ci * Returns the length of the buffer fetched. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, 24862306a36Sopenharmony_ci int offset, int len) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci acpi_status status; 25162306a36Sopenharmony_ci union acpi_object atrm_arg_elements[2], *obj; 25262306a36Sopenharmony_ci struct acpi_object_list atrm_arg; 25362306a36Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci atrm_arg.count = 2; 25662306a36Sopenharmony_ci atrm_arg.pointer = &atrm_arg_elements[0]; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; 25962306a36Sopenharmony_ci atrm_arg_elements[0].integer.value = offset; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; 26262306a36Sopenharmony_ci atrm_arg_elements[1].integer.value = len; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); 26562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 26662306a36Sopenharmony_ci DRM_ERROR("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); 26762306a36Sopenharmony_ci return -ENODEV; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci obj = (union acpi_object *)buffer.pointer; 27162306a36Sopenharmony_ci memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); 27262306a36Sopenharmony_ci len = obj->buffer.length; 27362306a36Sopenharmony_ci kfree(buffer.pointer); 27462306a36Sopenharmony_ci return len; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci int size = 256 * 1024; 28162306a36Sopenharmony_ci int i; 28262306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 28362306a36Sopenharmony_ci acpi_handle dhandle, atrm_handle; 28462306a36Sopenharmony_ci acpi_status status; 28562306a36Sopenharmony_ci bool found = false; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* ATRM is for the discrete card only */ 28862306a36Sopenharmony_ci if (adev->flags & AMD_IS_APU) 28962306a36Sopenharmony_ci return false; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* ATRM is for on-platform devices only */ 29262306a36Sopenharmony_ci if (dev_is_removable(&adev->pdev->dev)) 29362306a36Sopenharmony_ci return false; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 29662306a36Sopenharmony_ci dhandle = ACPI_HANDLE(&pdev->dev); 29762306a36Sopenharmony_ci if (!dhandle) 29862306a36Sopenharmony_ci continue; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); 30162306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 30262306a36Sopenharmony_ci found = true; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!found) { 30862306a36Sopenharmony_ci while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { 30962306a36Sopenharmony_ci dhandle = ACPI_HANDLE(&pdev->dev); 31062306a36Sopenharmony_ci if (!dhandle) 31162306a36Sopenharmony_ci continue; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); 31462306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 31562306a36Sopenharmony_ci found = true; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!found) 32262306a36Sopenharmony_ci return false; 32362306a36Sopenharmony_ci pci_dev_put(pdev); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci adev->bios = kmalloc(size, GFP_KERNEL); 32662306a36Sopenharmony_ci if (!adev->bios) { 32762306a36Sopenharmony_ci dev_err(adev->dev, "Unable to allocate bios\n"); 32862306a36Sopenharmony_ci return false; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { 33262306a36Sopenharmony_ci ret = amdgpu_atrm_call(atrm_handle, 33362306a36Sopenharmony_ci adev->bios, 33462306a36Sopenharmony_ci (i * ATRM_BIOS_PAGE), 33562306a36Sopenharmony_ci ATRM_BIOS_PAGE); 33662306a36Sopenharmony_ci if (ret < ATRM_BIOS_PAGE) 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, size)) { 34162306a36Sopenharmony_ci kfree(adev->bios); 34262306a36Sopenharmony_ci return false; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci adev->bios_size = size; 34562306a36Sopenharmony_ci return true; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci#else 34862306a36Sopenharmony_cistatic inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci return false; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci#endif 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci if (adev->flags & AMD_IS_APU) 35762306a36Sopenharmony_ci return igp_read_bios_from_vram(adev); 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci return (!adev->asic_funcs || !adev->asic_funcs->read_disabled_bios) ? 36062306a36Sopenharmony_ci false : amdgpu_asic_read_disabled_bios(adev); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci#ifdef CONFIG_ACPI 36462306a36Sopenharmony_cistatic bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct acpi_table_header *hdr; 36762306a36Sopenharmony_ci acpi_size tbl_size; 36862306a36Sopenharmony_ci UEFI_ACPI_VFCT *vfct; 36962306a36Sopenharmony_ci unsigned int offset; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) 37262306a36Sopenharmony_ci return false; 37362306a36Sopenharmony_ci tbl_size = hdr->length; 37462306a36Sopenharmony_ci if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { 37562306a36Sopenharmony_ci dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n"); 37662306a36Sopenharmony_ci return false; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci vfct = (UEFI_ACPI_VFCT *)hdr; 38062306a36Sopenharmony_ci offset = vfct->VBIOSImageOffset; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci while (offset < tbl_size) { 38362306a36Sopenharmony_ci GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset); 38462306a36Sopenharmony_ci VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci offset += sizeof(VFCT_IMAGE_HEADER); 38762306a36Sopenharmony_ci if (offset > tbl_size) { 38862306a36Sopenharmony_ci dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n"); 38962306a36Sopenharmony_ci return false; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci offset += vhdr->ImageLength; 39362306a36Sopenharmony_ci if (offset > tbl_size) { 39462306a36Sopenharmony_ci dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n"); 39562306a36Sopenharmony_ci return false; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (vhdr->ImageLength && 39962306a36Sopenharmony_ci vhdr->PCIBus == adev->pdev->bus->number && 40062306a36Sopenharmony_ci vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && 40162306a36Sopenharmony_ci vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && 40262306a36Sopenharmony_ci vhdr->VendorID == adev->pdev->vendor && 40362306a36Sopenharmony_ci vhdr->DeviceID == adev->pdev->device) { 40462306a36Sopenharmony_ci adev->bios = kmemdup(&vbios->VbiosContent, 40562306a36Sopenharmony_ci vhdr->ImageLength, 40662306a36Sopenharmony_ci GFP_KERNEL); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!check_atom_bios(adev->bios, vhdr->ImageLength)) { 40962306a36Sopenharmony_ci kfree(adev->bios); 41062306a36Sopenharmony_ci return false; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci adev->bios_size = vhdr->ImageLength; 41362306a36Sopenharmony_ci return true; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); 41862306a36Sopenharmony_ci return false; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci#else 42162306a36Sopenharmony_cistatic inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci return false; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci#endif 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cibool amdgpu_get_bios(struct amdgpu_device *adev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (amdgpu_atrm_get_bios(adev)) { 43062306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from ATRM\n"); 43162306a36Sopenharmony_ci goto success; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (amdgpu_acpi_vfct_bios(adev)) { 43562306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from VFCT\n"); 43662306a36Sopenharmony_ci goto success; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (igp_read_bios_from_vram(adev)) { 44062306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from VRAM BAR\n"); 44162306a36Sopenharmony_ci goto success; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (amdgpu_read_bios(adev)) { 44562306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from ROM BAR\n"); 44662306a36Sopenharmony_ci goto success; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (amdgpu_read_bios_from_rom(adev)) { 45062306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from ROM\n"); 45162306a36Sopenharmony_ci goto success; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (amdgpu_read_disabled_bios(adev)) { 45562306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from disabled ROM BAR\n"); 45662306a36Sopenharmony_ci goto success; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (amdgpu_read_platform_bios(adev)) { 46062306a36Sopenharmony_ci dev_info(adev->dev, "Fetched VBIOS from platform\n"); 46162306a36Sopenharmony_ci goto success; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci dev_err(adev->dev, "Unable to locate a BIOS ROM\n"); 46562306a36Sopenharmony_ci return false; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cisuccess: 46862306a36Sopenharmony_ci adev->is_atom_fw = adev->asic_type >= CHIP_VEGA10; 46962306a36Sopenharmony_ci return true; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* helper function for soc15 and onwards to read bios from rom */ 47362306a36Sopenharmony_cibool amdgpu_soc15_read_bios_from_rom(struct amdgpu_device *adev, 47462306a36Sopenharmony_ci u8 *bios, u32 length_bytes) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci u32 *dw_ptr; 47762306a36Sopenharmony_ci u32 i, length_dw; 47862306a36Sopenharmony_ci u32 rom_offset; 47962306a36Sopenharmony_ci u32 rom_index_offset; 48062306a36Sopenharmony_ci u32 rom_data_offset; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (bios == NULL) 48362306a36Sopenharmony_ci return false; 48462306a36Sopenharmony_ci if (length_bytes == 0) 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci /* APU vbios image is part of sbios image */ 48762306a36Sopenharmony_ci if (adev->flags & AMD_IS_APU) 48862306a36Sopenharmony_ci return false; 48962306a36Sopenharmony_ci if (!adev->smuio.funcs || 49062306a36Sopenharmony_ci !adev->smuio.funcs->get_rom_index_offset || 49162306a36Sopenharmony_ci !adev->smuio.funcs->get_rom_data_offset) 49262306a36Sopenharmony_ci return false; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci dw_ptr = (u32 *)bios; 49562306a36Sopenharmony_ci length_dw = ALIGN(length_bytes, 4) / 4; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci rom_index_offset = 49862306a36Sopenharmony_ci adev->smuio.funcs->get_rom_index_offset(adev); 49962306a36Sopenharmony_ci rom_data_offset = 50062306a36Sopenharmony_ci adev->smuio.funcs->get_rom_data_offset(adev); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (adev->nbio.funcs && 50362306a36Sopenharmony_ci adev->nbio.funcs->get_rom_offset) { 50462306a36Sopenharmony_ci rom_offset = adev->nbio.funcs->get_rom_offset(adev); 50562306a36Sopenharmony_ci rom_offset = rom_offset << 17; 50662306a36Sopenharmony_ci } else { 50762306a36Sopenharmony_ci rom_offset = 0; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* set rom index to rom_offset */ 51162306a36Sopenharmony_ci WREG32(rom_index_offset, rom_offset); 51262306a36Sopenharmony_ci /* read out the rom data */ 51362306a36Sopenharmony_ci for (i = 0; i < length_dw; i++) 51462306a36Sopenharmony_ci dw_ptr[i] = RREG32(rom_data_offset); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return true; 51762306a36Sopenharmony_ci} 518