// SPDX-License-Identifier: GPL-2.0
/*
* PCI support in ACPI
*
* Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
* Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
* Copyright (C) 2004 Intel Corp.
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/irqdomain.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/pci_hotplug.h>
#include <linux/module.h>
#include <linux/pci-aspm.h>
#include <linux/pci-acpi.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include "pci.h"
/*
* The GUID is defined in the PCI Firmware Specification available here:
* https://www.pcisig.com/members/downloads/pcifw_r3_1_13Dec10.pdf
*/
const guid_t pci_acpi_dsm_guid =
GUID_INIT(0xe5c937d0, 0x3553, 0x4d7a,
0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d);
#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
static int acpi_get_rc_addr(struct acpi_device *adev, struct resource *res)
{
struct device *dev = &adev->dev;
struct resource_entry *entry;
struct list_head list;
unsigned long flags;
int ret;
INIT_LIST_HEAD(&list);
flags = IORESOURCE_MEM;
ret = acpi_dev_get_resources(adev, &list,
acpi_dev_filter_resource_type_cb,
(void *) flags);
if (ret < 0) {
dev_err(dev, "failed to parse _CRS method, error code %d\n",
ret);
return ret;
}
if (ret == 0) {
dev_err(dev, "no IO and memory resources present in _CRS\n");
return -EINVAL;
}
entry = list_first_entry(&list, struct resource_entry, node);
*res = *entry->res;
acpi_dev_free_resource_list(&list);
return 0;
}
static acpi_status acpi_match_rc(acpi_handle handle, u32 lvl, void *context,
void **retval)
{
u16 *segment = context;
unsigned long long uid;
acpi_status status;
status = acpi_evaluate_integer(handle, "_UID", NULL, &uid);
if (ACPI_FAILURE(status) || uid != *segment)
return AE_CTRL_DEPTH;
*(acpi_handle *)retval = handle;
return AE_CTRL_TERMINATE;
}
int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
struct resource *res)
{
struct acpi_device *adev;
acpi_status status;
acpi_handle handle;
int ret;
status = acpi_get_devices(hid, acpi_match_rc, &segment, &handle);
if (ACPI_FAILURE(status)) {
dev_err(dev, "can't find _HID %s device to locate resources\n",
hid);
return -ENODEV;
}
ret = acpi_bus_get_device(handle, &adev);
if (ret)
return ret;
ret = acpi_get_rc_addr(adev, res);
if (ret) {
dev_err(dev, "can't get resource from %s\n",
dev_name(&adev->dev));
return ret;
}
return 0;
}
#endif
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
{
acpi_status status = AE_NOT_EXIST;
unsigned long long mcfg_addr;
if (handle)
status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
NULL, &mcfg_addr);
if (ACPI_FAILURE(status))
return 0;
return (phys_addr_t)mcfg_addr;
}
static acpi_status decode_type0_hpx_record(union acpi_object *record,
struct hpp_type0 *hpx0)
{
int i;
union acpi_object *fields = record->package.elements;
u32 revision = fields[1].integer.value;
switch (revision) {
case 1:
if (record->package.count != 6)
return AE_ERROR;
for (i = 2; i < 6; i++)
if (fields[i].type != ACPI_TYPE_INTEGER)
return AE_ERROR;
hpx0->revision = revision;
hpx0->cache_line_size = fields[2].integer.value;
hpx0->latency_timer = fields[3].integer.value;
hpx0->enable_serr = fields[4].integer.value;
hpx0->enable_perr = fields[5].integer.value;
break;
default:
pr_warn("%s: Type 0 Revision %d record not supported\n",
__func__, revision);
return AE_ERROR;
}
return AE_OK;
}
static acpi_status decode_type1_hpx_record(union acpi_object *record,
struct hpp_type1 *hpx1)
{
int i;
union acpi_object *fields = record->package.elements;
u32 revision = fields[1].integer.value;
switch (revision) {
case 1:
if (record->package.count != 5)
return AE_ERROR;
for (i = 2; i < 5; i++)
if (fields[i].type != ACPI_TYPE_INTEGER)
return AE_ERROR;
hpx1->revision = revision;
hpx1->max_mem_read = fields[2].integer.value;
hpx1->avg_max_split = fields[3].integer.value;
hpx1->tot_max_split = fields[4].integer.value;
break;
default:
pr_warn("%s: Type 1 Revision %d record not supported\