[PATCH 4/5] usb: typec: fusb302: Tell SOP' messages apart from SOP

From: Alexey Charkov

Date: Tue Nov 03 2026 - 06:33:17 EST


The driver sends every message with the SOP ordered set and reports
every received one as SOP, which leaves TCPM no way to reach the cable
plug. Start a message with the SOP' ordered set when TCPM asks for it,
and report a received message as SOP' when its RX FIFO token says so.

SOP' is Sync-1, Sync-1, Sync-3, Sync-3 in USB PD. The Sync-3 TX token
and the RX FIFO token codes are from the token tables of the FUSB302B
datasheet.

Nothing changes yet: TCPM asks for SOP' only from a port that claims
cable communication, and the chip drops SOP' packets until ENSOP1 is
set.

Signed-off-by: Alexey Charkov <alchark@xxxxxxxxxxx>
---
drivers/usb/typec/tcpm/fusb302.c | 26 ++++++++++++++++++++------
drivers/usb/typec/tcpm/fusb302_reg.h | 10 ++++++++++
2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 3319f6a2b0c9..8b3a2e095ef8 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -961,7 +961,20 @@ static int tcpm_start_toggling(struct tcpc_dev *dev,
return ret;
}

+/* K-codes of the ordered set that starts each kind of packet */
+static const u8 fusb302_sop_tokens[][4] = {
+ [TCPC_TX_SOP] = {
+ FUSB302_TKN_SYNC1, FUSB302_TKN_SYNC1,
+ FUSB302_TKN_SYNC1, FUSB302_TKN_SYNC2,
+ },
+ [TCPC_TX_SOP_PRIME] = {
+ FUSB302_TKN_SYNC1, FUSB302_TKN_SYNC1,
+ FUSB302_TKN_SYNC3, FUSB302_TKN_SYNC3,
+ },
+};
+
static int fusb302_pd_send_message(struct fusb302_chip *chip,
+ enum tcpm_transmit_type type,
const struct pd_message *msg)
{
int ret = 0;
@@ -969,11 +982,9 @@ static int fusb302_pd_send_message(struct fusb302_chip *chip,
u8 pos = 0;
int len;

- /* SOP tokens */
- buf[pos++] = FUSB302_TKN_SYNC1;
- buf[pos++] = FUSB302_TKN_SYNC1;
- buf[pos++] = FUSB302_TKN_SYNC1;
- buf[pos++] = FUSB302_TKN_SYNC2;
+ memcpy(&buf[pos], fusb302_sop_tokens[type],
+ sizeof(fusb302_sop_tokens[type]));
+ pos += sizeof(fusb302_sop_tokens[type]);

len = pd_header_cnt_le(msg->header) * 4;
/* plug 2 for header */
@@ -1037,6 +1048,7 @@ static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type,
mutex_lock(&chip->lock);
switch (type) {
case TCPC_TX_SOP:
+ case TCPC_TX_SOP_PRIME:
/* nRetryCount 3 in P2.0 spec, whereas 2 in PD3.0 spec */
ret = fusb302_enable_tx_auto_retries(chip, negotiated_rev > PD_REV20 ?
FUSB_REG_CONTROL3_N_RETRIES_2 :
@@ -1044,7 +1056,7 @@ static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type,
if (ret < 0)
fusb302_log(chip, "Cannot update retry count ret=%d", ret);

- ret = fusb302_pd_send_message(chip, msg);
+ ret = fusb302_pd_send_message(chip, type, msg);
if (ret < 0)
fusb302_log(chip,
"cannot send PD message, ret=%d", ret);
@@ -1475,6 +1487,8 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip,
*/
if ((!len) && (pd_header_type_le(msg->header) == PD_CTRL_GOOD_CRC))
tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS);
+ else if ((token & FUSB302_RX_TKN_MASK) == FUSB302_RX_TKN_SOP1)
+ tcpm_pd_receive(chip->tcpm_port, msg, TCPC_TX_SOP_PRIME);
else
tcpm_pd_receive(chip->tcpm_port, msg, TCPC_TX_SOP);

diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h
index edc0e4b0f1e6..c98a502bfbfc 100644
--- a/drivers/usb/typec/tcpm/fusb302_reg.h
+++ b/drivers/usb/typec/tcpm/fusb302_reg.h
@@ -174,4 +174,14 @@ enum fusb302_txfifo_tokens {
FUSB302_TKN_TXOFF = 0xFE,
};

+/* Tokens starting each packet in the FUSB302 RX FIFO, valid under the mask */
+#define FUSB302_RX_TKN_MASK GENMASK(7, 5)
+enum fusb302_rxfifo_tokens {
+ FUSB302_RX_TKN_SOP2DB = 0x60,
+ FUSB302_RX_TKN_SOP1DB = 0x80,
+ FUSB302_RX_TKN_SOP2 = 0xA0,
+ FUSB302_RX_TKN_SOP1 = 0xC0,
+ FUSB302_RX_TKN_SOP = 0xE0,
+};
+
#endif

--
2.56.0