18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Common ACPI functions for hot plug platforms 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * All rights reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Send feedback to <kristen.c.accardi@intel.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/pci.h> 178c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h> 188c2ecf20Sopenharmony_ci#include <linux/acpi.h> 198c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MY_NAME "acpi_pcihp" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0) 258c2ecf20Sopenharmony_ci#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg) 268c2ecf20Sopenharmony_ci#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg) 278c2ecf20Sopenharmony_ci#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define METHOD_NAME__SUN "_SUN" 308c2ecf20Sopenharmony_ci#define METHOD_NAME_OSHP "OSHP" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic bool debug_acpi; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* acpi_run_oshp - get control of hotplug from the firmware 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * @handle - the handle of the hotplug controller. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic acpi_status acpi_run_oshp(acpi_handle handle) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci acpi_status status; 418c2ecf20Sopenharmony_ci struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* run OSHP */ 468c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); 478c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 488c2ecf20Sopenharmony_ci if (status != AE_NOT_FOUND) 498c2ecf20Sopenharmony_ci printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", 508c2ecf20Sopenharmony_ci __func__, (char *)string.pointer, status); 518c2ecf20Sopenharmony_ci else 528c2ecf20Sopenharmony_ci dbg("%s:%s OSHP not found\n", 538c2ecf20Sopenharmony_ci __func__, (char *)string.pointer); 548c2ecf20Sopenharmony_ci else 558c2ecf20Sopenharmony_ci pr_debug("%s:%s OSHP passes\n", __func__, 568c2ecf20Sopenharmony_ci (char *)string.pointer); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci kfree(string.pointer); 598c2ecf20Sopenharmony_ci return status; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * acpi_get_hp_hw_control_from_firmware 648c2ecf20Sopenharmony_ci * @pdev: the pci_dev of the bridge that has a hotplug controller 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * Attempt to take hotplug control from firmware. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ciint acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci const struct pci_host_bridge *host; 718c2ecf20Sopenharmony_ci const struct acpi_pci_root *root; 728c2ecf20Sopenharmony_ci acpi_status status; 738c2ecf20Sopenharmony_ci acpi_handle chandle, handle; 748c2ecf20Sopenharmony_ci struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * If there's no ACPI host bridge (i.e., ACPI support is compiled 788c2ecf20Sopenharmony_ci * into the kernel but the hardware platform doesn't support ACPI), 798c2ecf20Sopenharmony_ci * there's nothing to do here. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci host = pci_find_host_bridge(pdev->bus); 828c2ecf20Sopenharmony_ci root = acpi_pci_find_root(ACPI_HANDLE(&host->dev)); 838c2ecf20Sopenharmony_ci if (!root) 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * If _OSC exists, it determines whether we're allowed to manage 888c2ecf20Sopenharmony_ci * the SHPC. We executed it while enumerating the host bridge. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (root->osc_support_set) { 918c2ecf20Sopenharmony_ci if (host->native_shpc_hotplug) 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci return -ENODEV; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * In the absence of _OSC, we're always allowed to manage the SHPC. 988c2ecf20Sopenharmony_ci * However, if an OSHP method is present, we must execute it so the 998c2ecf20Sopenharmony_ci * firmware can transfer control to the OS, e.g., direct interrupts 1008c2ecf20Sopenharmony_ci * to the OS instead of to the firmware. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse 1038c2ecf20Sopenharmony_ci * searching up the ACPI hierarchy, so the loops below are suspect. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(&pdev->dev); 1068c2ecf20Sopenharmony_ci if (!handle) { 1078c2ecf20Sopenharmony_ci /* 1088c2ecf20Sopenharmony_ci * This hotplug controller was not listed in the ACPI name 1098c2ecf20Sopenharmony_ci * space at all. Try to get ACPI handle of parent PCI bus. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci struct pci_bus *pbus; 1128c2ecf20Sopenharmony_ci for (pbus = pdev->bus; pbus; pbus = pbus->parent) { 1138c2ecf20Sopenharmony_ci handle = acpi_pci_get_bridge_handle(pbus); 1148c2ecf20Sopenharmony_ci if (handle) 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci while (handle) { 1208c2ecf20Sopenharmony_ci acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); 1218c2ecf20Sopenharmony_ci pci_info(pdev, "Requesting control of SHPC hotplug via OSHP (%s)\n", 1228c2ecf20Sopenharmony_ci (char *)string.pointer); 1238c2ecf20Sopenharmony_ci status = acpi_run_oshp(handle); 1248c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 1258c2ecf20Sopenharmony_ci goto got_one; 1268c2ecf20Sopenharmony_ci if (acpi_is_root_bridge(handle)) 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci chandle = handle; 1298c2ecf20Sopenharmony_ci status = acpi_get_parent(chandle, &handle); 1308c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci pci_info(pdev, "Cannot get control of SHPC hotplug\n"); 1358c2ecf20Sopenharmony_ci kfree(string.pointer); 1368c2ecf20Sopenharmony_ci return -ENODEV; 1378c2ecf20Sopenharmony_cigot_one: 1388c2ecf20Sopenharmony_ci pci_info(pdev, "Gained control of SHPC hotplug (%s)\n", 1398c2ecf20Sopenharmony_ci (char *)string.pointer); 1408c2ecf20Sopenharmony_ci kfree(string.pointer); 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int pcihp_is_ejectable(acpi_handle handle) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci acpi_status status; 1488c2ecf20Sopenharmony_ci unsigned long long removable; 1498c2ecf20Sopenharmony_ci if (!acpi_has_method(handle, "_ADR")) 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci if (acpi_has_method(handle, "_EJ0")) 1528c2ecf20Sopenharmony_ci return 1; 1538c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); 1548c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status) && removable) 1558c2ecf20Sopenharmony_ci return 1; 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot 1618c2ecf20Sopenharmony_ci * @pbus: the PCI bus of the PCI slot corresponding to 'handle' 1628c2ecf20Sopenharmony_ci * @handle: ACPI handle to check 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Return 1 if handle is ejectable PCI slot, 0 otherwise. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ciint acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci acpi_handle bridge_handle, parent_handle; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci bridge_handle = acpi_pci_get_bridge_handle(pbus); 1718c2ecf20Sopenharmony_ci if (!bridge_handle) 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle)))) 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci if (bridge_handle != parent_handle) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci return pcihp_is_ejectable(handle); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_pci_check_ejectable); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic acpi_status 1828c2ecf20Sopenharmony_cicheck_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int *found = (int *)context; 1858c2ecf20Sopenharmony_ci if (pcihp_is_ejectable(handle)) { 1868c2ecf20Sopenharmony_ci *found = 1; 1878c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci return AE_OK; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/** 1938c2ecf20Sopenharmony_ci * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots 1948c2ecf20Sopenharmony_ci * @handle: handle of the PCI bus to scan 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ciint acpi_pci_detect_ejectable(acpi_handle handle) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int found = 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!handle) 2038c2ecf20Sopenharmony_ci return found; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 2068c2ecf20Sopenharmony_ci check_hotplug, NULL, (void *)&found, NULL); 2078c2ecf20Sopenharmony_ci return found; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cimodule_param(debug_acpi, bool, 0644); 2128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); 213