1// SPDX-License-Identifier: GPL-2.0 2/* 3 * ACPI support 4 * 5 * Copyright (C) 2020, Intel Corporation 6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 7 */ 8 9#include <linux/acpi.h> 10#include <linux/pm_runtime.h> 11 12#include "tb.h" 13 14static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, 15 void **return_value) 16{ 17 struct fwnode_reference_args args; 18 struct fwnode_handle *fwnode; 19 struct tb_nhi *nhi = data; 20 struct acpi_device *adev; 21 struct pci_dev *pdev; 22 struct device *dev; 23 int ret; 24 25 if (acpi_bus_get_device(handle, &adev)) 26 return AE_OK; 27 28 fwnode = acpi_fwnode_handle(adev); 29 ret = fwnode_property_get_reference_args(fwnode, "usb4-host-interface", 30 NULL, 0, 0, &args); 31 if (ret) 32 return AE_OK; 33 34 /* It needs to reference this NHI */ 35 if (nhi->pdev->dev.fwnode != args.fwnode) 36 goto out_put; 37 38 /* 39 * Try to find physical device walking upwards to the hierarcy. 40 * We need to do this because the xHCI driver might not yet be 41 * bound so the USB3 SuperSpeed ports are not yet created. 42 */ 43 dev = acpi_get_first_physical_node(adev); 44 while (!dev) { 45 adev = adev->parent; 46 if (!adev) 47 break; 48 dev = acpi_get_first_physical_node(adev); 49 } 50 51 if (!dev) 52 goto out_put; 53 54 /* 55 * Check that the device is PCIe. This is because USB3 56 * SuperSpeed ports have this property and they are not power 57 * managed with the xHCI and the SuperSpeed hub so we create the 58 * link from xHCI instead. 59 */ 60 while (dev && !dev_is_pci(dev)) 61 dev = dev->parent; 62 63 if (!dev) 64 goto out_put; 65 66 /* 67 * Check that this actually matches the type of device we 68 * expect. It should either be xHCI or PCIe root/downstream 69 * port. 70 */ 71 pdev = to_pci_dev(dev); 72 if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI || 73 (pci_is_pcie(pdev) && 74 (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || 75 pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) { 76 const struct device_link *link; 77 78 /* 79 * Make them both active first to make sure the NHI does 80 * not runtime suspend before the consumer. The 81 * pm_runtime_put() below then allows the consumer to 82 * runtime suspend again (which then allows NHI runtime 83 * suspend too now that the device link is established). 84 */ 85 pm_runtime_get_sync(&pdev->dev); 86 87 link = device_link_add(&pdev->dev, &nhi->pdev->dev, 88 DL_FLAG_AUTOREMOVE_SUPPLIER | 89 DL_FLAG_RPM_ACTIVE | 90 DL_FLAG_PM_RUNTIME); 91 if (link) { 92 dev_dbg(&nhi->pdev->dev, "created link from %s\n", 93 dev_name(&pdev->dev)); 94 } else { 95 dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", 96 dev_name(&pdev->dev)); 97 } 98 99 pm_runtime_put(&pdev->dev); 100 } 101 102out_put: 103 fwnode_handle_put(args.fwnode); 104 return AE_OK; 105} 106 107/** 108 * tb_acpi_add_links() - Add device links based on ACPI description 109 * @nhi: Pointer to NHI 110 * 111 * Goes over ACPI namespace finding tunneled ports that reference to 112 * @nhi ACPI node. For each reference a device link is added. The link 113 * is automatically removed by the driver core. 114 */ 115void tb_acpi_add_links(struct tb_nhi *nhi) 116{ 117 acpi_status status; 118 119 if (!has_acpi_companion(&nhi->pdev->dev)) 120 return; 121 122 /* 123 * Find all devices that have usb4-host-controller interface 124 * property that references to this NHI. 125 */ 126 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 32, 127 tb_acpi_add_link, NULL, nhi, NULL); 128 if (ACPI_FAILURE(status)) 129 dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n"); 130} 131