usb: dwc3: Properly set system wakeup
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Fri, 8 Mar 2024 02:40:25 +0000 (02:40 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Mar 2024 13:58:05 +0000 (14:58 +0100)
If the device is configured for system wakeup, then make sure that the
xHCI driver knows about it and make sure to permit wakeup only at the
appropriate time.

For host mode, if the controller goes through the dwc3 code path, then a
child xHCI platform device is created. Make sure the platform device
also inherits the wakeup setting for xHCI to enable remote wakeup.

For device mode, make sure to disable system wakeup if no gadget driver
is bound. We may experience unwanted system wakeup due to the wakeup
signal from the controller PMU detecting connection/disconnection when
in low power (D3). E.g. In the case of Steam Deck, the PCI PME prevents
the system staying in suspend.

Cc: stable@vger.kernel.org
Reported-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
Closes: https://lore.kernel.org/linux-usb/70a7692d-647c-9be7-00a6-06fc60f77294@igalia.com/T/#mf00d6669c2eff7b308d1162acd1d66c09f0853c7
Fixes: d07e8819a03d ("usb: dwc3: add xHCI Host support")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Tested-by: Sanath S <Sanath.S@amd.com>
Tested-by: Guilherme G. Piccoli <gpiccoli@igalia.com> # Steam Deck
Link: https://lore.kernel.org/r/667cfda7009b502e08462c8fb3f65841d103cc0a.1709865476.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/host.c

index 3e55838c0001443845d975d297dcf25877fe34d5..31684cdaaae3056c6cf9b16d31cc0eb4f217b7bb 100644 (file)
@@ -1519,6 +1519,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
        else
                dwc->sysdev = dwc->dev;
 
+       dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
+
        ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
        if (ret >= 0) {
                dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
index c07edfc954f72e8bd5bb654a96ae731b1411ecf0..7e80dd3d466b88538f597a5f1534fd020ac23763 100644 (file)
@@ -1133,6 +1133,7 @@ struct dwc3_scratchpad_array {
  *     3       - Reserved
  * @dis_metastability_quirk: set to disable metastability quirk.
  * @dis_split_quirk: set to disable split boundary.
+ * @sys_wakeup: set if the device may do system wakeup.
  * @wakeup_configured: set if the device is configured for remote wakeup.
  * @suspended: set to track suspend event due to U3/L2.
  * @imod_interval: set the interrupt moderation interval in 250ns
@@ -1357,6 +1358,7 @@ struct dwc3 {
 
        unsigned                dis_split_quirk:1;
        unsigned                async_callbacks:1;
+       unsigned                sys_wakeup:1;
        unsigned                wakeup_configured:1;
        unsigned                suspended:1;
 
index 40c52dbc28d3b4a1b2ff9580dc183e0db1152bdd..4df2661f66751bd56a3717d9f0d2705a29f07738 100644 (file)
@@ -2955,6 +2955,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
        dwc->gadget_driver      = driver;
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       if (dwc->sys_wakeup)
+               device_wakeup_enable(dwc->sysdev);
+
        return 0;
 }
 
@@ -2970,6 +2973,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
        struct dwc3             *dwc = gadget_to_dwc(g);
        unsigned long           flags;
 
+       if (dwc->sys_wakeup)
+               device_wakeup_disable(dwc->sysdev);
+
        spin_lock_irqsave(&dwc->lock, flags);
        dwc->gadget_driver      = NULL;
        dwc->max_cfg_eps = 0;
@@ -4651,6 +4657,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
        else
                dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
 
+       /* No system wakeup if no gadget driver bound */
+       if (dwc->sys_wakeup)
+               device_wakeup_disable(dwc->sysdev);
+
        return 0;
 
 err5:
index 5a5cb6ce9946d36e8b3de702fec7b3ab102f0779..0204787df81d50b9291b1701c554795c3d97bca8 100644 (file)
@@ -173,6 +173,14 @@ int dwc3_host_init(struct dwc3 *dwc)
                goto err;
        }
 
+       if (dwc->sys_wakeup) {
+               /* Restore wakeup setting if switched from device */
+               device_wakeup_enable(dwc->sysdev);
+
+               /* Pass on wakeup setting to the new xhci platform device */
+               device_init_wakeup(&xhci->dev, true);
+       }
+
        return 0;
 err:
        platform_device_put(xhci);
@@ -181,6 +189,9 @@ err:
 
 void dwc3_host_exit(struct dwc3 *dwc)
 {
+       if (dwc->sys_wakeup)
+               device_init_wakeup(&dwc->xhci->dev, false);
+
        platform_device_unregister(dwc->xhci);
        dwc->xhci = NULL;
 }