Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: shpchp: Separate existence of SHPC and permission to use it

The shpchp driver registers for all PCI bridge devices. Its probe method
should fail if either (1) the bridge doesn't have an SHPC or (2) the OS
isn't allowed to use it (the platform firmware may be operating the SHPC
itself).

Separate these two tests into:

- A new shpc_capable() that looks for the SHPC hardware and is applicable
on all systems (ACPI and non-ACPI), and

- A simplified acpi_get_hp_hw_control_from_firmware() that we call only
when we already know an SHPC exists and there may be ACPI methods to
either request permission to use it (_OSC) or transfer control to the
OS (OSHP).

acpi_get_hp_hw_control_from_firmware() is implemented when CONFIG_ACPI=y,
but does nothing if the current platform doesn't support ACPI.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>

authored by

Bjorn Helgaas and committed by
Bjorn Helgaas
b03799b0 6f6f4246

+42 -35
+19 -17
drivers/pci/hotplug/acpi_pcihp.c
··· 74 74 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 75 75 76 76 /* 77 - * Per PCI firmware specification, we should run the ACPI _OSC 78 - * method to get control of hotplug hardware before using it. If 79 - * an _OSC is missing, we look for an OSHP to do the same thing. 80 - * To handle different BIOS behavior, we look for _OSC on a root 81 - * bridge preferentially (according to PCI fw spec). Later for 82 - * OSHP within the scope of the hotplug controller and its parents, 83 - * up to the host bridge under which this controller exists. 84 - */ 85 - if (shpchp_is_native(pdev)) 86 - return 0; 87 - 88 - /* If _OSC exists, we should not evaluate OSHP */ 89 - 90 - /* 91 77 * If there's no ACPI host bridge (i.e., ACPI support is compiled 92 78 * into the kernel but the hardware platform doesn't support ACPI), 93 79 * there's nothing to do here. ··· 83 97 if (!root) 84 98 return 0; 85 99 86 - if (root->osc_support_set) 87 - goto no_control; 100 + /* 101 + * If _OSC exists, it determines whether we're allowed to manage 102 + * the SHPC. We executed it while enumerating the host bridge. 103 + */ 104 + if (root->osc_support_set) { 105 + if (host->native_shpc_hotplug) 106 + return 0; 107 + return -ENODEV; 108 + } 88 109 110 + /* 111 + * In the absence of _OSC, we're always allowed to manage the SHPC. 112 + * However, if an OSHP method is present, we must execute it so the 113 + * firmware can transfer control to the OS, e.g., direct interrupts 114 + * to the OS instead of to the firmware. 115 + * 116 + * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse 117 + * searching up the ACPI hierarchy, so the loops below are suspect. 118 + */ 89 119 handle = ACPI_HANDLE(&pdev->dev); 90 120 if (!handle) { 91 121 /* ··· 130 128 if (ACPI_FAILURE(status)) 131 129 break; 132 130 } 133 - no_control: 131 + 134 132 pci_info(pdev, "Cannot get control of SHPC hotplug\n"); 135 133 kfree(string.pointer); 136 134 return -ENODEV;
+21
drivers/pci/hotplug/shpchp_core.c
··· 270 270 return 0; 271 271 } 272 272 273 + static bool shpc_capable(struct pci_dev *bridge) 274 + { 275 + /* 276 + * It is assumed that AMD GOLAM chips support SHPC but they do not 277 + * have SHPC capability. 278 + */ 279 + if (bridge->vendor == PCI_VENDOR_ID_AMD && 280 + bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450) 281 + return true; 282 + 283 + if (pci_find_capability(bridge, PCI_CAP_ID_SHPC)) 284 + return true; 285 + 286 + return false; 287 + } 288 + 273 289 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 274 290 { 275 291 int rc; 276 292 struct controller *ctrl; 293 + 294 + if (!shpc_capable(pdev)) 295 + return -ENODEV; 277 296 278 297 if (acpi_get_hp_hw_control_from_firmware(pdev)) 279 298 return -ENODEV; ··· 322 303 if (rc) 323 304 goto err_cleanup_slots; 324 305 306 + pdev->shpc_managed = 1; 325 307 return 0; 326 308 327 309 err_cleanup_slots: ··· 339 319 { 340 320 struct controller *ctrl = pci_get_drvdata(dev); 341 321 322 + dev->shpc_managed = 0; 342 323 shpchp_remove_ctrl_files(ctrl); 343 324 ctrl->hpc_ops->release_ctlr(ctrl); 344 325 kfree(ctrl);
+1 -18
drivers/pci/pci-acpi.c
··· 403 403 */ 404 404 bool shpchp_is_native(struct pci_dev *bridge) 405 405 { 406 - const struct pci_host_bridge *host; 407 - 408 - if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC)) 409 - return false; 410 - 411 - /* 412 - * It is assumed that AMD GOLAM chips support SHPC but they do not 413 - * have SHPC capability. 414 - */ 415 - if (bridge->vendor == PCI_VENDOR_ID_AMD && 416 - bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450) 417 - return true; 418 - 419 - if (!pci_find_capability(bridge, PCI_CAP_ID_SHPC)) 420 - return false; 421 - 422 - host = pci_find_host_bridge(bridge->bus); 423 - return host->native_shpc_hotplug; 406 + return bridge->shpc_managed; 424 407 } 425 408 426 409 /**
+1
include/linux/pci.h
··· 388 388 unsigned int is_virtfn:1; 389 389 unsigned int reset_fn:1; 390 390 unsigned int is_hotplug_bridge:1; 391 + unsigned int shpc_managed:1; /* SHPC owned by shpchp */ 391 392 unsigned int is_thunderbolt:1; /* Thunderbolt controller */ 392 393 unsigned int __aer_firmware_first_valid:1; 393 394 unsigned int __aer_firmware_first:1;