Commit | Line | Data |
---|---|---|
b2be2b05 MW |
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> | |
f3380cac | 10 | #include <linux/pm_runtime.h> |
b2be2b05 MW |
11 | |
12 | #include "tb.h" | |
13 | ||
14 | static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, | |
408e1d96 | 15 | void **ret) |
b2be2b05 | 16 | { |
7f7b571b | 17 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle); |
b2be2b05 MW |
18 | struct fwnode_handle *fwnode; |
19 | struct tb_nhi *nhi = data; | |
b2be2b05 MW |
20 | struct pci_dev *pdev; |
21 | struct device *dev; | |
b2be2b05 | 22 | |
7f7b571b | 23 | if (!adev) |
b2be2b05 MW |
24 | return AE_OK; |
25 | ||
2ae18cc2 HK |
26 | fwnode = fwnode_find_reference(acpi_fwnode_handle(adev), "usb4-host-interface", 0); |
27 | if (IS_ERR(fwnode)) | |
b2be2b05 MW |
28 | return AE_OK; |
29 | ||
30 | /* It needs to reference this NHI */ | |
2ae18cc2 | 31 | if (dev_fwnode(&nhi->pdev->dev) != fwnode) |
b2be2b05 MW |
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 | */ | |
953ff25f | 39 | do { |
b2be2b05 | 40 | dev = acpi_get_first_physical_node(adev); |
953ff25f AS |
41 | if (dev) |
42 | break; | |
b2be2b05 | 43 | |
953ff25f AS |
44 | adev = acpi_dev_parent(adev); |
45 | } while (adev); | |
b2be2b05 MW |
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 | */ | |
4d395c5e | 53 | while (dev && !dev_is_pci(dev)) |
b2be2b05 MW |
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(pdev) && | |
67 | (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || | |
68 | pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) { | |
69 | const struct device_link *link; | |
70 | ||
f3380cac MW |
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(&pdev->dev); | |
79 | ||
b2be2b05 MW |
80 | link = device_link_add(&pdev->dev, &nhi->pdev->dev, |
81 | DL_FLAG_AUTOREMOVE_SUPPLIER | | |
f3380cac | 82 | DL_FLAG_RPM_ACTIVE | |
b2be2b05 MW |
83 | DL_FLAG_PM_RUNTIME); |
84 | if (link) { | |
85 | dev_dbg(&nhi->pdev->dev, "created link from %s\n", | |
86 | dev_name(&pdev->dev)); | |
408e1d96 | 87 | *(bool *)ret = true; |
b2be2b05 MW |
88 | } else { |
89 | dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", | |
90 | dev_name(&pdev->dev)); | |
91 | } | |
f3380cac MW |
92 | |
93 | pm_runtime_put(&pdev->dev); | |
b2be2b05 MW |
94 | } |
95 | ||
96 | out_put: | |
2ae18cc2 | 97 | fwnode_handle_put(fwnode); |
b2be2b05 MW |
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. | |
408e1d96 MW |
108 | * |
109 | * Returns %true if at least one link was created. | |
b2be2b05 | 110 | */ |
408e1d96 | 111 | bool tb_acpi_add_links(struct tb_nhi *nhi) |
b2be2b05 MW |
112 | { |
113 | acpi_status status; | |
408e1d96 | 114 | bool ret = false; |
b2be2b05 MW |
115 | |
116 | if (!has_acpi_companion(&nhi->pdev->dev)) | |
408e1d96 | 117 | return false; |
b2be2b05 MW |
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, 32, | |
408e1d96 MW |
124 | tb_acpi_add_link, NULL, nhi, (void **)&ret); |
125 | if (ACPI_FAILURE(status)) { | |
b2be2b05 | 126 | dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n"); |
408e1d96 MW |
127 | return false; |
128 | } | |
129 | ||
130 | return ret; | |
b2be2b05 | 131 | } |
c6da62a2 MW |
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 | } | |
ccc5cb8a RM |
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(adev->handle, &retimer_dsm_guid, 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(adev->handle, &retimer_dsm_guid, 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, 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, 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 | ||
a73a204b | 305 | static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw) |
ccc5cb8a | 306 | { |
7ce54221 | 307 | struct tb_switch *parent_sw = tb_switch_parent(sw); |
a73a204b | 308 | struct acpi_device *adev = NULL; |
ccc5cb8a RM |
309 | |
310 | /* | |
311 | * Device routers exists under the downstream facing USB4 port | |
312 | * of the parent router. Their _ADR is always 0. | |
313 | */ | |
ccc5cb8a | 314 | if (parent_sw) { |
7ce54221 | 315 | struct tb_port *port = tb_switch_downstream_port(sw); |
ccc5cb8a RM |
316 | struct acpi_device *port_adev; |
317 | ||
a73a204b RW |
318 | port_adev = acpi_find_child_by_adr(ACPI_COMPANION(&parent_sw->dev), |
319 | port->port); | |
ccc5cb8a RM |
320 | if (port_adev) |
321 | adev = acpi_find_child_device(port_adev, 0, 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_adev, 0, 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(tb_to_switch(dev)); | |
4e99c98e | 351 | if (tb_is_usb4_port_device(dev)) |
a73a204b RW |
352 | return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent), |
353 | tb_to_usb4_port_device(dev)->port->port); | |
ccc5cb8a RM |
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(adev->handle, &retimer_dsm_guid, 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 | } |