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