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 | |
14 | static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, |
15 | void **ret) |
16 | { |
17 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle); |
18 | struct fwnode_handle *fwnode; |
19 | struct tb_nhi *nhi = data; |
20 | struct pci_dev *pdev; |
21 | struct device *dev; |
22 | |
23 | if (!adev) |
24 | return AE_OK; |
25 | |
26 | fwnode = fwnode_find_reference(fwnode: acpi_fwnode_handle(adev), name: "usb4-host-interface" , index: 0); |
27 | if (IS_ERR(ptr: fwnode)) |
28 | return AE_OK; |
29 | |
30 | /* It needs to reference this NHI */ |
31 | if (dev_fwnode(&nhi->pdev->dev) != fwnode) |
32 | goto out_put; |
33 | |
34 | /* |
35 | * Try to find physical device walking upwards to the hierarcy. |
36 | * We need to do this because the xHCI driver might not yet be |
37 | * bound so the USB3 SuperSpeed ports are not yet created. |
38 | */ |
39 | do { |
40 | dev = acpi_get_first_physical_node(adev); |
41 | if (dev) |
42 | break; |
43 | |
44 | adev = acpi_dev_parent(adev); |
45 | } while (adev); |
46 | |
47 | /* |
48 | * Check that the device is PCIe. This is because USB3 |
49 | * SuperSpeed ports have this property and they are not power |
50 | * managed with the xHCI and the SuperSpeed hub so we create the |
51 | * link from xHCI instead. |
52 | */ |
53 | while (dev && !dev_is_pci(dev)) |
54 | dev = dev->parent; |
55 | |
56 | if (!dev) |
57 | goto out_put; |
58 | |
59 | /* |
60 | * Check that this actually matches the type of device we |
61 | * expect. It should either be xHCI or PCIe root/downstream |
62 | * port. |
63 | */ |
64 | pdev = to_pci_dev(dev); |
65 | if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI || |
66 | (pci_is_pcie(dev: pdev) && |
67 | (pci_pcie_type(dev: pdev) == PCI_EXP_TYPE_ROOT_PORT || |
68 | pci_pcie_type(dev: pdev) == PCI_EXP_TYPE_DOWNSTREAM))) { |
69 | const struct device_link *link; |
70 | |
71 | /* |
72 | * Make them both active first to make sure the NHI does |
73 | * not runtime suspend before the consumer. The |
74 | * pm_runtime_put() below then allows the consumer to |
75 | * runtime suspend again (which then allows NHI runtime |
76 | * suspend too now that the device link is established). |
77 | */ |
78 | pm_runtime_get_sync(dev: &pdev->dev); |
79 | |
80 | link = device_link_add(consumer: &pdev->dev, supplier: &nhi->pdev->dev, |
81 | DL_FLAG_AUTOREMOVE_SUPPLIER | |
82 | DL_FLAG_RPM_ACTIVE | |
83 | DL_FLAG_PM_RUNTIME); |
84 | if (link) { |
85 | dev_dbg(&nhi->pdev->dev, "created link from %s\n" , |
86 | dev_name(&pdev->dev)); |
87 | *(bool *)ret = true; |
88 | } else { |
89 | dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n" , |
90 | dev_name(&pdev->dev)); |
91 | } |
92 | |
93 | pm_runtime_put(dev: &pdev->dev); |
94 | } |
95 | |
96 | out_put: |
97 | fwnode_handle_put(fwnode); |
98 | return AE_OK; |
99 | } |
100 | |
101 | /** |
102 | * tb_acpi_add_links() - Add device links based on ACPI description |
103 | * @nhi: Pointer to NHI |
104 | * |
105 | * Goes over ACPI namespace finding tunneled ports that reference to |
106 | * @nhi ACPI node. For each reference a device link is added. The link |
107 | * is automatically removed by the driver core. |
108 | * |
109 | * Returns %true if at least one link was created. |
110 | */ |
111 | bool tb_acpi_add_links(struct tb_nhi *nhi) |
112 | { |
113 | acpi_status status; |
114 | bool ret = false; |
115 | |
116 | if (!has_acpi_companion(dev: &nhi->pdev->dev)) |
117 | return false; |
118 | |
119 | /* |
120 | * Find all devices that have usb4-host-controller interface |
121 | * property that references to this NHI. |
122 | */ |
123 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, max_depth: 32, |
124 | descending_callback: tb_acpi_add_link, NULL, context: nhi, return_value: (void **)&ret); |
125 | if (ACPI_FAILURE(status)) { |
126 | dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n" ); |
127 | return false; |
128 | } |
129 | |
130 | return ret; |
131 | } |
132 | |
133 | /** |
134 | * tb_acpi_is_native() - Did the platform grant native TBT/USB4 control |
135 | * |
136 | * Returns %true if the platform granted OS native control over |
137 | * TBT/USB4. In this case software based connection manager can be used, |
138 | * otherwise there is firmware based connection manager running. |
139 | */ |
140 | bool tb_acpi_is_native(void) |
141 | { |
142 | return osc_sb_native_usb4_support_confirmed && |
143 | osc_sb_native_usb4_control; |
144 | } |
145 | |
146 | /** |
147 | * tb_acpi_may_tunnel_usb3() - Is USB3 tunneling allowed by the platform |
148 | * |
149 | * When software based connection manager is used, this function |
150 | * returns %true if platform allows native USB3 tunneling. |
151 | */ |
152 | bool tb_acpi_may_tunnel_usb3(void) |
153 | { |
154 | if (tb_acpi_is_native()) |
155 | return osc_sb_native_usb4_control & OSC_USB_USB3_TUNNELING; |
156 | return true; |
157 | } |
158 | |
159 | /** |
160 | * tb_acpi_may_tunnel_dp() - Is DisplayPort tunneling allowed by the platform |
161 | * |
162 | * When software based connection manager is used, this function |
163 | * returns %true if platform allows native DP tunneling. |
164 | */ |
165 | bool tb_acpi_may_tunnel_dp(void) |
166 | { |
167 | if (tb_acpi_is_native()) |
168 | return osc_sb_native_usb4_control & OSC_USB_DP_TUNNELING; |
169 | return true; |
170 | } |
171 | |
172 | /** |
173 | * tb_acpi_may_tunnel_pcie() - Is PCIe tunneling allowed by the platform |
174 | * |
175 | * When software based connection manager is used, this function |
176 | * returns %true if platform allows native PCIe tunneling. |
177 | */ |
178 | bool tb_acpi_may_tunnel_pcie(void) |
179 | { |
180 | if (tb_acpi_is_native()) |
181 | return osc_sb_native_usb4_control & OSC_USB_PCIE_TUNNELING; |
182 | return true; |
183 | } |
184 | |
185 | /** |
186 | * tb_acpi_is_xdomain_allowed() - Are XDomain connections allowed |
187 | * |
188 | * When software based connection manager is used, this function |
189 | * returns %true if platform allows XDomain connections. |
190 | */ |
191 | bool tb_acpi_is_xdomain_allowed(void) |
192 | { |
193 | if (tb_acpi_is_native()) |
194 | return osc_sb_native_usb4_control & OSC_USB_XDOMAIN; |
195 | return true; |
196 | } |
197 | |
198 | /* UUID for retimer _DSM: e0053122-795b-4122-8a5e-57be1d26acb3 */ |
199 | static const guid_t retimer_dsm_guid = |
200 | GUID_INIT(0xe0053122, 0x795b, 0x4122, |
201 | 0x8a, 0x5e, 0x57, 0xbe, 0x1d, 0x26, 0xac, 0xb3); |
202 | |
203 | #define RETIMER_DSM_QUERY_ONLINE_STATE 1 |
204 | #define RETIMER_DSM_SET_ONLINE_STATE 2 |
205 | |
206 | static int tb_acpi_retimer_set_power(struct tb_port *port, bool power) |
207 | { |
208 | struct usb4_port *usb4 = port->usb4; |
209 | union acpi_object argv4[2]; |
210 | struct acpi_device *adev; |
211 | union acpi_object *obj; |
212 | int ret; |
213 | |
214 | if (!usb4->can_offline) |
215 | return 0; |
216 | |
217 | adev = ACPI_COMPANION(&usb4->dev); |
218 | if (WARN_ON(!adev)) |
219 | return 0; |
220 | |
221 | /* Check if we are already powered on (and in correct mode) */ |
222 | obj = acpi_evaluate_dsm_typed(handle: adev->handle, guid: &retimer_dsm_guid, rev: 1, |
223 | RETIMER_DSM_QUERY_ONLINE_STATE, NULL, |
224 | ACPI_TYPE_INTEGER); |
225 | if (!obj) { |
226 | tb_port_warn(port, "ACPI: query online _DSM failed\n" ); |
227 | return -EIO; |
228 | } |
229 | |
230 | ret = obj->integer.value; |
231 | ACPI_FREE(obj); |
232 | |
233 | if (power == ret) |
234 | return 0; |
235 | |
236 | tb_port_dbg(port, "ACPI: calling _DSM to power %s retimers\n" , |
237 | power ? "on" : "off" ); |
238 | |
239 | argv4[0].type = ACPI_TYPE_PACKAGE; |
240 | argv4[0].package.count = 1; |
241 | argv4[0].package.elements = &argv4[1]; |
242 | argv4[1].integer.type = ACPI_TYPE_INTEGER; |
243 | argv4[1].integer.value = power; |
244 | |
245 | obj = acpi_evaluate_dsm_typed(handle: adev->handle, guid: &retimer_dsm_guid, rev: 1, |
246 | RETIMER_DSM_SET_ONLINE_STATE, argv4, |
247 | ACPI_TYPE_INTEGER); |
248 | if (!obj) { |
249 | tb_port_warn(port, |
250 | "ACPI: set online state _DSM evaluation failed\n" ); |
251 | return -EIO; |
252 | } |
253 | |
254 | ret = obj->integer.value; |
255 | ACPI_FREE(obj); |
256 | |
257 | if (ret >= 0) { |
258 | if (power) |
259 | return ret == 1 ? 0 : -EBUSY; |
260 | return 0; |
261 | } |
262 | |
263 | tb_port_warn(port, "ACPI: set online state _DSM failed with error %d\n" , ret); |
264 | return -EIO; |
265 | } |
266 | |
267 | /** |
268 | * tb_acpi_power_on_retimers() - Call platform to power on retimers |
269 | * @port: USB4 port |
270 | * |
271 | * Calls platform to turn on power to all retimers behind this USB4 |
272 | * port. After this function returns successfully the caller can |
273 | * continue with the normal retimer flows (as specified in the USB4 |
274 | * spec). Note if this returns %-EBUSY it means the type-C port is in |
275 | * non-USB4/TBT mode (there is non-USB4/TBT device connected). |
276 | * |
277 | * This should only be called if the USB4/TBT link is not up. |
278 | * |
279 | * Returns %0 on success. |
280 | */ |
281 | int tb_acpi_power_on_retimers(struct tb_port *port) |
282 | { |
283 | return tb_acpi_retimer_set_power(port, power: true); |
284 | } |
285 | |
286 | /** |
287 | * tb_acpi_power_off_retimers() - Call platform to power off retimers |
288 | * @port: USB4 port |
289 | * |
290 | * This is the opposite of tb_acpi_power_on_retimers(). After returning |
291 | * successfully the normal operations with the @port can continue. |
292 | * |
293 | * Returns %0 on success. |
294 | */ |
295 | int tb_acpi_power_off_retimers(struct tb_port *port) |
296 | { |
297 | return tb_acpi_retimer_set_power(port, power: false); |
298 | } |
299 | |
300 | static bool tb_acpi_bus_match(struct device *dev) |
301 | { |
302 | return tb_is_switch(dev) || tb_is_usb4_port_device(dev); |
303 | } |
304 | |
305 | static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw) |
306 | { |
307 | struct tb_switch *parent_sw = tb_switch_parent(sw); |
308 | struct acpi_device *adev = NULL; |
309 | |
310 | /* |
311 | * Device routers exists under the downstream facing USB4 port |
312 | * of the parent router. Their _ADR is always 0. |
313 | */ |
314 | if (parent_sw) { |
315 | struct tb_port *port = tb_switch_downstream_port(sw); |
316 | struct acpi_device *port_adev; |
317 | |
318 | port_adev = acpi_find_child_by_adr(ACPI_COMPANION(&parent_sw->dev), |
319 | adr: port->port); |
320 | if (port_adev) |
321 | adev = acpi_find_child_device(parent: port_adev, address: 0, check_children: false); |
322 | } else { |
323 | struct tb_nhi *nhi = sw->tb->nhi; |
324 | struct acpi_device *parent_adev; |
325 | |
326 | parent_adev = ACPI_COMPANION(&nhi->pdev->dev); |
327 | if (parent_adev) |
328 | adev = acpi_find_child_device(parent: parent_adev, address: 0, check_children: false); |
329 | } |
330 | |
331 | return adev; |
332 | } |
333 | |
334 | static struct acpi_device *tb_acpi_find_companion(struct device *dev) |
335 | { |
336 | /* |
337 | * The Thunderbolt/USB4 hierarchy looks like following: |
338 | * |
339 | * Device (NHI) |
340 | * Device (HR) // Host router _ADR == 0 |
341 | * Device (DFP0) // Downstream port _ADR == lane 0 adapter |
342 | * Device (DR) // Device router _ADR == 0 |
343 | * Device (UFP) // Upstream port _ADR == lane 0 adapter |
344 | * Device (DFP1) // Downstream port _ADR == lane 0 adapter number |
345 | * |
346 | * At the moment we bind the host router to the corresponding |
347 | * Linux device. |
348 | */ |
349 | if (tb_is_switch(dev)) |
350 | return tb_acpi_switch_find_companion(sw: tb_to_switch(dev)); |
351 | if (tb_is_usb4_port_device(dev)) |
352 | return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent), |
353 | adr: tb_to_usb4_port_device(dev)->port->port); |
354 | return NULL; |
355 | } |
356 | |
357 | static void tb_acpi_setup(struct device *dev) |
358 | { |
359 | struct acpi_device *adev = ACPI_COMPANION(dev); |
360 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); |
361 | |
362 | if (!adev || !usb4) |
363 | return; |
364 | |
365 | if (acpi_check_dsm(handle: adev->handle, guid: &retimer_dsm_guid, rev: 1, |
366 | BIT(RETIMER_DSM_QUERY_ONLINE_STATE) | |
367 | BIT(RETIMER_DSM_SET_ONLINE_STATE))) |
368 | usb4->can_offline = true; |
369 | } |
370 | |
371 | static struct acpi_bus_type tb_acpi_bus = { |
372 | .name = "thunderbolt" , |
373 | .match = tb_acpi_bus_match, |
374 | .find_companion = tb_acpi_find_companion, |
375 | .setup = tb_acpi_setup, |
376 | }; |
377 | |
378 | int tb_acpi_init(void) |
379 | { |
380 | return register_acpi_bus_type(&tb_acpi_bus); |
381 | } |
382 | |
383 | void tb_acpi_exit(void) |
384 | { |
385 | unregister_acpi_bus_type(&tb_acpi_bus); |
386 | } |
387 | |