usb: typec: ucsi: Resume in separate work
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Wed, 23 Nov 2022 09:30:21 +0000 (11:30 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 29 Nov 2022 07:56:08 +0000 (08:56 +0100)
It can take more than one second to check each connector
when the system is resumed. So if you have, say, eight
connectors, it may take eight seconds for ucsi_resume() to
finish. That's a bit too much.

This will modify ucsi_resume() so that it schedules a work
where the interface is actually resumed instead of checking
the connectors directly. The connections will also be
checked in separate tasks which are queued for each connector
separately.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216706
Fixes: 99f6d4361113 ("usb: typec: ucsi: Check the connection on resume")
Cc: <stable@vger.kernel.org>
Reported-by: Todd Brandt <todd.e.brandt@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20221123093021.25981-1-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h

index a7987fc..eabe519 100644 (file)
@@ -1270,8 +1270,9 @@ err:
        return ret;
 }
 
-int ucsi_resume(struct ucsi *ucsi)
+static void ucsi_resume_work(struct work_struct *work)
 {
+       struct ucsi *ucsi = container_of(work, struct ucsi, resume_work);
        struct ucsi_connector *con;
        u64 command;
        int ret;
@@ -1279,15 +1280,21 @@ int ucsi_resume(struct ucsi *ucsi)
        /* Restore UCSI notification enable mask after system resume */
        command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
        ret = ucsi_send_command(ucsi, command, NULL, 0);
-       if (ret < 0)
-               return ret;
+       if (ret < 0) {
+               dev_err(ucsi->dev, "failed to re-enable notifications (%d)\n", ret);
+               return;
+       }
 
        for (con = ucsi->connector; con->port; con++) {
                mutex_lock(&con->lock);
-               ucsi_check_connection(con);
+               ucsi_partner_task(con, ucsi_check_connection, 1, 0);
                mutex_unlock(&con->lock);
        }
+}
 
+int ucsi_resume(struct ucsi *ucsi)
+{
+       queue_work(system_long_wq, &ucsi->resume_work);
        return 0;
 }
 EXPORT_SYMBOL_GPL(ucsi_resume);
@@ -1347,6 +1354,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
        if (!ucsi)
                return ERR_PTR(-ENOMEM);
 
+       INIT_WORK(&ucsi->resume_work, ucsi_resume_work);
        INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);
        mutex_init(&ucsi->ppm_lock);
        ucsi->dev = dev;
@@ -1401,6 +1409,7 @@ void ucsi_unregister(struct ucsi *ucsi)
 
        /* Make sure that we are not in the middle of driver initialization */
        cancel_delayed_work_sync(&ucsi->work);
+       cancel_work_sync(&ucsi->resume_work);
 
        /* Disable notifications */
        ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
index 8eb391e..c968474 100644 (file)
@@ -287,6 +287,7 @@ struct ucsi {
        struct ucsi_capability cap;
        struct ucsi_connector *connector;
 
+       struct work_struct resume_work;
        struct delayed_work work;
        int work_count;
 #define UCSI_ROLE_SWITCH_RETRY_PER_HZ  10