acpi_pcihp.c (5839B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Common ACPI functions for hot plug platforms 4 * 5 * Copyright (C) 2006 Intel Corporation 6 * 7 * All rights reserved. 8 * 9 * Send feedback to <kristen.c.accardi@intel.com> 10 */ 11 12#include <linux/module.h> 13#include <linux/moduleparam.h> 14#include <linux/kernel.h> 15#include <linux/types.h> 16#include <linux/pci.h> 17#include <linux/pci_hotplug.h> 18#include <linux/acpi.h> 19#include <linux/pci-acpi.h> 20#include <linux/slab.h> 21 22#define MY_NAME "acpi_pcihp" 23 24#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0) 25#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg) 26#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg) 27#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg) 28 29#define METHOD_NAME__SUN "_SUN" 30#define METHOD_NAME_OSHP "OSHP" 31 32static bool debug_acpi; 33 34/* acpi_run_oshp - get control of hotplug from the firmware 35 * 36 * @handle - the handle of the hotplug controller. 37 */ 38static acpi_status acpi_run_oshp(acpi_handle handle) 39{ 40 acpi_status status; 41 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 42 43 acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); 44 45 /* run OSHP */ 46 status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL); 47 if (ACPI_FAILURE(status)) 48 if (status != AE_NOT_FOUND) 49 printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", 50 __func__, (char *)string.pointer, status); 51 else 52 dbg("%s:%s OSHP not found\n", 53 __func__, (char *)string.pointer); 54 else 55 pr_debug("%s:%s OSHP passes\n", __func__, 56 (char *)string.pointer); 57 58 kfree(string.pointer); 59 return status; 60} 61 62/** 63 * acpi_get_hp_hw_control_from_firmware 64 * @pdev: the pci_dev of the bridge that has a hotplug controller 65 * 66 * Attempt to take hotplug control from firmware. 67 */ 68int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) 69{ 70 const struct pci_host_bridge *host; 71 const struct acpi_pci_root *root; 72 acpi_status status; 73 acpi_handle chandle, handle; 74 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 75 76 /* 77 * If there's no ACPI host bridge (i.e., ACPI support is compiled 78 * into the kernel but the hardware platform doesn't support ACPI), 79 * there's nothing to do here. 80 */ 81 host = pci_find_host_bridge(pdev->bus); 82 root = acpi_pci_find_root(ACPI_HANDLE(&host->dev)); 83 if (!root) 84 return 0; 85 86 /* 87 * If _OSC exists, it determines whether we're allowed to manage 88 * the SHPC. We executed it while enumerating the host bridge. 89 */ 90 if (root->osc_support_set) { 91 if (host->native_shpc_hotplug) 92 return 0; 93 return -ENODEV; 94 } 95 96 /* 97 * In the absence of _OSC, we're always allowed to manage the SHPC. 98 * However, if an OSHP method is present, we must execute it so the 99 * firmware can transfer control to the OS, e.g., direct interrupts 100 * to the OS instead of to the firmware. 101 * 102 * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse 103 * searching up the ACPI hierarchy, so the loops below are suspect. 104 */ 105 handle = ACPI_HANDLE(&pdev->dev); 106 if (!handle) { 107 /* 108 * This hotplug controller was not listed in the ACPI name 109 * space at all. Try to get ACPI handle of parent PCI bus. 110 */ 111 struct pci_bus *pbus; 112 for (pbus = pdev->bus; pbus; pbus = pbus->parent) { 113 handle = acpi_pci_get_bridge_handle(pbus); 114 if (handle) 115 break; 116 } 117 } 118 119 while (handle) { 120 acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); 121 pci_info(pdev, "Requesting control of SHPC hotplug via OSHP (%s)\n", 122 (char *)string.pointer); 123 status = acpi_run_oshp(handle); 124 if (ACPI_SUCCESS(status)) 125 goto got_one; 126 if (acpi_is_root_bridge(handle)) 127 break; 128 chandle = handle; 129 status = acpi_get_parent(chandle, &handle); 130 if (ACPI_FAILURE(status)) 131 break; 132 } 133 134 pci_info(pdev, "Cannot get control of SHPC hotplug\n"); 135 kfree(string.pointer); 136 return -ENODEV; 137got_one: 138 pci_info(pdev, "Gained control of SHPC hotplug (%s)\n", 139 (char *)string.pointer); 140 kfree(string.pointer); 141 return 0; 142} 143EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); 144 145static int pcihp_is_ejectable(acpi_handle handle) 146{ 147 acpi_status status; 148 unsigned long long removable; 149 if (!acpi_has_method(handle, "_ADR")) 150 return 0; 151 if (acpi_has_method(handle, "_EJ0")) 152 return 1; 153 status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); 154 if (ACPI_SUCCESS(status) && removable) 155 return 1; 156 return 0; 157} 158 159/** 160 * acpi_pci_check_ejectable - check if handle is ejectable ACPI PCI slot 161 * @pbus: the PCI bus of the PCI slot corresponding to 'handle' 162 * @handle: ACPI handle to check 163 * 164 * Return 1 if handle is ejectable PCI slot, 0 otherwise. 165 */ 166int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) 167{ 168 acpi_handle bridge_handle, parent_handle; 169 170 bridge_handle = acpi_pci_get_bridge_handle(pbus); 171 if (!bridge_handle) 172 return 0; 173 if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle)))) 174 return 0; 175 if (bridge_handle != parent_handle) 176 return 0; 177 return pcihp_is_ejectable(handle); 178} 179EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable); 180 181static acpi_status 182check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) 183{ 184 int *found = (int *)context; 185 if (pcihp_is_ejectable(handle)) { 186 *found = 1; 187 return AE_CTRL_TERMINATE; 188 } 189 return AE_OK; 190} 191 192/** 193 * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots 194 * @handle: handle of the PCI bus to scan 195 * 196 * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. 197 */ 198int acpi_pci_detect_ejectable(acpi_handle handle) 199{ 200 int found = 0; 201 202 if (!handle) 203 return found; 204 205 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 206 check_hotplug, NULL, (void *)&found, NULL); 207 return found; 208} 209EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); 210 211module_param(debug_acpi, bool, 0644); 212MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not");