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