blob: e54952598c0d7bbbaf0017f63e3190918e658812 [file] [log] [blame]
Duncan Laurie2cc126b2020-08-28 19:46:35 +00001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <acpi/acpigen.h>
4#include <acpi/acpi_device.h>
John Zhao0b3f15c2021-04-27 10:47:25 -07005#include <acpi/acpi_pld.h>
Duncan Laurie2cc126b2020-08-28 19:46:35 +00006#include <console/console.h>
7#include <device/device.h>
8#include <device/path.h>
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +05309#include <drivers/usb/acpi/chip.h>
Duncan Laurie2cc126b2020-08-28 19:46:35 +000010#include <gpio.h>
John Zhao0b3f15c2021-04-27 10:47:25 -070011#include <string.h>
Duncan Laurie2cc126b2020-08-28 19:46:35 +000012#include "chip.h"
Brandon Breitenstein297d27b2020-12-14 13:52:24 -080013#include "retimer.h"
Duncan Laurie2cc126b2020-08-28 19:46:35 +000014
15/* Unique ID for the retimer _DSM. */
John Zhao0b3f15c2021-04-27 10:47:25 -070016#define INTEL_USB4_RETIMER_DSM_UUID "E0053122-795B-4122-8A5E-57BE1D26ACB3"
17
18static const char *usb4_retimer_scope;
19static const char *usb4_retimer_path_arg(const char *arg)
20{
21 /* \\_SB.PCI0.TDMx.ARG */
22 static char name[DEVICE_PATH_MAX];
23 snprintf(name, sizeof(name), "%s%c%s", usb4_retimer_scope, '.', arg);
24 return name;
25}
26
27/* Each polling cycle takes up to 25 ms with a total of 12 of these iterations */
28#define USB4_RETIMER_ITERATION_NUM 12
29#define USB4_RETIMER_POLL_CYCLE_MS 25
30static void usb4_retimer_execute_ec_cmd(uint8_t port, uint8_t cmd, uint8_t expected_value)
31{
32 const char *RFWU = ec_retimer_fw_update_path();
33 const uint8_t data = cmd << USB_RETIMER_FW_UPDATE_OP_SHIFT | port;
34
35 /* Invoke EC Retimer firmware update command execution */
36 ec_retimer_fw_update(data);
37 /* If RFWU has return value 0xfe, return error -1 */
38 acpigen_write_if_lequal_namestr_int(RFWU, USB_RETIMER_FW_UPDATE_ERROR);
39 acpigen_write_return_integer(-1);
40 acpigen_pop_len(); /* If */
41
42 acpigen_write_store_int_to_op(USB4_RETIMER_ITERATION_NUM, LOCAL2_OP);
43 acpigen_emit_byte(WHILE_OP);
44 acpigen_write_len_f();
45 acpigen_emit_byte(LGREATER_OP);
46 acpigen_emit_byte(LOCAL2_OP);
47 acpigen_emit_byte(ZERO_OP);
48 acpigen_write_if_lequal_namestr_int(RFWU, expected_value);
49 acpigen_emit_byte(BREAK_OP);
50 acpigen_pop_len(); /* If */
51
52 if (cmd == USB_RETIMER_FW_UPDATE_SET_TBT) {
53 /*
54 * EC return either USB_PD_MUX_USB4_ENABLED or USB_PD_MUX_TBT_COMPAT_ENABLED
55 * to RFWU after the USB_RETIMER_FW_UPDATE_SET_TBT command execution. It is
56 * needed to add additional check for USB_PD_MUX_TBT_COMPAT_ENABLED.
57 */
58 acpigen_write_if_lequal_namestr_int(RFWU, USB_PD_MUX_TBT_COMPAT_ENABLED);
59 acpigen_emit_byte(BREAK_OP);
60 acpigen_pop_len(); /* If */
61 }
62
63 acpigen_write_sleep(USB4_RETIMER_POLL_CYCLE_MS);
64 acpigen_emit_byte(DECREMENT_OP);
65 acpigen_emit_byte(LOCAL2_OP);
66 acpigen_pop_len(); /* While */
67
68 /*
69 * Check whether there is timeout error
70 * Return: -1 if timeout error occurring
71 */
72 acpigen_write_if_lequal_op_int(LOCAL2_OP, 0);
73 acpigen_write_return_integer(-1);
74 acpigen_pop_len(); /* If */
75}
76
77static void enable_retimer_online_state(uint8_t port, struct acpi_gpio *power_gpio)
78{
79 uint8_t expected_value;
80
81 /*
82 * Enable_retimer_online_state under NDA
83 * 1. Force power on
84 * 2. Check if there is a device connected
85 * 3. Suspend PD
86 * 4. Set Mux to USB mode
87 * 5. Set Mux to Safe mode
88 * 6. Set Mux to TBT mode
89 */
90
91 /* Force power on for the retimer on the port */
92 acpigen_enable_tx_gpio(power_gpio);
93
94 /*
95 * Get MUX mode state
96 * Return -1 if there is a device connected on the port.
97 * Otherwise proceed Retimer firmware upgrade operation.
98 */
99 expected_value = USB_PD_MUX_NONE;
100 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_GET_MUX, expected_value);
101
102 /*
103 * Suspend PD
104 * Command: USB_RETIMER_FW_UPDATE_SUSPEND_PD
105 * Expect return value: 0
106 */
107 expected_value = 0;
108 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_SUSPEND_PD, expected_value);
109
110 /*
111 * Set MUX USB Mode
112 * Command: USB_RETIMER_FW_UPDATE_SUSPEND_PD
113 * Expect return value: USB_PD_MUX_USB_ENABLED
114 */
115 expected_value = USB_PD_MUX_USB_ENABLED;
116 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_SET_USB, expected_value);
117
118 /*
119 * Set MUX Safe Mode
120 * Command: USB_RETIMER_FW_UPDATE_SET_SAFE
121 * Expect return value: USB_PD_MUX_SAFE_MODE
122 */
123 expected_value = USB_PD_MUX_SAFE_MODE;
124 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_SET_SAFE, expected_value);
125
126 /*
127 * Set MUX TBT Mode
128 * Command: USB_RETIMER_FW_UPDATE_SET_TBT
129 * Expect return value: USB_PD_MUX_USB4_ENABLED or USB_PD_MUX_TBT_COMPAT_ENABLED
130 */
131 expected_value = USB_PD_MUX_USB4_ENABLED;
132 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_SET_TBT, expected_value);
133}
134
135static void disable_retimer_online_state(uint8_t port, struct acpi_gpio *power_gpio)
136{
137 uint8_t expected_value;
138
139 /*
140 * Disable_retimer_online_state
141 * 1. Set Mux to disconnect mode
142 * 2. Resume PD
143 * 3. Force power off
144 */
145
146 /*
147 * Set MUX Disconnect Mode
148 * Command: USB_RETIMER_FW_UPDATE_DISCONNECT
149 * Expect return value: 0
150 */
151 expected_value = 0;
152 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_DISCONNECT, expected_value);
153
154 /*
155 * Resume PD
156 * Command: USB_RETIMER_FW_UPDATE_RESUME_PD
157 * Expect return value: 1
158 */
159 expected_value = 1;
160 usb4_retimer_execute_ec_cmd(port, USB_RETIMER_FW_UPDATE_RESUME_PD, expected_value);
161
162 /* Force power off */
163 acpigen_disable_tx_gpio(power_gpio);
164}
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000165
166/*
John Zhao0b3f15c2021-04-27 10:47:25 -0700167 * Arg0: UUID e0053122-795b-4122-8a5e-57be1d26acb3
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000168 * Arg1: Revision ID (set to 1)
169 * Arg2: Function Index
170 * 0: Query command implemented
John Zhao0b3f15c2021-04-27 10:47:25 -0700171 * 1: Get power state
172 * 2: Set power state
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000173 * Arg3: A package containing parameters for the function specified
John Zhao0b3f15c2021-04-27 10:47:25 -0700174 * by the UUID, revision ID, function index and port index.
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000175 */
John Zhao0b3f15c2021-04-27 10:47:25 -0700176static void usb4_retimer_cb_standard_query(uint8_t port, void *arg)
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000177{
178 /*
John Zhao0b3f15c2021-04-27 10:47:25 -0700179 * ToInteger (Arg1, Local1)
180 * If (Local1 == 1) {
181 * Return(Buffer() {0x7})
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000182 * }
183 * Return (Buffer() {0x01})
184 */
John Zhao0b3f15c2021-04-27 10:47:25 -0700185 acpigen_write_to_integer(ARG1_OP, LOCAL1_OP);
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000186
John Zhao0b3f15c2021-04-27 10:47:25 -0700187 /* Revision 1 supports 2 Functions beyond the standard query */
188 acpigen_write_if_lequal_op_int(LOCAL1_OP, 1);
189 acpigen_write_return_singleton_buffer(0x7);
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000190 acpigen_pop_len(); /* If */
191
192 /* Other revisions support no additional functions */
John Zhao0b3f15c2021-04-27 10:47:25 -0700193 acpigen_write_return_singleton_buffer(0x1);
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000194}
195
John Zhao0b3f15c2021-04-27 10:47:25 -0700196static void usb4_retimer_cb_get_power_state(uint8_t port, void *arg)
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000197{
John Zhao0b3f15c2021-04-27 10:47:25 -0700198 const char *PWR;
199 char pwr[DEVICE_PATH_MAX];
200
201 snprintf(pwr, sizeof(pwr), "HR.DFP%1d.PWR", port);
202 PWR = usb4_retimer_path_arg(pwr);
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000203
204 /*
John Zhao0b3f15c2021-04-27 10:47:25 -0700205 * If (PWR > 0) {
206 * Return (1)
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000207 * }
208 */
John Zhao0b3f15c2021-04-27 10:47:25 -0700209 acpigen_write_if();
210 acpigen_emit_byte(LGREATER_OP);
211 acpigen_emit_namestring(PWR);
212 acpigen_emit_byte(0);
213 acpigen_write_return_integer(1);
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000214
215 /*
216 * Else {
John Zhao0b3f15c2021-04-27 10:47:25 -0700217 * Return (0)
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000218 * }
219 */
220 acpigen_write_else();
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000221 acpigen_write_return_integer(0);
John Zhao0b3f15c2021-04-27 10:47:25 -0700222 acpigen_pop_len();
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000223}
224
John Zhao0b3f15c2021-04-27 10:47:25 -0700225static void usb4_retimer_cb_set_power_state(uint8_t port, void *arg)
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800226{
John Zhao0b3f15c2021-04-27 10:47:25 -0700227 struct acpi_gpio *power_gpio = arg;
228 const char *PWR;
229 char pwr[DEVICE_PATH_MAX];
230
231 snprintf(pwr, sizeof(pwr), "HR.DFP%1d.PWR", port);
232 PWR = usb4_retimer_path_arg(pwr);
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800233
234 /*
John Zhao0b3f15c2021-04-27 10:47:25 -0700235 * Get information to set retimer power state from Arg3[0]
236 * Local1 = DeRefOf (Arg3[0])
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800237 */
John Zhao0b3f15c2021-04-27 10:47:25 -0700238 acpigen_get_package_op_element(ARG3_OP, 0, LOCAL1_OP);
239
240 /*
241 * If ((Local1 == 0) && (PWR > 0)) {
242 * PWR--
243 * If (PWR == 0) {
244 * // Disable retimer online state
245 * }
246 * }
247 */
248 acpigen_write_if();
249 acpigen_emit_byte(LAND_OP);
250 acpigen_emit_byte(LEQUAL_OP);
251 acpigen_emit_byte(LOCAL1_OP);
252 acpigen_emit_byte(0);
253 acpigen_emit_byte(LGREATER_OP);
254 acpigen_emit_namestring(PWR);
255 acpigen_emit_byte(0);
256 /* PWR-- */
257 acpigen_emit_byte(DECREMENT_OP);
258 acpigen_emit_namestring(PWR);
259 acpigen_write_if_lequal_namestr_int(PWR, 0); /* If (PWR == 0) */
260 disable_retimer_online_state(port, power_gpio);
261 acpigen_pop_len(); /* If (PWR == 0) */
262
263 /*
264 * Else If ((Local1 == 1) && (PWR == 0)) {
265 * // Enable retimer online state
266 * PWR++
267 * }
268 */
269 acpigen_write_else();
270 acpigen_write_if();
271 acpigen_emit_byte(LAND_OP);
272 acpigen_emit_byte(LEQUAL_OP);
273 acpigen_emit_byte(LOCAL1_OP);
274 acpigen_emit_byte(1);
275 acpigen_emit_byte(LEQUAL_OP);
276 acpigen_emit_namestring(PWR);
277 acpigen_emit_byte(0);
278 enable_retimer_online_state(port, power_gpio);
279 /* PWR++ */
280 acpigen_emit_byte(INCREMENT_OP);
281 acpigen_emit_namestring(PWR);
282
283 /*
284 * Else {
285 * Return (0)
286 * }
287 */
288 acpigen_write_else();
289 acpigen_write_return_integer(0);
290 acpigen_pop_len(); /* Else */
291 acpigen_pop_len(); /* If */
292
293 /*
294 * If (PWR == 1) {
295 * Return (1)
296 * }
297 */
298 acpigen_write_if_lequal_namestr_int(PWR, 1);
299 acpigen_write_return_integer(1);
300
301 /*
302 * Else {
303 * Return (0)
304 * }
305 */
306 acpigen_write_else();
307 acpigen_write_return_integer(0);
308 acpigen_pop_len();
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800309}
310
John Zhao0b3f15c2021-04-27 10:47:25 -0700311static void (*usb4_retimer_callbacks[3])(uint8_t port, void *) = {
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000312 usb4_retimer_cb_standard_query, /* Function 0 */
313 usb4_retimer_cb_get_power_state, /* Function 1 */
314 usb4_retimer_cb_set_power_state, /* Function 2 */
315};
316
John Zhao0b3f15c2021-04-27 10:47:25 -0700317static void usb4_retimer_write_dsm(uint8_t port, const char *uuid,
318 void (**callbacks)(uint8_t port, void *), size_t count, void *arg)
319{
320 struct usb4_retimer_dsm_uuid id = DSM_UUID(uuid, callbacks, count, arg);
321 size_t i;
322
323 acpigen_write_to_integer(ARG2_OP, LOCAL0_OP);
324
325 for (i = 0; i < id.count; i++) {
326 /* If (LEqual (Local0, i)) */
327 acpigen_write_if_lequal_op_int(LOCAL0_OP, i);
328
329 /* Callback to write if handler. */
330 if (id.callbacks[i])
331 id.callbacks[i](port, id.arg);
332
333 acpigen_pop_len(); /* If */
334 }
335}
336
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000337static void usb4_retimer_fill_ssdt(const struct device *dev)
338{
John Zhao0b3f15c2021-04-27 10:47:25 -0700339 struct drivers_intel_usb4_retimer_config *config = dev->chip_info;
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530340 const struct device *usb_device;
John Zhao0b3f15c2021-04-27 10:47:25 -0700341 static char dfp[DEVICE_PATH_MAX];
342 struct acpi_pld pld;
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530343 uint8_t dfp_port, usb_port;
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000344
John Zhao0b3f15c2021-04-27 10:47:25 -0700345 usb4_retimer_scope = acpi_device_scope(dev);
346 if (!usb4_retimer_scope || !config)
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000347 return;
348
John Zhao0b3f15c2021-04-27 10:47:25 -0700349 /* Scope */
350 acpigen_write_scope(usb4_retimer_scope);
351
352 /* Host router */
353 acpigen_write_device("HR");
354 acpigen_write_ADR(0);
355 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
356
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530357 for (dfp_port = 0; dfp_port < DFP_NUM_MAX; dfp_port++) {
358
359 if (!config->dfp[dfp_port].power_gpio.pin_count) {
360 printk(BIOS_ERR, "%s: No DFP%1d power GPIO for %s\n",
361 __func__, dfp_port, dev_path(dev));
John Zhao0b3f15c2021-04-27 10:47:25 -0700362 continue;
363 }
364
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530365 usb_device = config->dfp[dfp_port].typec_port;
366 usb_port = usb_device->path.usb.port_id;
367
John Zhao0b3f15c2021-04-27 10:47:25 -0700368 /* DFPx */
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530369 snprintf(dfp, sizeof(dfp), "DFP%1d", usb_port);
John Zhao0b3f15c2021-04-27 10:47:25 -0700370 acpigen_write_device(dfp);
371 /* _ADR part is for the lane adapter */
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530372 acpigen_write_ADR(dfp_port*2 + 1);
John Zhao0b3f15c2021-04-27 10:47:25 -0700373
374 /* Fill _PLD with the same USB 3.x object on the Type-C connector */
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530375 if (CONFIG(DRIVERS_USB_ACPI)) {
376 if (usb_acpi_get_pld(usb_device, &pld))
377 acpigen_write_pld(&pld);
378 else
379 printk(BIOS_ERR, "Error retrieving PLD for USB Type-C %d\n",
380 usb_port);
381 }
John Zhao0b3f15c2021-04-27 10:47:25 -0700382
383 /* Power online reference counter(_PWR) */
384 acpigen_write_name("PWR");
385 acpigen_write_zero();
386
387 /* Method (_DSM, 4, Serialized) */
388 acpigen_write_method_serialized("_DSM", 0x4);
389 /* ToBuffer (Arg0, Local0) */
390 acpigen_write_to_buffer(ARG0_OP, LOCAL0_OP);
391 acpigen_write_if(); /* If (UUID != INTEL_USB4_RETIMER_DSM_UUID) */
392 acpigen_emit_byte(LNOT_OP);
393 acpigen_emit_byte(LEQUAL_OP);
394 acpigen_emit_byte(LOCAL0_OP);
395 acpigen_write_uuid(INTEL_USB4_RETIMER_DSM_UUID);
396 /* Return (Buffer (One) { 0x0 }) */
397 acpigen_write_return_singleton_buffer(0x0);
398 acpigen_pop_len();
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530399 usb4_retimer_write_dsm(usb_port, INTEL_USB4_RETIMER_DSM_UUID,
John Zhao0b3f15c2021-04-27 10:47:25 -0700400 usb4_retimer_callbacks, ARRAY_SIZE(usb4_retimer_callbacks),
Maulik V Vaghela0f7e0862021-06-29 17:16:49 +0530401 (void *)&config->dfp[dfp_port].power_gpio);
John Zhao0b3f15c2021-04-27 10:47:25 -0700402 /* Default case: Return (Buffer (One) { 0x0 }) */
403 acpigen_write_return_singleton_buffer(0x0);
404
405 acpigen_pop_len(); /* Method _DSM */
406 acpigen_pop_len(); /* DFP */
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000407 }
John Zhao0b3f15c2021-04-27 10:47:25 -0700408 acpigen_pop_len(); /* Host Router */
Duncan Laurie2cc126b2020-08-28 19:46:35 +0000409 acpigen_pop_len(); /* Scope */
410
411 printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev), dev->chip_ops->name,
412 dev_path(dev));
413}
414
415static struct device_operations usb4_retimer_dev_ops = {
416 .read_resources = noop_read_resources,
417 .set_resources = noop_set_resources,
418 .acpi_fill_ssdt = usb4_retimer_fill_ssdt,
419};
420
421static void usb4_retimer_enable(struct device *dev)
422{
423 dev->ops = &usb4_retimer_dev_ops;
424}
425
426struct chip_operations drivers_intel_usb4_retimer_ops = {
427 CHIP_NAME("Intel USB4 Retimer")
428 .enable_dev = usb4_retimer_enable
429};
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800430
431__weak const char *ec_retimer_fw_update_path(void)
432{
433 return NULL;
434}
435
John Zhao0b3f15c2021-04-27 10:47:25 -0700436__weak void ec_retimer_fw_update(uint8_t data)
Brandon Breitenstein297d27b2020-12-14 13:52:24 -0800437{
438}