18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI support in ACPI 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2004 Intel Corp. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/msi.h> 158c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_qos.h> 208c2ecf20Sopenharmony_ci#include "pci.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * The GUID is defined in the PCI Firmware Specification available here: 248c2ecf20Sopenharmony_ci * https://www.pcisig.com/members/downloads/pcifw_r3_1_13Dec10.pdf 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ciconst guid_t pci_acpi_dsm_guid = 278c2ecf20Sopenharmony_ci GUID_INIT(0xe5c937d0, 0x3553, 0x4d7a, 288c2ecf20Sopenharmony_ci 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64) 318c2ecf20Sopenharmony_cistatic int acpi_get_rc_addr(struct acpi_device *adev, struct resource *res) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct device *dev = &adev->dev; 348c2ecf20Sopenharmony_ci struct resource_entry *entry; 358c2ecf20Sopenharmony_ci struct list_head list; 368c2ecf20Sopenharmony_ci unsigned long flags; 378c2ecf20Sopenharmony_ci int ret; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 408c2ecf20Sopenharmony_ci flags = IORESOURCE_MEM; 418c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &list, 428c2ecf20Sopenharmony_ci acpi_dev_filter_resource_type_cb, 438c2ecf20Sopenharmony_ci (void *) flags); 448c2ecf20Sopenharmony_ci if (ret < 0) { 458c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse _CRS method, error code %d\n", 468c2ecf20Sopenharmony_ci ret); 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (ret == 0) { 518c2ecf20Sopenharmony_ci dev_err(dev, "no IO and memory resources present in _CRS\n"); 528c2ecf20Sopenharmony_ci return -EINVAL; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci entry = list_first_entry(&list, struct resource_entry, node); 568c2ecf20Sopenharmony_ci *res = *entry->res; 578c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&list); 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic acpi_status acpi_match_rc(acpi_handle handle, u32 lvl, void *context, 628c2ecf20Sopenharmony_ci void **retval) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u16 *segment = context; 658c2ecf20Sopenharmony_ci unsigned long long uid; 668c2ecf20Sopenharmony_ci acpi_status status; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_UID", NULL, &uid); 698c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || uid != *segment) 708c2ecf20Sopenharmony_ci return AE_CTRL_DEPTH; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci *(acpi_handle *)retval = handle; 738c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment, 778c2ecf20Sopenharmony_ci struct resource *res) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct acpi_device *adev; 808c2ecf20Sopenharmony_ci acpi_status status; 818c2ecf20Sopenharmony_ci acpi_handle handle; 828c2ecf20Sopenharmony_ci int ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci status = acpi_get_devices(hid, acpi_match_rc, &segment, &handle); 858c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 868c2ecf20Sopenharmony_ci dev_err(dev, "can't find _HID %s device to locate resources\n", 878c2ecf20Sopenharmony_ci hid); 888c2ecf20Sopenharmony_ci return -ENODEV; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ret = acpi_bus_get_device(handle, &adev); 928c2ecf20Sopenharmony_ci if (ret) 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = acpi_get_rc_addr(adev, res); 968c2ecf20Sopenharmony_ci if (ret) { 978c2ecf20Sopenharmony_ci dev_err(dev, "can't get resource from %s\n", 988c2ecf20Sopenharmony_ci dev_name(&adev->dev)); 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci#endif 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciphys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci acpi_status status = AE_NOT_EXIST; 1098c2ecf20Sopenharmony_ci unsigned long long mcfg_addr; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (handle) 1128c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, 1138c2ecf20Sopenharmony_ci NULL, &mcfg_addr); 1148c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return (phys_addr_t)mcfg_addr; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* _HPX PCI Setting Record (Type 0); same as _HPP */ 1218c2ecf20Sopenharmony_cistruct hpx_type0 { 1228c2ecf20Sopenharmony_ci u32 revision; /* Not present in _HPP */ 1238c2ecf20Sopenharmony_ci u8 cache_line_size; /* Not applicable to PCIe */ 1248c2ecf20Sopenharmony_ci u8 latency_timer; /* Not applicable to PCIe */ 1258c2ecf20Sopenharmony_ci u8 enable_serr; 1268c2ecf20Sopenharmony_ci u8 enable_perr; 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct hpx_type0 pci_default_type0 = { 1308c2ecf20Sopenharmony_ci .revision = 1, 1318c2ecf20Sopenharmony_ci .cache_line_size = 8, 1328c2ecf20Sopenharmony_ci .latency_timer = 0x40, 1338c2ecf20Sopenharmony_ci .enable_serr = 0, 1348c2ecf20Sopenharmony_ci .enable_perr = 0, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void program_hpx_type0(struct pci_dev *dev, struct hpx_type0 *hpx) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u16 pci_cmd, pci_bctl; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!hpx) 1428c2ecf20Sopenharmony_ci hpx = &pci_default_type0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (hpx->revision > 1) { 1458c2ecf20Sopenharmony_ci pci_warn(dev, "PCI settings rev %d not supported; using defaults\n", 1468c2ecf20Sopenharmony_ci hpx->revision); 1478c2ecf20Sopenharmony_ci hpx = &pci_default_type0; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpx->cache_line_size); 1518c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpx->latency_timer); 1528c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); 1538c2ecf20Sopenharmony_ci if (hpx->enable_serr) 1548c2ecf20Sopenharmony_ci pci_cmd |= PCI_COMMAND_SERR; 1558c2ecf20Sopenharmony_ci if (hpx->enable_perr) 1568c2ecf20Sopenharmony_ci pci_cmd |= PCI_COMMAND_PARITY; 1578c2ecf20Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, pci_cmd); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Program bridge control value */ 1608c2ecf20Sopenharmony_ci if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { 1618c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 1628c2ecf20Sopenharmony_ci hpx->latency_timer); 1638c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); 1648c2ecf20Sopenharmony_ci if (hpx->enable_perr) 1658c2ecf20Sopenharmony_ci pci_bctl |= PCI_BRIDGE_CTL_PARITY; 1668c2ecf20Sopenharmony_ci pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic acpi_status decode_type0_hpx_record(union acpi_object *record, 1718c2ecf20Sopenharmony_ci struct hpx_type0 *hpx0) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int i; 1748c2ecf20Sopenharmony_ci union acpi_object *fields = record->package.elements; 1758c2ecf20Sopenharmony_ci u32 revision = fields[1].integer.value; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci switch (revision) { 1788c2ecf20Sopenharmony_ci case 1: 1798c2ecf20Sopenharmony_ci if (record->package.count != 6) 1808c2ecf20Sopenharmony_ci return AE_ERROR; 1818c2ecf20Sopenharmony_ci for (i = 2; i < 6; i++) 1828c2ecf20Sopenharmony_ci if (fields[i].type != ACPI_TYPE_INTEGER) 1838c2ecf20Sopenharmony_ci return AE_ERROR; 1848c2ecf20Sopenharmony_ci hpx0->revision = revision; 1858c2ecf20Sopenharmony_ci hpx0->cache_line_size = fields[2].integer.value; 1868c2ecf20Sopenharmony_ci hpx0->latency_timer = fields[3].integer.value; 1878c2ecf20Sopenharmony_ci hpx0->enable_serr = fields[4].integer.value; 1888c2ecf20Sopenharmony_ci hpx0->enable_perr = fields[5].integer.value; 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci default: 1918c2ecf20Sopenharmony_ci pr_warn("%s: Type 0 Revision %d record not supported\n", 1928c2ecf20Sopenharmony_ci __func__, revision); 1938c2ecf20Sopenharmony_ci return AE_ERROR; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return AE_OK; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* _HPX PCI-X Setting Record (Type 1) */ 1998c2ecf20Sopenharmony_cistruct hpx_type1 { 2008c2ecf20Sopenharmony_ci u32 revision; 2018c2ecf20Sopenharmony_ci u8 max_mem_read; 2028c2ecf20Sopenharmony_ci u8 avg_max_split; 2038c2ecf20Sopenharmony_ci u16 tot_max_split; 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void program_hpx_type1(struct pci_dev *dev, struct hpx_type1 *hpx) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int pos; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!hpx) 2118c2ecf20Sopenharmony_ci return; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); 2148c2ecf20Sopenharmony_ci if (!pos) 2158c2ecf20Sopenharmony_ci return; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci pci_warn(dev, "PCI-X settings not supported\n"); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic acpi_status decode_type1_hpx_record(union acpi_object *record, 2218c2ecf20Sopenharmony_ci struct hpx_type1 *hpx1) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int i; 2248c2ecf20Sopenharmony_ci union acpi_object *fields = record->package.elements; 2258c2ecf20Sopenharmony_ci u32 revision = fields[1].integer.value; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci switch (revision) { 2288c2ecf20Sopenharmony_ci case 1: 2298c2ecf20Sopenharmony_ci if (record->package.count != 5) 2308c2ecf20Sopenharmony_ci return AE_ERROR; 2318c2ecf20Sopenharmony_ci for (i = 2; i < 5; i++) 2328c2ecf20Sopenharmony_ci if (fields[i].type != ACPI_TYPE_INTEGER) 2338c2ecf20Sopenharmony_ci return AE_ERROR; 2348c2ecf20Sopenharmony_ci hpx1->revision = revision; 2358c2ecf20Sopenharmony_ci hpx1->max_mem_read = fields[2].integer.value; 2368c2ecf20Sopenharmony_ci hpx1->avg_max_split = fields[3].integer.value; 2378c2ecf20Sopenharmony_ci hpx1->tot_max_split = fields[4].integer.value; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci default: 2408c2ecf20Sopenharmony_ci pr_warn("%s: Type 1 Revision %d record not supported\n", 2418c2ecf20Sopenharmony_ci __func__, revision); 2428c2ecf20Sopenharmony_ci return AE_ERROR; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci return AE_OK; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic bool pcie_root_rcb_set(struct pci_dev *dev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct pci_dev *rp = pcie_find_root_port(dev); 2508c2ecf20Sopenharmony_ci u16 lnkctl; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!rp) 2538c2ecf20Sopenharmony_ci return false; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl); 2568c2ecf20Sopenharmony_ci if (lnkctl & PCI_EXP_LNKCTL_RCB) 2578c2ecf20Sopenharmony_ci return true; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return false; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* _HPX PCI Express Setting Record (Type 2) */ 2638c2ecf20Sopenharmony_cistruct hpx_type2 { 2648c2ecf20Sopenharmony_ci u32 revision; 2658c2ecf20Sopenharmony_ci u32 unc_err_mask_and; 2668c2ecf20Sopenharmony_ci u32 unc_err_mask_or; 2678c2ecf20Sopenharmony_ci u32 unc_err_sever_and; 2688c2ecf20Sopenharmony_ci u32 unc_err_sever_or; 2698c2ecf20Sopenharmony_ci u32 cor_err_mask_and; 2708c2ecf20Sopenharmony_ci u32 cor_err_mask_or; 2718c2ecf20Sopenharmony_ci u32 adv_err_cap_and; 2728c2ecf20Sopenharmony_ci u32 adv_err_cap_or; 2738c2ecf20Sopenharmony_ci u16 pci_exp_devctl_and; 2748c2ecf20Sopenharmony_ci u16 pci_exp_devctl_or; 2758c2ecf20Sopenharmony_ci u16 pci_exp_lnkctl_and; 2768c2ecf20Sopenharmony_ci u16 pci_exp_lnkctl_or; 2778c2ecf20Sopenharmony_ci u32 sec_unc_err_sever_and; 2788c2ecf20Sopenharmony_ci u32 sec_unc_err_sever_or; 2798c2ecf20Sopenharmony_ci u32 sec_unc_err_mask_and; 2808c2ecf20Sopenharmony_ci u32 sec_unc_err_mask_or; 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int pos; 2868c2ecf20Sopenharmony_ci u32 reg32; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!hpx) 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!pci_is_pcie(dev)) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (hpx->revision > 1) { 2958c2ecf20Sopenharmony_ci pci_warn(dev, "PCIe settings rev %d not supported\n", 2968c2ecf20Sopenharmony_ci hpx->revision); 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Don't allow _HPX to change MPS or MRRS settings. We manage 3028c2ecf20Sopenharmony_ci * those to make sure they're consistent with the rest of the 3038c2ecf20Sopenharmony_ci * platform. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD | 3068c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL_READRQ; 3078c2ecf20Sopenharmony_ci hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD | 3088c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL_READRQ); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Initialize Device Control Register */ 3118c2ecf20Sopenharmony_ci pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, 3128c2ecf20Sopenharmony_ci ~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Initialize Link Control Register */ 3158c2ecf20Sopenharmony_ci if (pcie_cap_has_lnkctl(dev)) { 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* 3188c2ecf20Sopenharmony_ci * If the Root Port supports Read Completion Boundary of 3198c2ecf20Sopenharmony_ci * 128, set RCB to 128. Otherwise, clear it. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB; 3228c2ecf20Sopenharmony_ci hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB; 3238c2ecf20Sopenharmony_ci if (pcie_root_rcb_set(dev)) 3248c2ecf20Sopenharmony_ci hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, 3278c2ecf20Sopenharmony_ci ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Find Advanced Error Reporting Enhanced Capability */ 3318c2ecf20Sopenharmony_ci pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); 3328c2ecf20Sopenharmony_ci if (!pos) 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Initialize Uncorrectable Error Mask Register */ 3368c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32); 3378c2ecf20Sopenharmony_ci reg32 = (reg32 & hpx->unc_err_mask_and) | hpx->unc_err_mask_or; 3388c2ecf20Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Initialize Uncorrectable Error Severity Register */ 3418c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32); 3428c2ecf20Sopenharmony_ci reg32 = (reg32 & hpx->unc_err_sever_and) | hpx->unc_err_sever_or; 3438c2ecf20Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Initialize Correctable Error Mask Register */ 3468c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32); 3478c2ecf20Sopenharmony_ci reg32 = (reg32 & hpx->cor_err_mask_and) | hpx->cor_err_mask_or; 3488c2ecf20Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Initialize Advanced Error Capabilities and Control Register */ 3518c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); 3528c2ecf20Sopenharmony_ci reg32 = (reg32 & hpx->adv_err_cap_and) | hpx->adv_err_cap_or; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Don't enable ECRC generation or checking if unsupported */ 3558c2ecf20Sopenharmony_ci if (!(reg32 & PCI_ERR_CAP_ECRC_GENC)) 3568c2ecf20Sopenharmony_ci reg32 &= ~PCI_ERR_CAP_ECRC_GENE; 3578c2ecf20Sopenharmony_ci if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC)) 3588c2ecf20Sopenharmony_ci reg32 &= ~PCI_ERR_CAP_ECRC_CHKE; 3598c2ecf20Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * FIXME: The following two registers are not supported yet. 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * o Secondary Uncorrectable Error Severity Register 3658c2ecf20Sopenharmony_ci * o Secondary Uncorrectable Error Mask Register 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic acpi_status decode_type2_hpx_record(union acpi_object *record, 3708c2ecf20Sopenharmony_ci struct hpx_type2 *hpx2) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int i; 3738c2ecf20Sopenharmony_ci union acpi_object *fields = record->package.elements; 3748c2ecf20Sopenharmony_ci u32 revision = fields[1].integer.value; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci switch (revision) { 3778c2ecf20Sopenharmony_ci case 1: 3788c2ecf20Sopenharmony_ci if (record->package.count != 18) 3798c2ecf20Sopenharmony_ci return AE_ERROR; 3808c2ecf20Sopenharmony_ci for (i = 2; i < 18; i++) 3818c2ecf20Sopenharmony_ci if (fields[i].type != ACPI_TYPE_INTEGER) 3828c2ecf20Sopenharmony_ci return AE_ERROR; 3838c2ecf20Sopenharmony_ci hpx2->revision = revision; 3848c2ecf20Sopenharmony_ci hpx2->unc_err_mask_and = fields[2].integer.value; 3858c2ecf20Sopenharmony_ci hpx2->unc_err_mask_or = fields[3].integer.value; 3868c2ecf20Sopenharmony_ci hpx2->unc_err_sever_and = fields[4].integer.value; 3878c2ecf20Sopenharmony_ci hpx2->unc_err_sever_or = fields[5].integer.value; 3888c2ecf20Sopenharmony_ci hpx2->cor_err_mask_and = fields[6].integer.value; 3898c2ecf20Sopenharmony_ci hpx2->cor_err_mask_or = fields[7].integer.value; 3908c2ecf20Sopenharmony_ci hpx2->adv_err_cap_and = fields[8].integer.value; 3918c2ecf20Sopenharmony_ci hpx2->adv_err_cap_or = fields[9].integer.value; 3928c2ecf20Sopenharmony_ci hpx2->pci_exp_devctl_and = fields[10].integer.value; 3938c2ecf20Sopenharmony_ci hpx2->pci_exp_devctl_or = fields[11].integer.value; 3948c2ecf20Sopenharmony_ci hpx2->pci_exp_lnkctl_and = fields[12].integer.value; 3958c2ecf20Sopenharmony_ci hpx2->pci_exp_lnkctl_or = fields[13].integer.value; 3968c2ecf20Sopenharmony_ci hpx2->sec_unc_err_sever_and = fields[14].integer.value; 3978c2ecf20Sopenharmony_ci hpx2->sec_unc_err_sever_or = fields[15].integer.value; 3988c2ecf20Sopenharmony_ci hpx2->sec_unc_err_mask_and = fields[16].integer.value; 3998c2ecf20Sopenharmony_ci hpx2->sec_unc_err_mask_or = fields[17].integer.value; 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci default: 4028c2ecf20Sopenharmony_ci pr_warn("%s: Type 2 Revision %d record not supported\n", 4038c2ecf20Sopenharmony_ci __func__, revision); 4048c2ecf20Sopenharmony_ci return AE_ERROR; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci return AE_OK; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* _HPX PCI Express Setting Record (Type 3) */ 4108c2ecf20Sopenharmony_cistruct hpx_type3 { 4118c2ecf20Sopenharmony_ci u16 device_type; 4128c2ecf20Sopenharmony_ci u16 function_type; 4138c2ecf20Sopenharmony_ci u16 config_space_location; 4148c2ecf20Sopenharmony_ci u16 pci_exp_cap_id; 4158c2ecf20Sopenharmony_ci u16 pci_exp_cap_ver; 4168c2ecf20Sopenharmony_ci u16 pci_exp_vendor_id; 4178c2ecf20Sopenharmony_ci u16 dvsec_id; 4188c2ecf20Sopenharmony_ci u16 dvsec_rev; 4198c2ecf20Sopenharmony_ci u16 match_offset; 4208c2ecf20Sopenharmony_ci u32 match_mask_and; 4218c2ecf20Sopenharmony_ci u32 match_value; 4228c2ecf20Sopenharmony_ci u16 reg_offset; 4238c2ecf20Sopenharmony_ci u32 reg_mask_and; 4248c2ecf20Sopenharmony_ci u32 reg_mask_or; 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cienum hpx_type3_dev_type { 4288c2ecf20Sopenharmony_ci HPX_TYPE_ENDPOINT = BIT(0), 4298c2ecf20Sopenharmony_ci HPX_TYPE_LEG_END = BIT(1), 4308c2ecf20Sopenharmony_ci HPX_TYPE_RC_END = BIT(2), 4318c2ecf20Sopenharmony_ci HPX_TYPE_RC_EC = BIT(3), 4328c2ecf20Sopenharmony_ci HPX_TYPE_ROOT_PORT = BIT(4), 4338c2ecf20Sopenharmony_ci HPX_TYPE_UPSTREAM = BIT(5), 4348c2ecf20Sopenharmony_ci HPX_TYPE_DOWNSTREAM = BIT(6), 4358c2ecf20Sopenharmony_ci HPX_TYPE_PCI_BRIDGE = BIT(7), 4368c2ecf20Sopenharmony_ci HPX_TYPE_PCIE_BRIDGE = BIT(8), 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic u16 hpx3_device_type(struct pci_dev *dev) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci u16 pcie_type = pci_pcie_type(dev); 4428c2ecf20Sopenharmony_ci static const int pcie_to_hpx3_type[] = { 4438c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT, 4448c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END, 4458c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END, 4468c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC, 4478c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT, 4488c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM, 4498c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM, 4508c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE, 4518c2ecf20Sopenharmony_ci [PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE, 4528c2ecf20Sopenharmony_ci }; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type)) 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return pcie_to_hpx3_type[pcie_type]; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cienum hpx_type3_fn_type { 4618c2ecf20Sopenharmony_ci HPX_FN_NORMAL = BIT(0), 4628c2ecf20Sopenharmony_ci HPX_FN_SRIOV_PHYS = BIT(1), 4638c2ecf20Sopenharmony_ci HPX_FN_SRIOV_VIRT = BIT(2), 4648c2ecf20Sopenharmony_ci}; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic u8 hpx3_function_type(struct pci_dev *dev) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci if (dev->is_virtfn) 4698c2ecf20Sopenharmony_ci return HPX_FN_SRIOV_VIRT; 4708c2ecf20Sopenharmony_ci else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0) 4718c2ecf20Sopenharmony_ci return HPX_FN_SRIOV_PHYS; 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci return HPX_FN_NORMAL; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci u8 cap_ver = hpx3_cap_id & 0xf; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id) 4818c2ecf20Sopenharmony_ci return true; 4828c2ecf20Sopenharmony_ci else if (cap_ver == pcie_cap_id) 4838c2ecf20Sopenharmony_ci return true; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return false; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cienum hpx_type3_cfg_loc { 4898c2ecf20Sopenharmony_ci HPX_CFG_PCICFG = 0, 4908c2ecf20Sopenharmony_ci HPX_CFG_PCIE_CAP = 1, 4918c2ecf20Sopenharmony_ci HPX_CFG_PCIE_CAP_EXT = 2, 4928c2ecf20Sopenharmony_ci HPX_CFG_VEND_CAP = 3, 4938c2ecf20Sopenharmony_ci HPX_CFG_DVSEC = 4, 4948c2ecf20Sopenharmony_ci HPX_CFG_MAX, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void program_hpx_type3_register(struct pci_dev *dev, 4988c2ecf20Sopenharmony_ci const struct hpx_type3 *reg) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci u32 match_reg, write_reg, header, orig_value; 5018c2ecf20Sopenharmony_ci u16 pos; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (!(hpx3_device_type(dev) & reg->device_type)) 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (!(hpx3_function_type(dev) & reg->function_type)) 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci switch (reg->config_space_location) { 5108c2ecf20Sopenharmony_ci case HPX_CFG_PCICFG: 5118c2ecf20Sopenharmony_ci pos = 0; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case HPX_CFG_PCIE_CAP: 5148c2ecf20Sopenharmony_ci pos = pci_find_capability(dev, reg->pci_exp_cap_id); 5158c2ecf20Sopenharmony_ci if (pos == 0) 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case HPX_CFG_PCIE_CAP_EXT: 5208c2ecf20Sopenharmony_ci pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id); 5218c2ecf20Sopenharmony_ci if (pos == 0) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos, &header); 5258c2ecf20Sopenharmony_ci if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header), 5268c2ecf20Sopenharmony_ci reg->pci_exp_cap_ver)) 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case HPX_CFG_VEND_CAP: 5318c2ecf20Sopenharmony_ci case HPX_CFG_DVSEC: 5328c2ecf20Sopenharmony_ci default: 5338c2ecf20Sopenharmony_ci pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location"); 5348c2ecf20Sopenharmony_ci return; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + reg->match_offset, &match_reg); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if ((match_reg & reg->match_mask_and) != reg->match_value) 5408c2ecf20Sopenharmony_ci return; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg); 5438c2ecf20Sopenharmony_ci orig_value = write_reg; 5448c2ecf20Sopenharmony_ci write_reg &= reg->reg_mask_and; 5458c2ecf20Sopenharmony_ci write_reg |= reg->reg_mask_or; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (orig_value == write_reg) 5488c2ecf20Sopenharmony_ci return; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pci_write_config_dword(dev, pos + reg->reg_offset, write_reg); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x", 5538c2ecf20Sopenharmony_ci pos, orig_value, write_reg); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci if (!hpx) 5598c2ecf20Sopenharmony_ci return; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!pci_is_pcie(dev)) 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci program_hpx_type3_register(dev, hpx); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void parse_hpx3_register(struct hpx_type3 *hpx3_reg, 5688c2ecf20Sopenharmony_ci union acpi_object *reg_fields) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci hpx3_reg->device_type = reg_fields[0].integer.value; 5718c2ecf20Sopenharmony_ci hpx3_reg->function_type = reg_fields[1].integer.value; 5728c2ecf20Sopenharmony_ci hpx3_reg->config_space_location = reg_fields[2].integer.value; 5738c2ecf20Sopenharmony_ci hpx3_reg->pci_exp_cap_id = reg_fields[3].integer.value; 5748c2ecf20Sopenharmony_ci hpx3_reg->pci_exp_cap_ver = reg_fields[4].integer.value; 5758c2ecf20Sopenharmony_ci hpx3_reg->pci_exp_vendor_id = reg_fields[5].integer.value; 5768c2ecf20Sopenharmony_ci hpx3_reg->dvsec_id = reg_fields[6].integer.value; 5778c2ecf20Sopenharmony_ci hpx3_reg->dvsec_rev = reg_fields[7].integer.value; 5788c2ecf20Sopenharmony_ci hpx3_reg->match_offset = reg_fields[8].integer.value; 5798c2ecf20Sopenharmony_ci hpx3_reg->match_mask_and = reg_fields[9].integer.value; 5808c2ecf20Sopenharmony_ci hpx3_reg->match_value = reg_fields[10].integer.value; 5818c2ecf20Sopenharmony_ci hpx3_reg->reg_offset = reg_fields[11].integer.value; 5828c2ecf20Sopenharmony_ci hpx3_reg->reg_mask_and = reg_fields[12].integer.value; 5838c2ecf20Sopenharmony_ci hpx3_reg->reg_mask_or = reg_fields[13].integer.value; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic acpi_status program_type3_hpx_record(struct pci_dev *dev, 5878c2ecf20Sopenharmony_ci union acpi_object *record) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci union acpi_object *fields = record->package.elements; 5908c2ecf20Sopenharmony_ci u32 desc_count, expected_length, revision; 5918c2ecf20Sopenharmony_ci union acpi_object *reg_fields; 5928c2ecf20Sopenharmony_ci struct hpx_type3 hpx3; 5938c2ecf20Sopenharmony_ci int i; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci revision = fields[1].integer.value; 5968c2ecf20Sopenharmony_ci switch (revision) { 5978c2ecf20Sopenharmony_ci case 1: 5988c2ecf20Sopenharmony_ci desc_count = fields[2].integer.value; 5998c2ecf20Sopenharmony_ci expected_length = 3 + desc_count * 14; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (record->package.count != expected_length) 6028c2ecf20Sopenharmony_ci return AE_ERROR; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci for (i = 2; i < expected_length; i++) 6058c2ecf20Sopenharmony_ci if (fields[i].type != ACPI_TYPE_INTEGER) 6068c2ecf20Sopenharmony_ci return AE_ERROR; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci for (i = 0; i < desc_count; i++) { 6098c2ecf20Sopenharmony_ci reg_fields = fields + 3 + i * 14; 6108c2ecf20Sopenharmony_ci parse_hpx3_register(&hpx3, reg_fields); 6118c2ecf20Sopenharmony_ci program_hpx_type3(dev, &hpx3); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci default: 6168c2ecf20Sopenharmony_ci printk(KERN_WARNING 6178c2ecf20Sopenharmony_ci "%s: Type 3 Revision %d record not supported\n", 6188c2ecf20Sopenharmony_ci __func__, revision); 6198c2ecf20Sopenharmony_ci return AE_ERROR; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci return AE_OK; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci acpi_status status; 6278c2ecf20Sopenharmony_ci struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 6288c2ecf20Sopenharmony_ci union acpi_object *package, *record, *fields; 6298c2ecf20Sopenharmony_ci struct hpx_type0 hpx0; 6308c2ecf20Sopenharmony_ci struct hpx_type1 hpx1; 6318c2ecf20Sopenharmony_ci struct hpx_type2 hpx2; 6328c2ecf20Sopenharmony_ci u32 type; 6338c2ecf20Sopenharmony_ci int i; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); 6368c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6378c2ecf20Sopenharmony_ci return status; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci package = (union acpi_object *)buffer.pointer; 6408c2ecf20Sopenharmony_ci if (package->type != ACPI_TYPE_PACKAGE) { 6418c2ecf20Sopenharmony_ci status = AE_ERROR; 6428c2ecf20Sopenharmony_ci goto exit; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci for (i = 0; i < package->package.count; i++) { 6468c2ecf20Sopenharmony_ci record = &package->package.elements[i]; 6478c2ecf20Sopenharmony_ci if (record->type != ACPI_TYPE_PACKAGE) { 6488c2ecf20Sopenharmony_ci status = AE_ERROR; 6498c2ecf20Sopenharmony_ci goto exit; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci fields = record->package.elements; 6538c2ecf20Sopenharmony_ci if (fields[0].type != ACPI_TYPE_INTEGER || 6548c2ecf20Sopenharmony_ci fields[1].type != ACPI_TYPE_INTEGER) { 6558c2ecf20Sopenharmony_ci status = AE_ERROR; 6568c2ecf20Sopenharmony_ci goto exit; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci type = fields[0].integer.value; 6608c2ecf20Sopenharmony_ci switch (type) { 6618c2ecf20Sopenharmony_ci case 0: 6628c2ecf20Sopenharmony_ci memset(&hpx0, 0, sizeof(hpx0)); 6638c2ecf20Sopenharmony_ci status = decode_type0_hpx_record(record, &hpx0); 6648c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6658c2ecf20Sopenharmony_ci goto exit; 6668c2ecf20Sopenharmony_ci program_hpx_type0(dev, &hpx0); 6678c2ecf20Sopenharmony_ci break; 6688c2ecf20Sopenharmony_ci case 1: 6698c2ecf20Sopenharmony_ci memset(&hpx1, 0, sizeof(hpx1)); 6708c2ecf20Sopenharmony_ci status = decode_type1_hpx_record(record, &hpx1); 6718c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6728c2ecf20Sopenharmony_ci goto exit; 6738c2ecf20Sopenharmony_ci program_hpx_type1(dev, &hpx1); 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case 2: 6768c2ecf20Sopenharmony_ci memset(&hpx2, 0, sizeof(hpx2)); 6778c2ecf20Sopenharmony_ci status = decode_type2_hpx_record(record, &hpx2); 6788c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6798c2ecf20Sopenharmony_ci goto exit; 6808c2ecf20Sopenharmony_ci program_hpx_type2(dev, &hpx2); 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci case 3: 6838c2ecf20Sopenharmony_ci status = program_type3_hpx_record(dev, record); 6848c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6858c2ecf20Sopenharmony_ci goto exit; 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci default: 6888c2ecf20Sopenharmony_ci pr_err("%s: Type %d record not supported\n", 6898c2ecf20Sopenharmony_ci __func__, type); 6908c2ecf20Sopenharmony_ci status = AE_ERROR; 6918c2ecf20Sopenharmony_ci goto exit; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci exit: 6958c2ecf20Sopenharmony_ci kfree(buffer.pointer); 6968c2ecf20Sopenharmony_ci return status; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci acpi_status status; 7028c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 7038c2ecf20Sopenharmony_ci union acpi_object *package, *fields; 7048c2ecf20Sopenharmony_ci struct hpx_type0 hpx0; 7058c2ecf20Sopenharmony_ci int i; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci memset(&hpx0, 0, sizeof(hpx0)); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); 7108c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 7118c2ecf20Sopenharmony_ci return status; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci package = (union acpi_object *) buffer.pointer; 7148c2ecf20Sopenharmony_ci if (package->type != ACPI_TYPE_PACKAGE || 7158c2ecf20Sopenharmony_ci package->package.count != 4) { 7168c2ecf20Sopenharmony_ci status = AE_ERROR; 7178c2ecf20Sopenharmony_ci goto exit; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci fields = package->package.elements; 7218c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 7228c2ecf20Sopenharmony_ci if (fields[i].type != ACPI_TYPE_INTEGER) { 7238c2ecf20Sopenharmony_ci status = AE_ERROR; 7248c2ecf20Sopenharmony_ci goto exit; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci hpx0.revision = 1; 7298c2ecf20Sopenharmony_ci hpx0.cache_line_size = fields[0].integer.value; 7308c2ecf20Sopenharmony_ci hpx0.latency_timer = fields[1].integer.value; 7318c2ecf20Sopenharmony_ci hpx0.enable_serr = fields[2].integer.value; 7328c2ecf20Sopenharmony_ci hpx0.enable_perr = fields[3].integer.value; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci program_hpx_type0(dev, &hpx0); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ciexit: 7378c2ecf20Sopenharmony_ci kfree(buffer.pointer); 7388c2ecf20Sopenharmony_ci return status; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/* pci_acpi_program_hp_params 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * @dev - the pci_dev for which we want parameters 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ciint pci_acpi_program_hp_params(struct pci_dev *dev) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci acpi_status status; 7488c2ecf20Sopenharmony_ci acpi_handle handle, phandle; 7498c2ecf20Sopenharmony_ci struct pci_bus *pbus; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (acpi_pci_disabled) 7528c2ecf20Sopenharmony_ci return -ENODEV; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci handle = NULL; 7558c2ecf20Sopenharmony_ci for (pbus = dev->bus; pbus; pbus = pbus->parent) { 7568c2ecf20Sopenharmony_ci handle = acpi_pci_get_bridge_handle(pbus); 7578c2ecf20Sopenharmony_ci if (handle) 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * _HPP settings apply to all child buses, until another _HPP is 7638c2ecf20Sopenharmony_ci * encountered. If we don't find an _HPP for the input pci dev, 7648c2ecf20Sopenharmony_ci * look for it in the parent device scope since that would apply to 7658c2ecf20Sopenharmony_ci * this pci dev. 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_ci while (handle) { 7688c2ecf20Sopenharmony_ci status = acpi_run_hpx(dev, handle); 7698c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci status = acpi_run_hpp(dev, handle); 7728c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci if (acpi_is_root_bridge(handle)) 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci status = acpi_get_parent(handle, &phandle); 7778c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci handle = phandle; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci return -ENODEV; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci/** 7858c2ecf20Sopenharmony_ci * pciehp_is_native - Check whether a hotplug port is handled by the OS 7868c2ecf20Sopenharmony_ci * @bridge: Hotplug port to check 7878c2ecf20Sopenharmony_ci * 7888c2ecf20Sopenharmony_ci * Returns true if the given @bridge is handled by the native PCIe hotplug 7898c2ecf20Sopenharmony_ci * driver. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_cibool pciehp_is_native(struct pci_dev *bridge) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci const struct pci_host_bridge *host; 7948c2ecf20Sopenharmony_ci u32 slot_cap; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE)) 7978c2ecf20Sopenharmony_ci return false; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci pcie_capability_read_dword(bridge, PCI_EXP_SLTCAP, &slot_cap); 8008c2ecf20Sopenharmony_ci if (!(slot_cap & PCI_EXP_SLTCAP_HPC)) 8018c2ecf20Sopenharmony_ci return false; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (pcie_ports_native) 8048c2ecf20Sopenharmony_ci return true; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci host = pci_find_host_bridge(bridge->bus); 8078c2ecf20Sopenharmony_ci return host->native_pcie_hotplug; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/** 8118c2ecf20Sopenharmony_ci * shpchp_is_native - Check whether a hotplug port is handled by the OS 8128c2ecf20Sopenharmony_ci * @bridge: Hotplug port to check 8138c2ecf20Sopenharmony_ci * 8148c2ecf20Sopenharmony_ci * Returns true if the given @bridge is handled by the native SHPC hotplug 8158c2ecf20Sopenharmony_ci * driver. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_cibool shpchp_is_native(struct pci_dev *bridge) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci return bridge->shpc_managed; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/** 8238c2ecf20Sopenharmony_ci * pci_acpi_wake_bus - Root bus wakeup notification fork function. 8248c2ecf20Sopenharmony_ci * @context: Device wakeup context. 8258c2ecf20Sopenharmony_ci */ 8268c2ecf20Sopenharmony_cistatic void pci_acpi_wake_bus(struct acpi_device_wakeup_context *context) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct acpi_device *adev; 8298c2ecf20Sopenharmony_ci struct acpi_pci_root *root; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci adev = container_of(context, struct acpi_device, wakeup.context); 8328c2ecf20Sopenharmony_ci root = acpi_driver_data(adev); 8338c2ecf20Sopenharmony_ci pci_pme_wakeup_bus(root->bus); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/** 8378c2ecf20Sopenharmony_ci * pci_acpi_wake_dev - PCI device wakeup notification work function. 8388c2ecf20Sopenharmony_ci * @context: Device wakeup context. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_cistatic void pci_acpi_wake_dev(struct acpi_device_wakeup_context *context) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci pci_dev = to_pci_dev(context->dev); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (pci_dev->pme_poll) 8478c2ecf20Sopenharmony_ci pci_dev->pme_poll = false; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (pci_dev->current_state == PCI_D3cold) { 8508c2ecf20Sopenharmony_ci pci_wakeup_event(pci_dev); 8518c2ecf20Sopenharmony_ci pm_request_resume(&pci_dev->dev); 8528c2ecf20Sopenharmony_ci return; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* Clear PME Status if set. */ 8568c2ecf20Sopenharmony_ci if (pci_dev->pme_support) 8578c2ecf20Sopenharmony_ci pci_check_pme_status(pci_dev); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci pci_wakeup_event(pci_dev); 8608c2ecf20Sopenharmony_ci pm_request_resume(&pci_dev->dev); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci pci_pme_wakeup_bus(pci_dev->subordinate); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci/** 8668c2ecf20Sopenharmony_ci * pci_acpi_add_bus_pm_notifier - Register PM notifier for root PCI bus. 8678c2ecf20Sopenharmony_ci * @dev: PCI root bridge ACPI device. 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_ciacpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci return acpi_add_pm_notifier(dev, NULL, pci_acpi_wake_bus); 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci/** 8758c2ecf20Sopenharmony_ci * pci_acpi_add_pm_notifier - Register PM notifier for given PCI device. 8768c2ecf20Sopenharmony_ci * @dev: ACPI device to add the notifier for. 8778c2ecf20Sopenharmony_ci * @pci_dev: PCI device to check for the PME status if an event is signaled. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_ciacpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, 8808c2ecf20Sopenharmony_ci struct pci_dev *pci_dev) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* 8868c2ecf20Sopenharmony_ci * _SxD returns the D-state with the highest power 8878c2ecf20Sopenharmony_ci * (lowest D-state number) supported in the S-state "x". 8888c2ecf20Sopenharmony_ci * 8898c2ecf20Sopenharmony_ci * If the devices does not have a _PRW 8908c2ecf20Sopenharmony_ci * (Power Resources for Wake) supporting system wakeup from "x" 8918c2ecf20Sopenharmony_ci * then the OS is free to choose a lower power (higher number 8928c2ecf20Sopenharmony_ci * D-state) than the return value from _SxD. 8938c2ecf20Sopenharmony_ci * 8948c2ecf20Sopenharmony_ci * But if _PRW is enabled at S-state "x", the OS 8958c2ecf20Sopenharmony_ci * must not choose a power lower than _SxD -- 8968c2ecf20Sopenharmony_ci * unless the device has an _SxW method specifying 8978c2ecf20Sopenharmony_ci * the lowest power (highest D-state number) the device 8988c2ecf20Sopenharmony_ci * may enter while still able to wake the system. 8998c2ecf20Sopenharmony_ci * 9008c2ecf20Sopenharmony_ci * ie. depending on global OS policy: 9018c2ecf20Sopenharmony_ci * 9028c2ecf20Sopenharmony_ci * if (_PRW at S-state x) 9038c2ecf20Sopenharmony_ci * choose from highest power _SxD to lowest power _SxW 9048c2ecf20Sopenharmony_ci * else // no _PRW at S-state x 9058c2ecf20Sopenharmony_ci * choose highest power _SxD or any lower power 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci int acpi_state, d_max; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (pdev->no_d3cold || !pdev->d3cold_allowed) 9138c2ecf20Sopenharmony_ci d_max = ACPI_STATE_D3_HOT; 9148c2ecf20Sopenharmony_ci else 9158c2ecf20Sopenharmony_ci d_max = ACPI_STATE_D3_COLD; 9168c2ecf20Sopenharmony_ci acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max); 9178c2ecf20Sopenharmony_ci if (acpi_state < 0) 9188c2ecf20Sopenharmony_ci return PCI_POWER_ERROR; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci switch (acpi_state) { 9218c2ecf20Sopenharmony_ci case ACPI_STATE_D0: 9228c2ecf20Sopenharmony_ci return PCI_D0; 9238c2ecf20Sopenharmony_ci case ACPI_STATE_D1: 9248c2ecf20Sopenharmony_ci return PCI_D1; 9258c2ecf20Sopenharmony_ci case ACPI_STATE_D2: 9268c2ecf20Sopenharmony_ci return PCI_D2; 9278c2ecf20Sopenharmony_ci case ACPI_STATE_D3_HOT: 9288c2ecf20Sopenharmony_ci return PCI_D3hot; 9298c2ecf20Sopenharmony_ci case ACPI_STATE_D3_COLD: 9308c2ecf20Sopenharmony_ci return PCI_D3cold; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci return PCI_POWER_ERROR; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic struct acpi_device *acpi_pci_find_companion(struct device *dev); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic bool acpi_pci_bridge_d3(struct pci_dev *dev) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci const struct fwnode_handle *fwnode; 9408c2ecf20Sopenharmony_ci struct acpi_device *adev; 9418c2ecf20Sopenharmony_ci struct pci_dev *root; 9428c2ecf20Sopenharmony_ci u8 val; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (!dev->is_hotplug_bridge) 9458c2ecf20Sopenharmony_ci return false; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Assume D3 support if the bridge is power-manageable by ACPI. */ 9488c2ecf20Sopenharmony_ci adev = ACPI_COMPANION(&dev->dev); 9498c2ecf20Sopenharmony_ci if (!adev && !pci_dev_is_added(dev)) { 9508c2ecf20Sopenharmony_ci adev = acpi_pci_find_companion(&dev->dev); 9518c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(&dev->dev, adev); 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (adev && acpi_device_power_manageable(adev)) 9558c2ecf20Sopenharmony_ci return true; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci * Look for a special _DSD property for the root port and if it 9598c2ecf20Sopenharmony_ci * is set we know the hierarchy behind it supports D3 just fine. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_ci root = pcie_find_root_port(dev); 9628c2ecf20Sopenharmony_ci if (!root) 9638c2ecf20Sopenharmony_ci return false; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci adev = ACPI_COMPANION(&root->dev); 9668c2ecf20Sopenharmony_ci if (root == dev) { 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * It is possible that the ACPI companion is not yet bound 9698c2ecf20Sopenharmony_ci * for the root port so look it up manually here. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci if (!adev && !pci_dev_is_added(root)) 9728c2ecf20Sopenharmony_ci adev = acpi_pci_find_companion(&root->dev); 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (!adev) 9768c2ecf20Sopenharmony_ci return false; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci fwnode = acpi_fwnode_handle(adev); 9798c2ecf20Sopenharmony_ci if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val)) 9808c2ecf20Sopenharmony_ci return false; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci return val == 1; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic bool acpi_pci_power_manageable(struct pci_dev *dev) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 9888c2ecf20Sopenharmony_ci return adev ? acpi_device_power_manageable(adev) : false; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 9948c2ecf20Sopenharmony_ci static const u8 state_conv[] = { 9958c2ecf20Sopenharmony_ci [PCI_D0] = ACPI_STATE_D0, 9968c2ecf20Sopenharmony_ci [PCI_D1] = ACPI_STATE_D1, 9978c2ecf20Sopenharmony_ci [PCI_D2] = ACPI_STATE_D2, 9988c2ecf20Sopenharmony_ci [PCI_D3hot] = ACPI_STATE_D3_HOT, 9998c2ecf20Sopenharmony_ci [PCI_D3cold] = ACPI_STATE_D3_COLD, 10008c2ecf20Sopenharmony_ci }; 10018c2ecf20Sopenharmony_ci int error = -EINVAL; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* If the ACPI device has _EJ0, ignore the device */ 10048c2ecf20Sopenharmony_ci if (!adev || acpi_has_method(adev->handle, "_EJ0")) 10058c2ecf20Sopenharmony_ci return -ENODEV; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci switch (state) { 10088c2ecf20Sopenharmony_ci case PCI_D3cold: 10098c2ecf20Sopenharmony_ci if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == 10108c2ecf20Sopenharmony_ci PM_QOS_FLAGS_ALL) { 10118c2ecf20Sopenharmony_ci error = -EBUSY; 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci fallthrough; 10158c2ecf20Sopenharmony_ci case PCI_D0: 10168c2ecf20Sopenharmony_ci case PCI_D1: 10178c2ecf20Sopenharmony_ci case PCI_D2: 10188c2ecf20Sopenharmony_ci case PCI_D3hot: 10198c2ecf20Sopenharmony_ci error = acpi_device_set_power(adev, state_conv[state]); 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!error) 10238c2ecf20Sopenharmony_ci pci_dbg(dev, "power state changed by ACPI to %s\n", 10248c2ecf20Sopenharmony_ci acpi_power_state_string(state_conv[state])); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return error; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 10328c2ecf20Sopenharmony_ci static const pci_power_t state_conv[] = { 10338c2ecf20Sopenharmony_ci [ACPI_STATE_D0] = PCI_D0, 10348c2ecf20Sopenharmony_ci [ACPI_STATE_D1] = PCI_D1, 10358c2ecf20Sopenharmony_ci [ACPI_STATE_D2] = PCI_D2, 10368c2ecf20Sopenharmony_ci [ACPI_STATE_D3_HOT] = PCI_D3hot, 10378c2ecf20Sopenharmony_ci [ACPI_STATE_D3_COLD] = PCI_D3cold, 10388c2ecf20Sopenharmony_ci }; 10398c2ecf20Sopenharmony_ci int state; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (!adev || !acpi_device_power_manageable(adev)) 10428c2ecf20Sopenharmony_ci return PCI_UNKNOWN; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci state = adev->power.state; 10458c2ecf20Sopenharmony_ci if (state == ACPI_STATE_UNKNOWN) 10468c2ecf20Sopenharmony_ci return PCI_UNKNOWN; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci return state_conv[state]; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic void acpi_pci_refresh_power_state(struct pci_dev *dev) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (adev && acpi_device_power_manageable(adev)) 10568c2ecf20Sopenharmony_ci acpi_device_update_power(adev, NULL); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci while (bus->parent) { 10628c2ecf20Sopenharmony_ci if (acpi_pm_device_can_wakeup(&bus->self->dev)) 10638c2ecf20Sopenharmony_ci return acpi_pm_set_device_wakeup(&bus->self->dev, enable); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci bus = bus->parent; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* We have reached the root bus. */ 10698c2ecf20Sopenharmony_ci if (bus->bridge) { 10708c2ecf20Sopenharmony_ci if (acpi_pm_device_can_wakeup(bus->bridge)) 10718c2ecf20Sopenharmony_ci return acpi_pm_set_device_wakeup(bus->bridge, enable); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci return 0; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int acpi_pci_wakeup(struct pci_dev *dev, bool enable) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci if (acpi_pm_device_can_wakeup(&dev->dev)) 10798c2ecf20Sopenharmony_ci return acpi_pm_set_device_wakeup(&dev->dev, enable); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci return acpi_pci_propagate_wakeup(dev->bus, enable); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic bool acpi_pci_need_resume(struct pci_dev *dev) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* 10898c2ecf20Sopenharmony_ci * In some cases (eg. Samsung 305V4A) leaving a bridge in suspend over 10908c2ecf20Sopenharmony_ci * system-wide suspend/resume confuses the platform firmware, so avoid 10918c2ecf20Sopenharmony_ci * doing that. According to Section 16.1.6 of ACPI 6.2, endpoint 10928c2ecf20Sopenharmony_ci * devices are expected to be in D3 before invoking the S3 entry path 10938c2ecf20Sopenharmony_ci * from the firmware, so they should not be affected by this issue. 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci if (pci_is_bridge(dev) && acpi_target_system_state() != ACPI_STATE_S0) 10968c2ecf20Sopenharmony_ci return true; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (!adev || !acpi_device_power_manageable(adev)) 10998c2ecf20Sopenharmony_ci return false; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (adev->wakeup.flags.valid && 11028c2ecf20Sopenharmony_ci device_may_wakeup(&dev->dev) != !!adev->wakeup.prepare_count) 11038c2ecf20Sopenharmony_ci return true; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (acpi_target_system_state() == ACPI_STATE_S0) 11068c2ecf20Sopenharmony_ci return false; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci return !!adev->power.flags.dsw_present; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic const struct pci_platform_pm_ops acpi_pci_platform_pm = { 11128c2ecf20Sopenharmony_ci .bridge_d3 = acpi_pci_bridge_d3, 11138c2ecf20Sopenharmony_ci .is_manageable = acpi_pci_power_manageable, 11148c2ecf20Sopenharmony_ci .set_state = acpi_pci_set_power_state, 11158c2ecf20Sopenharmony_ci .get_state = acpi_pci_get_power_state, 11168c2ecf20Sopenharmony_ci .refresh_state = acpi_pci_refresh_power_state, 11178c2ecf20Sopenharmony_ci .choose_state = acpi_pci_choose_state, 11188c2ecf20Sopenharmony_ci .set_wakeup = acpi_pci_wakeup, 11198c2ecf20Sopenharmony_ci .need_resume = acpi_pci_need_resume, 11208c2ecf20Sopenharmony_ci}; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_civoid acpi_pci_add_bus(struct pci_bus *bus) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci union acpi_object *obj; 11258c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (acpi_pci_disabled || !bus->bridge || !ACPI_HANDLE(bus->bridge)) 11288c2ecf20Sopenharmony_ci return; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci acpi_pci_slot_enumerate(bus); 11318c2ecf20Sopenharmony_ci acpiphp_enumerate_slots(bus); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* 11348c2ecf20Sopenharmony_ci * For a host bridge, check its _DSM for function 8 and if 11358c2ecf20Sopenharmony_ci * that is available, mark it in pci_host_bridge. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci if (!pci_is_root_bus(bus)) 11388c2ecf20Sopenharmony_ci return; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 3, 11418c2ecf20Sopenharmony_ci DSM_PCI_POWER_ON_RESET_DELAY, NULL); 11428c2ecf20Sopenharmony_ci if (!obj) 11438c2ecf20Sopenharmony_ci return; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) { 11468c2ecf20Sopenharmony_ci bridge = pci_find_host_bridge(bus); 11478c2ecf20Sopenharmony_ci bridge->ignore_reset_delay = 1; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci ACPI_FREE(obj); 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_civoid acpi_pci_remove_bus(struct pci_bus *bus) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci if (acpi_pci_disabled || !bus->bridge) 11558c2ecf20Sopenharmony_ci return; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci acpiphp_remove_slots(bus); 11588c2ecf20Sopenharmony_ci acpi_pci_slot_remove(bus); 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci/* ACPI bus type */ 11628c2ecf20Sopenharmony_cistatic struct acpi_device *acpi_pci_find_companion(struct device *dev) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 11658c2ecf20Sopenharmony_ci bool check_children; 11668c2ecf20Sopenharmony_ci u64 addr; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci check_children = pci_is_bridge(pci_dev); 11698c2ecf20Sopenharmony_ci /* Please ref to ACPI spec for the syntax of _ADR */ 11708c2ecf20Sopenharmony_ci addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); 11718c2ecf20Sopenharmony_ci return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr, 11728c2ecf20Sopenharmony_ci check_children); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/** 11768c2ecf20Sopenharmony_ci * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI 11778c2ecf20Sopenharmony_ci * @pdev: the PCI device whose delay is to be updated 11788c2ecf20Sopenharmony_ci * @handle: ACPI handle of this device 11798c2ecf20Sopenharmony_ci * 11808c2ecf20Sopenharmony_ci * Update the d3hot_delay and d3cold_delay of a PCI device from the ACPI _DSM 11818c2ecf20Sopenharmony_ci * control method of either the device itself or the PCI host bridge. 11828c2ecf20Sopenharmony_ci * 11838c2ecf20Sopenharmony_ci * Function 8, "Reset Delay," applies to the entire hierarchy below a PCI 11848c2ecf20Sopenharmony_ci * host bridge. If it returns one, the OS may assume that all devices in 11858c2ecf20Sopenharmony_ci * the hierarchy have already completed power-on reset delays. 11868c2ecf20Sopenharmony_ci * 11878c2ecf20Sopenharmony_ci * Function 9, "Device Readiness Durations," applies only to the object 11888c2ecf20Sopenharmony_ci * where it is located. It returns delay durations required after various 11898c2ecf20Sopenharmony_ci * events if the device requires less time than the spec requires. Delays 11908c2ecf20Sopenharmony_ci * from this function take precedence over the Reset Delay function. 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * These _DSM functions are defined by the draft ECN of January 28, 2014, 11938c2ecf20Sopenharmony_ci * titled "ACPI additions for FW latency optimizations." 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_cistatic void pci_acpi_optimize_delay(struct pci_dev *pdev, 11968c2ecf20Sopenharmony_ci acpi_handle handle) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); 11998c2ecf20Sopenharmony_ci int value; 12008c2ecf20Sopenharmony_ci union acpi_object *obj, *elements; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (bridge->ignore_reset_delay) 12038c2ecf20Sopenharmony_ci pdev->d3cold_delay = 0; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 3, 12068c2ecf20Sopenharmony_ci DSM_PCI_DEVICE_READINESS_DURATIONS, NULL); 12078c2ecf20Sopenharmony_ci if (!obj) 12088c2ecf20Sopenharmony_ci return; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) { 12118c2ecf20Sopenharmony_ci elements = obj->package.elements; 12128c2ecf20Sopenharmony_ci if (elements[0].type == ACPI_TYPE_INTEGER) { 12138c2ecf20Sopenharmony_ci value = (int)elements[0].integer.value / 1000; 12148c2ecf20Sopenharmony_ci if (value < PCI_PM_D3COLD_WAIT) 12158c2ecf20Sopenharmony_ci pdev->d3cold_delay = value; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci if (elements[3].type == ACPI_TYPE_INTEGER) { 12188c2ecf20Sopenharmony_ci value = (int)elements[3].integer.value / 1000; 12198c2ecf20Sopenharmony_ci if (value < PCI_PM_D3HOT_WAIT) 12208c2ecf20Sopenharmony_ci pdev->d3hot_delay = value; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci ACPI_FREE(obj); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic void pci_acpi_set_external_facing(struct pci_dev *dev) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci u8 val; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) 12318c2ecf20Sopenharmony_ci return; 12328c2ecf20Sopenharmony_ci if (device_property_read_u8(&dev->dev, "ExternalFacingPort", &val)) 12338c2ecf20Sopenharmony_ci return; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* 12368c2ecf20Sopenharmony_ci * These root ports expose PCIe (including DMA) outside of the 12378c2ecf20Sopenharmony_ci * system. Everything downstream from them is external. 12388c2ecf20Sopenharmony_ci */ 12398c2ecf20Sopenharmony_ci if (val) 12408c2ecf20Sopenharmony_ci dev->external_facing = 1; 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic void pci_acpi_setup(struct device *dev) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 12468c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (!adev) 12498c2ecf20Sopenharmony_ci return; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci pci_acpi_optimize_delay(pci_dev, adev->handle); 12528c2ecf20Sopenharmony_ci pci_acpi_set_external_facing(pci_dev); 12538c2ecf20Sopenharmony_ci pci_acpi_add_edr_notifier(pci_dev); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci pci_acpi_add_pm_notifier(adev, pci_dev); 12568c2ecf20Sopenharmony_ci if (!adev->wakeup.flags.valid) 12578c2ecf20Sopenharmony_ci return; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, true); 12608c2ecf20Sopenharmony_ci /* 12618c2ecf20Sopenharmony_ci * For bridges that can do D3 we enable wake automatically (as 12628c2ecf20Sopenharmony_ci * we do for the power management itself in that case). The 12638c2ecf20Sopenharmony_ci * reason is that the bridge may have additional methods such as 12648c2ecf20Sopenharmony_ci * _DSW that need to be called. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci if (pci_dev->bridge_d3) 12678c2ecf20Sopenharmony_ci device_wakeup_enable(dev); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci acpi_pci_wakeup(pci_dev, false); 12708c2ecf20Sopenharmony_ci acpi_device_power_add_dependent(adev, dev); 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void pci_acpi_cleanup(struct device *dev) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 12768c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (!adev) 12798c2ecf20Sopenharmony_ci return; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci pci_acpi_remove_edr_notifier(pci_dev); 12828c2ecf20Sopenharmony_ci pci_acpi_remove_pm_notifier(adev); 12838c2ecf20Sopenharmony_ci if (adev->wakeup.flags.valid) { 12848c2ecf20Sopenharmony_ci acpi_device_power_remove_dependent(adev, dev); 12858c2ecf20Sopenharmony_ci if (pci_dev->bridge_d3) 12868c2ecf20Sopenharmony_ci device_wakeup_disable(dev); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, false); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic bool pci_acpi_bus_match(struct device *dev) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci return dev_is_pci(dev); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic struct acpi_bus_type acpi_pci_bus = { 12988c2ecf20Sopenharmony_ci .name = "PCI", 12998c2ecf20Sopenharmony_ci .match = pci_acpi_bus_match, 13008c2ecf20Sopenharmony_ci .find_companion = acpi_pci_find_companion, 13018c2ecf20Sopenharmony_ci .setup = pci_acpi_setup, 13028c2ecf20Sopenharmony_ci .cleanup = pci_acpi_cleanup, 13038c2ecf20Sopenharmony_ci}; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic struct fwnode_handle *(*pci_msi_get_fwnode_cb)(struct device *dev); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci/** 13098c2ecf20Sopenharmony_ci * pci_msi_register_fwnode_provider - Register callback to retrieve fwnode 13108c2ecf20Sopenharmony_ci * @fn: Callback matching a device to a fwnode that identifies a PCI 13118c2ecf20Sopenharmony_ci * MSI domain. 13128c2ecf20Sopenharmony_ci * 13138c2ecf20Sopenharmony_ci * This should be called by irqchip driver, which is the parent of 13148c2ecf20Sopenharmony_ci * the MSI domain to provide callback interface to query fwnode. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_civoid 13178c2ecf20Sopenharmony_cipci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *)) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci pci_msi_get_fwnode_cb = fn; 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci/** 13238c2ecf20Sopenharmony_ci * pci_host_bridge_acpi_msi_domain - Retrieve MSI domain of a PCI host bridge 13248c2ecf20Sopenharmony_ci * @bus: The PCI host bridge bus. 13258c2ecf20Sopenharmony_ci * 13268c2ecf20Sopenharmony_ci * This function uses the callback function registered by 13278c2ecf20Sopenharmony_ci * pci_msi_register_fwnode_provider() to retrieve the irq_domain with 13288c2ecf20Sopenharmony_ci * type DOMAIN_BUS_PCI_MSI of the specified host bridge bus. 13298c2ecf20Sopenharmony_ci * This returns NULL on error or when the domain is not found. 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_cistruct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (!pci_msi_get_fwnode_cb) 13368c2ecf20Sopenharmony_ci return NULL; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci fwnode = pci_msi_get_fwnode_cb(&bus->dev); 13398c2ecf20Sopenharmony_ci if (!fwnode) 13408c2ecf20Sopenharmony_ci return NULL; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci return irq_find_matching_fwnode(fwnode, DOMAIN_BUS_PCI_MSI); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic int __init acpi_pci_init(void) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci int ret; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) { 13508c2ecf20Sopenharmony_ci pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n"); 13518c2ecf20Sopenharmony_ci pci_no_msi(); 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { 13558c2ecf20Sopenharmony_ci pr_info("ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); 13568c2ecf20Sopenharmony_ci pcie_no_aspm(); 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci ret = register_acpi_bus_type(&acpi_pci_bus); 13608c2ecf20Sopenharmony_ci if (ret) 13618c2ecf20Sopenharmony_ci return 0; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci pci_set_platform_pm(&acpi_pci_platform_pm); 13648c2ecf20Sopenharmony_ci acpi_pci_slot_init(); 13658c2ecf20Sopenharmony_ci acpiphp_init(); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ciarch_initcall(acpi_pci_init); 1370