]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
usb: typec: ucsi: Clear UCSI_CCI_RESET_COMPLETE before reset
authorChristian A. Ehrhardt <lk@c--e.de>
Wed, 20 Mar 2024 07:39:26 +0000 (08:39 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Mar 2024 14:00:48 +0000 (15:00 +0100)
Check the UCSI_CCI_RESET_COMPLETE complete flag before starting
another reset. Use a UCSI_SET_NOTIFICATION_ENABLE command to clear
the flag if it is set.

Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
Cc: stable <stable@kernel.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-QRD
Link: https://lore.kernel.org/r/20240320073927.1641788-6-lk@c--e.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/ucsi.c

index 63f340dbd8674376452af9bd431900ab0ed7b9d4..85e507df7fa86f73ec58d6cd96cf5c845deab672 100644 (file)
@@ -1264,13 +1264,47 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
 
 static int ucsi_reset_ppm(struct ucsi *ucsi)
 {
-       u64 command = UCSI_PPM_RESET;
+       u64 command;
        unsigned long tmo;
        u32 cci;
        int ret;
 
        mutex_lock(&ucsi->ppm_lock);
 
+       ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+       if (ret < 0)
+               goto out;
+
+       /*
+        * If UCSI_CCI_RESET_COMPLETE is already set we must clear
+        * the flag before we start another reset. Send a
+        * UCSI_SET_NOTIFICATION_ENABLE command to achieve this.
+        * Ignore a timeout and try the reset anyway if this fails.
+        */
+       if (cci & UCSI_CCI_RESET_COMPLETE) {
+               command = UCSI_SET_NOTIFICATION_ENABLE;
+               ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
+                                            sizeof(command));
+               if (ret < 0)
+                       goto out;
+
+               tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
+               do {
+                       ret = ucsi->ops->read(ucsi, UCSI_CCI,
+                                             &cci, sizeof(cci));
+                       if (ret < 0)
+                               goto out;
+                       if (cci & UCSI_CCI_COMMAND_COMPLETE)
+                               break;
+                       if (time_is_before_jiffies(tmo))
+                               break;
+                       msleep(20);
+               } while (1);
+
+               WARN_ON(cci & UCSI_CCI_RESET_COMPLETE);
+       }
+
+       command = UCSI_PPM_RESET;
        ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
                                     sizeof(command));
        if (ret < 0)