Skip to content

Commit ef52b4a

Browse files
kyletsoadlgregkh
authored andcommitted
usb: typec: tcpm: Raise vdm_sm_running flag only when VDM SM is running
If the port is going to send Discover_Identity Message, vdm_sm_running flag was intentionally set before entering Ready States in order to avoid the conflict because the port and the port partner might start AMS at almost the same time after entering Ready States. However, the original design has a problem. When the port is doing DR_SWAP from Device to Host, it raises the flag. Later in the tcpm_send_discover_work, the flag blocks the procedure of sending the Discover_Identity and it might never be cleared until disconnection. Since there exists another flag send_discover representing that the port is going to send Discover_Identity or not, it is enough to use that flag to prevent the conflict. Also change the timing of the set/clear of vdm_sm_running to indicate whether the VDM SM is actually running or not. Fixes: c34e85f ("usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work") Cc: stable <[email protected]> Cc: Badhri Jagan Sridharan <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Acked-by: Heikki Krogerus <[email protected]> Signed-off-by: Kyle Tso <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c82cacd commit ef52b4a

File tree

1 file changed

+38
-43
lines changed

1 file changed

+38
-43
lines changed

drivers/usb/typec/tcpm/tcpm.c

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ struct tcpm_port {
341341
bool vbus_source;
342342
bool vbus_charge;
343343

344+
/* Set to true when Discover_Identity Command is expected to be sent in Ready states. */
344345
bool send_discover;
345346
bool op_vsafe5v;
346347

@@ -370,6 +371,7 @@ struct tcpm_port {
370371
struct hrtimer send_discover_timer;
371372
struct kthread_work send_discover_work;
372373
bool state_machine_running;
374+
/* Set to true when VDM State Machine has following actions. */
373375
bool vdm_sm_running;
374376

375377
struct completion tx_complete;
@@ -1431,6 +1433,7 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
14311433
/* Set ready, vdm state machine will actually send */
14321434
port->vdm_retries = 0;
14331435
port->vdm_state = VDM_STATE_READY;
1436+
port->vdm_sm_running = true;
14341437

14351438
mod_vdm_delayed_work(port, 0);
14361439
}
@@ -1673,7 +1676,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
16731676
rlen = 1;
16741677
} else {
16751678
tcpm_register_partner_altmodes(port);
1676-
port->vdm_sm_running = false;
16771679
}
16781680
break;
16791681
case CMD_ENTER_MODE:
@@ -1721,14 +1723,12 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
17211723
(VDO_SVDM_VERS(svdm_version));
17221724
break;
17231725
}
1724-
port->vdm_sm_running = false;
17251726
break;
17261727
default:
17271728
response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK);
17281729
rlen = 1;
17291730
response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) |
17301731
(VDO_SVDM_VERS(svdm_version));
1731-
port->vdm_sm_running = false;
17321732
break;
17331733
}
17341734

@@ -1769,6 +1769,20 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
17691769
}
17701770

17711771
if (PD_VDO_SVDM(p[0]) && (adev || tcpm_vdm_ams(port) || port->nr_snk_vdo)) {
1772+
/*
1773+
* Here a SVDM is received (INIT or RSP or unknown). Set the vdm_sm_running in
1774+
* advance because we are dropping the lock but may send VDMs soon.
1775+
* For the cases of INIT received:
1776+
* - If no response to send, it will be cleared later in this function.
1777+
* - If there are responses to send, it will be cleared in the state machine.
1778+
* For the cases of RSP received:
1779+
* - If no further INIT to send, it will be cleared later in this function.
1780+
* - Otherwise, it will be cleared in the state machine if timeout or it will go
1781+
* back here until no further INIT to send.
1782+
* For the cases of unknown type received:
1783+
* - We will send NAK and the flag will be cleared in the state machine.
1784+
*/
1785+
port->vdm_sm_running = true;
17721786
rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action);
17731787
} else {
17741788
if (port->negotiated_rev >= PD_REV30)
@@ -1837,6 +1851,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
18371851

18381852
if (rlen > 0)
18391853
tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
1854+
else
1855+
port->vdm_sm_running = false;
18401856
}
18411857

18421858
static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd,
@@ -1902,8 +1918,10 @@ static void vdm_run_state_machine(struct tcpm_port *port)
19021918
* if there's traffic or we're not in PDO ready state don't send
19031919
* a VDM.
19041920
*/
1905-
if (port->state != SRC_READY && port->state != SNK_READY)
1921+
if (port->state != SRC_READY && port->state != SNK_READY) {
1922+
port->vdm_sm_running = false;
19061923
break;
1924+
}
19071925

19081926
/* TODO: AMS operation for Unstructured VDM */
19091927
if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMDT(vdo_hdr) == CMDT_INIT) {
@@ -2556,10 +2574,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
25562574
TYPEC_PWR_MODE_PD,
25572575
port->pps_data.active,
25582576
port->supply_voltage);
2559-
/* Set VDM running flag ASAP */
2560-
if (port->data_role == TYPEC_HOST &&
2561-
port->send_discover)
2562-
port->vdm_sm_running = true;
25632577
tcpm_set_state(port, SNK_READY, 0);
25642578
} else {
25652579
/*
@@ -2597,14 +2611,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
25972611
switch (port->state) {
25982612
case SNK_NEGOTIATE_CAPABILITIES:
25992613
/* USB PD specification, Figure 8-43 */
2600-
if (port->explicit_contract) {
2614+
if (port->explicit_contract)
26012615
next_state = SNK_READY;
2602-
if (port->data_role == TYPEC_HOST &&
2603-
port->send_discover)
2604-
port->vdm_sm_running = true;
2605-
} else {
2616+
else
26062617
next_state = SNK_WAIT_CAPABILITIES;
2607-
}
26082618

26092619
/* Threshold was relaxed before sending Request. Restore it back. */
26102620
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
@@ -2619,10 +2629,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
26192629
port->pps_status = (type == PD_CTRL_WAIT ?
26202630
-EAGAIN : -EOPNOTSUPP);
26212631

2622-
if (port->data_role == TYPEC_HOST &&
2623-
port->send_discover)
2624-
port->vdm_sm_running = true;
2625-
26262632
/* Threshold was relaxed before sending Request. Restore it back. */
26272633
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
26282634
port->pps_data.active,
@@ -2698,10 +2704,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
26982704
}
26992705
break;
27002706
case DR_SWAP_SEND:
2701-
if (port->data_role == TYPEC_DEVICE &&
2702-
port->send_discover)
2703-
port->vdm_sm_running = true;
2704-
27052707
tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0);
27062708
break;
27072709
case PR_SWAP_SEND:
@@ -2739,7 +2741,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
27392741
PD_MSG_CTRL_NOT_SUPP,
27402742
NONE_AMS);
27412743
} else {
2742-
if (port->vdm_sm_running) {
2744+
if (port->send_discover) {
27432745
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
27442746
break;
27452747
}
@@ -2755,7 +2757,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
27552757
PD_MSG_CTRL_NOT_SUPP,
27562758
NONE_AMS);
27572759
} else {
2758-
if (port->vdm_sm_running) {
2760+
if (port->send_discover) {
27592761
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
27602762
break;
27612763
}
@@ -2764,7 +2766,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
27642766
}
27652767
break;
27662768
case PD_CTRL_VCONN_SWAP:
2767-
if (port->vdm_sm_running) {
2769+
if (port->send_discover) {
27682770
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
27692771
break;
27702772
}
@@ -4480,18 +4482,20 @@ static void run_state_machine(struct tcpm_port *port)
44804482
/* DR_Swap states */
44814483
case DR_SWAP_SEND:
44824484
tcpm_pd_send_control(port, PD_CTRL_DR_SWAP);
4485+
if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20)
4486+
port->send_discover = true;
44834487
tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT,
44844488
PD_T_SENDER_RESPONSE);
44854489
break;
44864490
case DR_SWAP_ACCEPT:
44874491
tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
4488-
/* Set VDM state machine running flag ASAP */
4489-
if (port->data_role == TYPEC_DEVICE && port->send_discover)
4490-
port->vdm_sm_running = true;
4492+
if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20)
4493+
port->send_discover = true;
44914494
tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0);
44924495
break;
44934496
case DR_SWAP_SEND_TIMEOUT:
44944497
tcpm_swap_complete(port, -ETIMEDOUT);
4498+
port->send_discover = false;
44954499
tcpm_ams_finish(port);
44964500
tcpm_set_state(port, ready_state(port), 0);
44974501
break;
@@ -4503,7 +4507,6 @@ static void run_state_machine(struct tcpm_port *port)
45034507
} else {
45044508
tcpm_set_roles(port, true, port->pwr_role,
45054509
TYPEC_HOST);
4506-
port->send_discover = true;
45074510
}
45084511
tcpm_ams_finish(port);
45094512
tcpm_set_state(port, ready_state(port), 0);
@@ -4646,8 +4649,6 @@ static void run_state_machine(struct tcpm_port *port)
46464649
break;
46474650
case VCONN_SWAP_SEND_TIMEOUT:
46484651
tcpm_swap_complete(port, -ETIMEDOUT);
4649-
if (port->data_role == TYPEC_HOST && port->send_discover)
4650-
port->vdm_sm_running = true;
46514652
tcpm_set_state(port, ready_state(port), 0);
46524653
break;
46534654
case VCONN_SWAP_START:
@@ -4663,23 +4664,17 @@ static void run_state_machine(struct tcpm_port *port)
46634664
case VCONN_SWAP_TURN_ON_VCONN:
46644665
tcpm_set_vconn(port, true);
46654666
tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
4666-
if (port->data_role == TYPEC_HOST && port->send_discover)
4667-
port->vdm_sm_running = true;
46684667
tcpm_set_state(port, ready_state(port), 0);
46694668
break;
46704669
case VCONN_SWAP_TURN_OFF_VCONN:
46714670
tcpm_set_vconn(port, false);
4672-
if (port->data_role == TYPEC_HOST && port->send_discover)
4673-
port->vdm_sm_running = true;
46744671
tcpm_set_state(port, ready_state(port), 0);
46754672
break;
46764673

46774674
case DR_SWAP_CANCEL:
46784675
case PR_SWAP_CANCEL:
46794676
case VCONN_SWAP_CANCEL:
46804677
tcpm_swap_complete(port, port->swap_status);
4681-
if (port->data_role == TYPEC_HOST && port->send_discover)
4682-
port->vdm_sm_running = true;
46834678
if (port->pwr_role == TYPEC_SOURCE)
46844679
tcpm_set_state(port, SRC_READY, 0);
46854680
else
@@ -5029,9 +5024,6 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
50295024
switch (port->state) {
50305025
case SNK_TRANSITION_SINK_VBUS:
50315026
port->explicit_contract = true;
5032-
/* Set the VDM flag ASAP */
5033-
if (port->data_role == TYPEC_HOST && port->send_discover)
5034-
port->vdm_sm_running = true;
50355027
tcpm_set_state(port, SNK_READY, 0);
50365028
break;
50375029
case SNK_DISCOVERY:
@@ -5426,15 +5418,18 @@ static void tcpm_send_discover_work(struct kthread_work *work)
54265418
if (!port->send_discover)
54275419
goto unlock;
54285420

5421+
if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) {
5422+
port->send_discover = false;
5423+
goto unlock;
5424+
}
5425+
54295426
/* Retry if the port is not idle */
54305427
if ((port->state != SRC_READY && port->state != SNK_READY) || port->vdm_sm_running) {
54315428
mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
54325429
goto unlock;
54335430
}
54345431

5435-
/* Only send the Message if the port is host for PD rev2.0 */
5436-
if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20)
5437-
tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0);
5432+
tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0);
54385433

54395434
unlock:
54405435
mutex_unlock(&port->lock);

0 commit comments

Comments
 (0)