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