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