usb: typec: ucsi: Fix role swapping
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Tue, 5 Apr 2022 13:48:24 +0000 (16:48 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 May 2022 07:03:22 +0000 (09:03 +0200)
commit eb5d7ff3cf0d55093c619b5ad107cd5c05ce8134 upstream.

All attempts to swap the roles timed out because the
completion was done without releasing the port lock. Fixing
that by releasing the lock before starting to wait for the
completion.

Link: https://lore.kernel.org/linux-usb/037de7ac-e210-bdf5-ec7a-8c0c88a0be20@gmail.com/
Fixes: ad74b8649bea ("usb: typec: ucsi: Preliminary support for alternate modes")
Cc: stable@vger.kernel.org
Reported-and-tested-by: Jia-Ju Bai <baijiaju1990@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20220405134824.68067-3-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/ucsi.c

index f3934f2d3c443df5e27d66543178ee04461be20e..0268e654cae68bc514b9b5874e4606096185c4b8 100644 (file)
@@ -748,14 +748,18 @@ ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role)
        if (ret < 0)
                goto out_unlock;
 
+       mutex_unlock(&con->lock);
+
        if (!wait_for_completion_timeout(&con->complete,
-                                       msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
-               ret = -ETIMEDOUT;
+                                        msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
+               return -ETIMEDOUT;
+
+       return 0;
 
 out_unlock:
        mutex_unlock(&con->lock);
 
-       return ret < 0 ? ret : 0;
+       return ret;
 }
 
 static int
@@ -780,11 +784,13 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role)
        if (ret < 0)
                goto out_unlock;
 
+       mutex_unlock(&con->lock);
+
        if (!wait_for_completion_timeout(&con->complete,
-                               msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) {
-               ret = -ETIMEDOUT;
-               goto out_unlock;
-       }
+                                        msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
+               return -ETIMEDOUT;
+
+       mutex_lock(&con->lock);
 
        /* Something has gone wrong while swapping the role */
        if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) {