]> git.hungrycats.org Git - linux/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>
Wed, 3 Apr 2024 13:11:56 +0000 (15:11 +0200)
commit 3de4f996a0b5412aa451729008130a488f71563e upstream.

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 cba01a4993a11526d48abb08ab678b7f03b1ce7d..70d9f4eebf1a7a8d0de33bfe466218c76a891124 100644 (file)
@@ -982,13 +982,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)