From 6170c519d6385a6ab93d14d4474f6feb733e4535 Mon Sep 17 00:00:00 2001 From: Roberto Dantas <7robertodantas@gmail.com> Date: Thu, 12 Nov 2026 07:16:49 -0300 Subject: [PATCH 1/4] dt-bindings: usb: apple,sn201202x: cover a retimer kept from boot apple,reset-inherited-usb2-connection was written for the J716c, whose boot port keeps a USB2 repeater connection. The J575c needs the same renegotiation for another reason: a connection that is up when Linux starts keeps its Type-C retimer set up from before the host PHY ran, and SuperSpeed never trains. Describe both cases. Assisted-by: LLM (Claude Code) Signed-off-by: Roberto Dantas <7robertodantas@gmail.com> Co-Authored-By: Claude Opus 5.5 --- .../devicetree/bindings/usb/apple,sn201202x.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml b/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml index 282d26ae0c6d2..fe7d229c8f91e 100644 --- a/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml +++ b/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml @@ -46,10 +46,13 @@ properties: apple,reset-inherited-usb2-connection: type: boolean - description: - The boot USB port retains a USB2 repeater connection that cannot be - recovered by resetting the host PHY. Renegotiate a normal application - connection before starting the host controllers. Debug routes are preserved. + description: | + A connection that is up when the OS starts keeps state from the boot + chain that resetting the host PHY does not recover: a USB2 repeater + connection on the boot port (J716c), or a Type-C retimer set up before + the host PHY ran, which keeps SuperSpeed from training (J575c). + Renegotiate a normal application connection before starting the host + controllers. Debug routes are preserved. required: - compatible From 1a774eeee49a23644325f5d99243bc92fc4fb4c9 Mon Sep 17 00:00:00 2001 From: Roberto Dantas <7robertodantas@gmail.com> Date: Thu, 12 Nov 2026 07:16:49 -0300 Subject: [PATCH 2/4] usb: typec: tipd: renegotiate inherited J575c connections On the J575c (Mac Studio, M4 Max) a USB3 device that is attached while the Mac boots enumerates at USB2: its SuperSpeed port stays in RxDetect although the T6040 PHY tunes the right lane. Unplugging and plugging it again in the same orientation gives SuperSpeed Plus. The port controller reports the same status, data status and mode in both cases, and the same values macOS reports for that device at 10 Gb/s. Only byte 7 of register 0x7e differs: bit 2 on every connection Linux inherits from the boot chain, bit 3 after a replug and on every connected port under macOS, whose port controllers attach the devices again as macOS starts. Reuse the J716c renegotiation (unlock, DISC with a delay, lock) for the J575c, with its key, the reversed target identifier J575. Keep the board keys in a table. On the J575c only renegotiate a connection whose attach state lacks bit 3, and leave the port as it was if that fails, since it then only loses SuperSpeed. The J716c path is unchanged. Tested on a J575c with a USB 3.2 Gen 2 SSD attached across reboots on the fourth back port: SuperSpeed Plus Gen 2x1 and 926 MB/s without a replug (USB2 before); attach state 0x08 afterwards on every renegotiated port, as under macOS. The keyboard hub, a USB-C DP adapter (it hotplugs once) and the debug port keep working. Assisted-by: LLM (Claude Code) Signed-off-by: Roberto Dantas <7robertodantas@gmail.com> Co-Authored-By: Claude Opus 5.5 --- drivers/usb/typec/tipd/core.c | 100 ++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index fed6ea4097662..dda499791f3fb 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -2948,27 +2948,80 @@ static int cd321x_remove(struct tps6598x *tps) return 0; } -static int cd321x_reset_inherited_connection(struct tps6598x *tps, u32 *status) +/* + * Boards whose inherited boot connection must be renegotiated. The port + * controller key is the reversed target identifier. + * + * J716c: the boot port keeps a USB2 repeater connection. + * J575c: a connection that was up before Linux keeps its Type-C retimer set up + * from before the host PHY ran, so SuperSpeed stays in RxDetect until the + * partner is replugged. Byte 7 of register 0x7e has bit 3 set once the + * controller has completed an attach with the OS running (after a replug, and + * under macOS) and bit 2 in the inherited state; renegotiate only without + * bit 3. A failure leaves the port as it was (USB2 only) instead of failing + * the probe. + */ +#define CD321X_ATTACH_STATE_REG 0x7e +#define CD321X_ATTACH_STATE_BYTE 7 +#define CD321X_ATTACH_STATE_DONE BIT(3) + +struct cd321x_inherited_board { + const char *compatible; + u8 key[4]; + bool check_attach_state; + bool best_effort; +}; + +static const struct cd321x_inherited_board cd321x_inherited_boards[] = { + { .compatible = "apple,j716c", .key = { '6', '1', '7', 'J' } }, + { .compatible = "apple,j575c", .key = { '5', '7', '5', 'J' }, + .check_attach_state = true, .best_effort = true }, +}; + +static const struct cd321x_inherited_board *cd321x_inherited_board(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cd321x_inherited_boards); i++) + if (of_machine_is_compatible(cd321x_inherited_boards[i].compatible)) + return &cd321x_inherited_boards[i]; + return NULL; +} + +static int cd321x_attach_state_done(struct tps6598x *tps) +{ + u8 state[CD321X_ATTACH_STATE_BYTE + 1]; + int ret; + + ret = tps6598x_block_read(tps, CD321X_ATTACH_STATE_REG, state, sizeof(state)); + if (ret) + return ret; + dev_dbg(tps->dev, "inherited connection, attach state 0x%02x\n", + state[CD321X_ATTACH_STATE_BYTE]); + return !!(state[CD321X_ATTACH_STATE_BYTE] & CD321X_ATTACH_STATE_DONE); +} + +static int __cd321x_reset_inherited_connection(struct tps6598x *tps, + const struct cd321x_inherited_board *board, + u32 *status) { - /* J716c's port-controller key is the reversed target identifier J716. */ - static const u8 key[] = { '6', '1', '7', 'J' }; static const u8 locked[4]; u8 delay = 5, result; unsigned long timeout; int ret, lock_ret, mode; - if (!device_property_read_bool(tps->dev, "apple,reset-inherited-usb2-connection") || - !of_machine_is_compatible("apple,j716c") || - !device_is_compatible(tps->dev, "apple,sn201202x") || - tps->dbma_at_probe || !(*status & TPS_STATUS_PLUG_PRESENT) || - !TPS_STATUS_TO_TYPEC_DATAROLE(*status)) - return 0; mode = tps6598x_check_mode(tps); if (mode != TPS_MODE_APP && mode != TPS_MODE_APP1) return 0; + if (board->check_attach_state) { + ret = cd321x_attach_state_done(tps); + if (ret) + return ret < 0 ? ret : 0; + } /* All suppliers are available, but no USB or router session has started. */ - ret = tps6598x_exec_cmd(tps, "LOCK", sizeof(key), key, sizeof(result), &result); + ret = tps6598x_exec_cmd(tps, "LOCK", sizeof(board->key), board->key, sizeof(result), + &result); if (ret) return ret; if (result) { @@ -3004,7 +3057,32 @@ static int cd321x_reset_inherited_connection(struct tps6598x *tps, u32 *status) if (!tps6598x_read_status(tps, status)) return -EIO; tps->status = *status; - dev_info(tps->dev, "reinitialized inherited USB2 connection\n"); + dev_info(tps->dev, "reinitialized inherited connection\n"); + return 0; +} + +static int cd321x_reset_inherited_connection(struct tps6598x *tps, u32 *status) +{ + const struct cd321x_inherited_board *board; + int ret; + + if (!device_property_read_bool(tps->dev, "apple,reset-inherited-usb2-connection") || + !device_is_compatible(tps->dev, "apple,sn201202x") || + tps->dbma_at_probe || !(*status & TPS_STATUS_PLUG_PRESENT) || + !TPS_STATUS_TO_TYPEC_DATAROLE(*status)) + return 0; + board = cd321x_inherited_board(); + if (!board) + return 0; + + ret = __cd321x_reset_inherited_connection(tps, board, status); + if (!ret || !board->best_effort) + return ret; + + dev_warn(tps->dev, "inherited connection not reinitialized: %d\n", ret); + if (!tps6598x_read_status(tps, status)) + return -EIO; + tps->status = *status; return 0; } From 218e3f8550ba11288155885b78bb9c67f0541769 Mon Sep 17 00:00:00 2001 From: Roberto Dantas <7robertodantas@gmail.com> Date: Thu, 12 Nov 2026 07:16:50 -0300 Subject: [PATCH 3/4] usb: typec: tipd: sn201202x: probe asynchronously Renegotiating an inherited connection waits about 5.6 s per port, and the controllers of one machine probed one after another, so with three ports in use the last one came up after 17 s. Prefer asynchronous probing; the driver keeps no shared state besides the debugfs root, which is already locked. On a J575c with three inherited connections all three are renegotiated within 2 ms of each other, 13.8 s after boot instead of 25.1 s. Assisted-by: LLM (Claude Code) Signed-off-by: Roberto Dantas <7robertodantas@gmail.com> Co-Authored-By: Claude Opus 5.5 --- drivers/usb/typec/tipd/spmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/tipd/spmi.c b/drivers/usb/typec/tipd/spmi.c index 30d472c3d83a4..44d1f12a10e89 100644 --- a/drivers/usb/typec/tipd/spmi.c +++ b/drivers/usb/typec/tipd/spmi.c @@ -985,6 +985,11 @@ static struct spmi_driver sn201202x_driver = { .name = "sn201202x", .pm = &sn201202x_pm_ops, .of_match_table = sn201202x_of_match, + /* + * Renegotiating an inherited connection waits several seconds per + * port; let the ports of one machine probe at the same time. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = sn201202x_probe, .remove = sn201202x_remove, From f1485310b173859db17786b9340df20d27f69ff1 Mon Sep 17 00:00:00 2001 From: Roberto Dantas <7robertodantas@gmail.com> Date: Thu, 12 Nov 2026 07:16:50 -0300 Subject: [PATCH 4/4] arm64: dts: apple: t6041-j575c: renegotiate inherited Type-C connections A USB3 device attached to a back Type-C port while the Mac boots stays at USB2 until it is replugged: the port's retimer (ADT i2c6 uatcrt0-3) was set up before the host PHY ran. Ask the port controllers of the second to fourth back ports to attach such a device again at probe. The first back port (hpm0) is left out: on the test machine it carries the DebugUSB cable, so a USB3 device there was not tested. The driver skips a port in debug mode either way. Assisted-by: LLM (Claude Code) Signed-off-by: Roberto Dantas <7robertodantas@gmail.com> Co-Authored-By: Claude Opus 5.5 --- arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi | 2 ++ arch/arm64/boot/dts/apple/t6041-j575c.dts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi b/arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi index 7f66ec53ff2e7..6399e6698293b 100644 --- a/arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi +++ b/arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi @@ -114,6 +114,8 @@ <95 IRQ_TYPE_EDGE_RISING>, <97 IRQ_TYPE_EDGE_RISING>; interrupt-names = "irq", "select", "sleep", "wake"; + /* Retimer set up before Linux keeps SuperSpeed down until replugged. */ + apple,reset-inherited-usb2-connection; typec3: connector { compatible = "usb-c-connector"; diff --git a/arch/arm64/boot/dts/apple/t6041-j575c.dts b/arch/arm64/boot/dts/apple/t6041-j575c.dts index 1291bfa70e33b..312df96b09c81 100644 --- a/arch/arm64/boot/dts/apple/t6041-j575c.dts +++ b/arch/arm64/boot/dts/apple/t6041-j575c.dts @@ -504,6 +504,8 @@ <43 IRQ_TYPE_EDGE_RISING>, <45 IRQ_TYPE_EDGE_RISING>; interrupt-names = "irq", "select", "sleep", "wake"; + /* Retimer set up before Linux keeps SuperSpeed down until replugged. */ + apple,reset-inherited-usb2-connection; typec1: connector { compatible = "usb-c-connector"; @@ -533,6 +535,8 @@ <69 IRQ_TYPE_EDGE_RISING>, <71 IRQ_TYPE_EDGE_RISING>; interrupt-names = "irq", "select", "sleep", "wake"; + /* Retimer set up before Linux keeps SuperSpeed down until replugged. */ + apple,reset-inherited-usb2-connection; typec2: connector { compatible = "usb-c-connector";