Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Documentation/devicetree/bindings/usb/apple,sn201202x.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions arch/arm64/boot/dts/apple/t6041-j575c-atc3.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/boot/dts/apple/t6041-j575c.dts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Expand Down
100 changes: 89 additions & 11 deletions drivers/usb/typec/tipd/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 5 additions & 0 deletions drivers/usb/typec/tipd/spmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading