]> git.baikalelectronics.ru Git - kernel.git/commitdiff
usb: typec: ucsi: Wait for the USB role switches
authorLinyu Yuan <quic_linyyuan@quicinc.com>
Mon, 25 Apr 2022 10:18:06 +0000 (18:18 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Apr 2022 11:44:57 +0000 (13:44 +0200)
When role switch module probe late than ucsi module,
fwnode_usb_role_switch_get() will return -EPROBE_DEFER,
it is better to restart ucsi init work to find
it again every 100ms, total wait time is 10 second.

It also means change ucsi init work to delayed_work.

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Linyu Yuan <quic_linyyuan@quicinc.com>
Link: https://lore.kernel.org/r/1650881886-25530-3-git-send-email-quic_linyyuan@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h

index a168510675da89644cafc5fcfb941cebcda7ae75..7574bb9a2968f7568e9e637974077ede9c485c47 100644 (file)
@@ -1053,6 +1053,14 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
        con->num = index + 1;
        con->ucsi = ucsi;
 
+       cap->fwnode = ucsi_find_fwnode(con);
+       con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
+       if (IS_ERR(con->usb_role_sw)) {
+               dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
+                       con->num);
+               return PTR_ERR(con->usb_role_sw);
+       }
+
        /* Delay other interactions with the con until registration is complete */
        mutex_lock(&con->lock);
 
@@ -1088,7 +1096,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
        if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY)
                *accessory = TYPEC_ACCESSORY_DEBUG;
 
-       cap->fwnode = ucsi_find_fwnode(con);
        cap->driver_data = con;
        cap->ops = &ucsi_ops;
 
@@ -1146,13 +1153,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
                ucsi_port_psy_changed(con);
        }
 
-       con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
-       if (IS_ERR(con->usb_role_sw)) {
-               dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
-                       con->num);
-               con->usb_role_sw = NULL;
-       }
-
        /* Only notify USB controller if partner supports USB data */
        if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB))
                u_role = USB_ROLE_NONE;
@@ -1285,12 +1285,20 @@ err:
 
 static void ucsi_init_work(struct work_struct *work)
 {
-       struct ucsi *ucsi = container_of(work, struct ucsi, work);
+       struct ucsi *ucsi = container_of(work, struct ucsi, work.work);
        int ret;
 
        ret = ucsi_init(ucsi);
        if (ret)
                dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
+
+       if (ret == -EPROBE_DEFER) {
+               if (ucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT)
+                       return;
+
+               queue_delayed_work(system_long_wq, &ucsi->work,
+                                  UCSI_ROLE_SWITCH_INTERVAL);
+       }
 }
 
 /**
@@ -1330,7 +1338,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
        if (!ucsi)
                return ERR_PTR(-ENOMEM);
 
-       INIT_WORK(&ucsi->work, ucsi_init_work);
+       INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);
        mutex_init(&ucsi->ppm_lock);
        ucsi->dev = dev;
        ucsi->ops = ops;
@@ -1365,7 +1373,7 @@ int ucsi_register(struct ucsi *ucsi)
        if (!ucsi->version)
                return -ENODEV;
 
-       queue_work(system_long_wq, &ucsi->work);
+       queue_delayed_work(system_long_wq, &ucsi->work, 0);
 
        return 0;
 }
@@ -1382,7 +1390,7 @@ void ucsi_unregister(struct ucsi *ucsi)
        u64 cmd = UCSI_SET_NOTIFICATION_ENABLE;
 
        /* Make sure that we are not in the middle of driver initialization */
-       cancel_work_sync(&ucsi->work);
+       cancel_delayed_work_sync(&ucsi->work);
 
        /* Disable notifications */
        ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
index 280f1e1bda2c9cbbdd4c561888c2878d2f1130e7..8eb391e3e592ca2bf7ecf6b3b390ba63df170061 100644 (file)
@@ -287,7 +287,11 @@ struct ucsi {
        struct ucsi_capability cap;
        struct ucsi_connector *connector;
 
-       struct work_struct work;
+       struct delayed_work work;
+       int work_count;
+#define UCSI_ROLE_SWITCH_RETRY_PER_HZ  10
+#define UCSI_ROLE_SWITCH_INTERVAL      (HZ / UCSI_ROLE_SWITCH_RETRY_PER_HZ)
+#define UCSI_ROLE_SWITCH_WAIT_COUNT    (10 * UCSI_ROLE_SWITCH_RETRY_PER_HZ)
 
        /* PPM Communication lock */
        struct mutex ppm_lock;