diff --git a/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml b/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml index 45706bf194f47..a1f25de21b2e6 100644 --- a/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml +++ b/Documentation/devicetree/bindings/usb/apple,sn201202x.yaml @@ -71,10 +71,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 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 522076cd14fa3..940a7eaf6fc78 100644 --- a/arch/arm64/boot/dts/apple/t6041-j575c.dts +++ b/arch/arm64/boot/dts/apple/t6041-j575c.dts @@ -1409,6 +1409,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"; @@ -1438,6 +1440,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"; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index a43e56d31133a..538959833e4ee 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -3168,27 +3168,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) { @@ -3224,7 +3277,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; } diff --git a/drivers/usb/typec/tipd/spmi.c b/drivers/usb/typec/tipd/spmi.c index f4abac0b3a457..ddd28b8d94e46 100644 --- a/drivers/usb/typec/tipd/spmi.c +++ b/drivers/usb/typec/tipd/spmi.c @@ -995,6 +995,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,