Skip to content

Commit 7fa6b25

Browse files
Christian A. Ehrhardtgregkh
authored andcommitted
usb: typec: ucsi: Fix busy loop on ASUS VivoBooks
If the busy indicator is set, all other fields in CCI should be clear according to the spec. However, some UCSI implementations do not follow this rule and report bogus data in CCI along with the busy indicator. Ignore the contents of CCI if the busy indicator is set. If a command timeout is hit it is possible that the EVENT_PENDING bit is cleared while connector work is still scheduled which can cause the EVENT_PENDING bit to go out of sync with scheduled connector work. Check and set the EVENT_PENDING bit on entry to ucsi_handle_connector_change() to fix this. Finally, check UCSI_CCI_BUSY before the return code of ->sync_control. This ensures that the command is cancelled even if ->sync_control returns an error (most likely -ETIMEDOUT). Reported-by: Anurag Bijea <[email protected]> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219108 Bisected-by: Christian Heusel <[email protected]> Tested-by: Anurag Bijea <[email protected]> Fixes: de52aca ("usb: typec: ucsi: Never send a lone connector change ack") Cc: [email protected] Signed-off-by: Christian A. Ehrhardt <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent e8afd5a commit 7fa6b25

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838

3939
void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
4040
{
41+
/* Ignore bogus data in CCI if busy indicator is set. */
42+
if (cci & UCSI_CCI_BUSY)
43+
return;
44+
4145
if (UCSI_CCI_CONNECTOR(cci))
4246
ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
4347

@@ -103,15 +107,13 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
103107
return -EINVAL;
104108

105109
ret = ucsi->ops->sync_control(ucsi, command);
106-
if (ret)
107-
return ret;
108-
109-
ret = ucsi->ops->read_cci(ucsi, cci);
110-
if (ret)
111-
return ret;
110+
if (ucsi->ops->read_cci(ucsi, cci))
111+
return -EIO;
112112

113113
if (*cci & UCSI_CCI_BUSY)
114114
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
115+
if (ret)
116+
return ret;
115117

116118
if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
117119
return -EIO;
@@ -1197,6 +1199,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
11971199

11981200
mutex_lock(&con->lock);
11991201

1202+
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
1203+
dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n",
1204+
__func__);
1205+
12001206
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
12011207

12021208
ret = ucsi_send_command_common(ucsi, command, &con->status,

0 commit comments

Comments
 (0)