Add SRC_VDM_IDENTITY_REQUEST state which first enters after SRC_STARTUP. The state sends Discover Identity on SOP' and transitions to SRC_SEND_CAPABILITIES. SRC_SEND_CAPABILITIES will transition back into SRC_VDM_IDENTITY_REQUEST instead of retrying immediately. Signed-off-by: RD Babiera <rdbabiera@xxxxxxxxxx> Reviewed-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> --- Changes since v2: * Added reviewed-by tag --- drivers/usb/typec/tcpm/tcpm.c | 49 ++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a870fbc6bc35..1ce525c8f97c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -146,7 +146,9 @@ S(PORT_RESET_WAIT_OFF), \ \ S(AMS_START), \ - S(CHUNK_NOT_SUPP) + S(CHUNK_NOT_SUPP), \ + \ + S(SRC_VDM_IDENTITY_REQUEST) #define FOREACH_AMS(S) \ S(NONE_AMS), \ @@ -1963,6 +1965,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, ret = tcpm_ams_start(port, VCONN_SWAP); if (!ret) return 0; + /* Cannot perform Vconn swap */ port->upcoming_state = INVALID_STATE; port->send_discover_prime = false; } @@ -1994,6 +1997,16 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, * the svdm_version for the cable moving forward. */ svdm_consume_identity_sop_prime(port, p, cnt); + + /* + * If received in SRC_VDM_IDENTITY_REQUEST, continue + * to SRC_SEND_CAPABILITIES + */ + if (port->state == SRC_VDM_IDENTITY_REQUEST) { + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + return 0; + } + *response_tx_sop_type = TCPC_TX_SOP; response[0] = VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(typec), @@ -2288,7 +2301,8 @@ static void vdm_run_state_machine(struct tcpm_port *port) * if there's traffic or we're not in PDO ready state don't send * a VDM. */ - if (port->state != SRC_READY && port->state != SNK_READY) { + if (port->state != SRC_READY && port->state != SNK_READY && + port->state != SRC_VDM_IDENTITY_REQUEST) { port->vdm_sm_running = false; break; } @@ -2364,13 +2378,22 @@ static void vdm_run_state_machine(struct tcpm_port *port) tcpm_ams_finish(port); break; case VDM_STATE_ERR_SEND: + /* + * When sending Discover Identity to SOP' before establishing an + * explicit contract, do not retry. Instead, weave sending + * Source_Capabilities over SOP and Discover Identity over SOP'. + */ + if (port->state == SRC_VDM_IDENTITY_REQUEST) { + tcpm_ams_finish(port); + port->vdm_state = VDM_STATE_DONE; + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); /* * A partner which does not support USB PD will not reply, * so this is not a fatal error. At the same time, some * devices may not return GoodCRC under some circumstances, * so we need to retry. */ - if (port->vdm_retries < 3) { + } else if (port->vdm_retries < 3) { tcpm_log(port, "VDM Tx error, retry"); port->vdm_retries++; port->vdm_state = VDM_STATE_READY; @@ -4478,8 +4501,12 @@ static void run_state_machine(struct tcpm_port *port) } ret = tcpm_pd_send_source_caps(port); if (ret < 0) { - tcpm_set_state(port, SRC_SEND_CAPABILITIES, - PD_T_SEND_SOURCE_CAP); + if (tcpm_can_communicate_sop_prime(port) && + IS_ERR_OR_NULL(port->cable)) + tcpm_set_state(port, SRC_VDM_IDENTITY_REQUEST, 0); + else + tcpm_set_state(port, SRC_SEND_CAPABILITIES, + PD_T_SEND_SOURCE_CAP); } else { /* * Per standard, we should clear the reset counter here. @@ -5395,6 +5422,15 @@ static void run_state_machine(struct tcpm_port *port) tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); tcpm_set_state(port, port->pwr_role == TYPEC_SOURCE ? SRC_READY : SNK_READY, 0); break; + + /* Cable states */ + case SRC_VDM_IDENTITY_REQUEST: + port->send_discover_prime = true; + port->tx_sop_type = TCPC_TX_SOP_PRIME; + mod_send_discover_delayed_work(port, 0); + port->upcoming_state = SRC_SEND_CAPABILITIES; + break; + default: WARN(1, "Unexpected port state %d\n", port->state); break; @@ -6120,7 +6156,8 @@ static void tcpm_send_discover_work(struct kthread_work *work) } /* Retry if the port is not idle */ - if ((port->state != SRC_READY && port->state != SNK_READY) || port->vdm_sm_running) { + if ((port->state != SRC_READY && port->state != SNK_READY && + port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) { mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); goto unlock; } -- 2.43.0.472.g3155946c3a-goog