@@ -61,12 +61,28 @@ static int ucsi_acknowledge_command(struct ucsi *ucsi)
static int ucsi_acknowledge_connector_change(struct ucsi *ucsi)
{
+ unsigned int con_num;
u64 ctrl;
+ u32 cci;
+ int ret;
ctrl = UCSI_ACK_CC_CI;
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
- return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+ ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+ if (ret)
+ return ret;
+
+ clear_bit(EVENT_PENDING, &ucsi->flags);
+ ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ return ret;
+
+ con_num = UCSI_CCI_CONNECTOR(cci);
+ if (con_num)
+ ucsi_connector_change(ucsi, con_num);
+
+ return 0;
}
static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
@@ -1215,8 +1231,6 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
- clear_bit(EVENT_PENDING, &con->ucsi->flags);
-
mutex_lock(&ucsi->ppm_lock);
ret = ucsi_acknowledge_connector_change(ucsi);
mutex_unlock(&ucsi->ppm_lock);
The code to handle connection change events contains a race: there is an open window for notifications to arrive between clearing EVENT_PENDING bit and sending the ACK_CC_CI command to acknowledge the connection change. This is mostly not an issue, but on Qualcomm platforms when the PPM receives ACK_CC_CI with the ConnectorChange bit set if there is no pending reported Connector Change, it responds with the CommandCompleted + NotSupported notifications, completely breaking UCSI state machine. Fix this by reading out CCI after ACK_CC_CI and scheduling the work if there is a connector change reported. Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> --- drivers/usb/typec/ucsi/ucsi.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)