On Fri, Apr 28, 2023, Roger Quadros wrote: > > > On 28/04/2023 01:13, Thinh Nguyen wrote: > > On Thu, Apr 27, 2023, Roger Quadros wrote: > >> Hi, > >> > >> On 26/04/2023 23:01, Thinh Nguyen wrote: > >>> Hi, > >>> > >>> On Wed, Apr 26, 2023, Roger Quadros wrote: > >>>> Hi Thinh, > >>>> > >>>> On Linux kernel v6.3 > >>>> Test procedure: > >>>> > >>>> - modprobe g_zero > >>>> - Connect to PC host > >>>> - systemctl suspend > >>>> > >>>> A large delay of 3 seconds is observed. The delay comes from dwc3_gadget_suspend()->dwc3_gadget_run_stop() waiting for DWC3_DSTS_DEVCTRLHLT to be set. > >>>> It returns -ETIMEDOUT. > >>>> > >>>> Are we missing something to do a clean stop during suspend? > >>>> > >>>> FYI. Unloading g_zero does not show this delay on stop. > >>>> > >>>> cheers, > >>>> -roger > >>> > >>> When clearing run_stop bit and the controller doesn't halt, that usually > >>> means there are active transfers/endpoints that aren't ended yet. > >>> > >>> The dwc3_gadget_suspend() doesn't properly do all the cleanup before > >>> clearing the run_stop bit. I think you just need to call > >>> dwc3_gadget_soft_disconnect() in dwc3_gadget_suspend() to fix this. > >> > >> That seems to do the trick. > >> How does this look? > >> > >> -------------------------- drivers/usb/dwc3/gadget.c -------------------------- > >> @@ -4674,11 +4676,18 @@ void dwc3_gadget_exit(struct dwc3 *dwc) > >> int dwc3_gadget_suspend(struct dwc3 *dwc) > >> { > >> unsigned long flags; > >> + int ret; > >> > >> - if (!dwc->gadget_driver) > >> + if (!dwc->gadget_driver || !dwc->softconnect) > >> return 0; > >> > >> - dwc3_gadget_run_stop(dwc, false, false); > >> + ret = dwc3_gadget_soft_disconnect(dwc); > >> + if (ret) > >> + goto err0; > >> + > >> + ret = dwc3_gadget_run_stop(dwc, false, false); > >> + if (ret) > >> + goto err1; > >> > > > > We already clear run_stop in dwc3_gadget_soft_disconnect(). > > > > Can you try the following change (not tested): > > > > diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c > > index c0ca4d12f95d..2996bcb4d53d 100644 > > --- a/drivers/usb/dwc3/gadget.c > > +++ b/drivers/usb/dwc3/gadget.c > > @@ -2699,6 +2699,21 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) > > return ret; > > } > > > > +static int dwc3_gadget_soft_connect(struct dwc3 *dwc) > > +{ > > + /* > > + * In the Synopsys DWC_usb31 1.90a programming guide section > > + * 4.1.9, it specifies that for a reconnect after a > > + * device-initiated disconnect requires a core soft reset > > + * (DCTL.CSftRst) before enabling the run/stop bit. > > + */ > > + dwc3_core_soft_reset(dwc); > > + > > + dwc3_event_buffers_setup(dwc); > > + __dwc3_gadget_start(dwc); > > + return dwc3_gadget_run_stop(dwc, true); > > return dwc3_gadget_run_stop(dwc, true, false); You need to rebase your change to Greg's usb-next. > > > +}> + > > static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) > > { > > struct dwc3 *dwc = gadget_to_dwc(g); > > @@ -2737,21 +2752,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) > > > > synchronize_irq(dwc->irq_gadget); > > > > - if (!is_on) { > > + if (!is_on) > > ret = dwc3_gadget_soft_disconnect(dwc); > > - } else { > > - /* > > - * In the Synopsys DWC_usb31 1.90a programming guide section > > - * 4.1.9, it specifies that for a reconnect after a > > - * device-initiated disconnect requires a core soft reset > > - * (DCTL.CSftRst) before enabling the run/stop bit. > > - */ > > - dwc3_core_soft_reset(dwc); > > - > > - dwc3_event_buffers_setup(dwc); > > - __dwc3_gadget_start(dwc); > > - ret = dwc3_gadget_run_stop(dwc, true); > > - } > > + else > > + ret = dwc3_gadget_soft_connect(dwc); > > > > pm_runtime_put(dwc->dev); > > > > @@ -4655,42 +4659,39 @@ void dwc3_gadget_exit(struct dwc3 *dwc) > > int dwc3_gadget_suspend(struct dwc3 *dwc) > > { > > unsigned long flags; > > + int ret; > > > > if (!dwc->gadget_driver) > > We need to check for dwc->softconnect here. If it is not set that means > controller has already stopped so we can simply exit. That makes sense. Perhaps that can be a separate change? > > > return 0; > > > > - dwc3_gadget_run_stop(dwc, false); > > + ret = dwc3_gadget_soft_disconnect(dwc); > > + if (ret) > > + goto err; > > > > spin_lock_irqsave(&dwc->lock, flags); > > dwc3_disconnect_gadget(dwc); > > - __dwc3_gadget_stop(dwc); > > spin_unlock_irqrestore(&dwc->lock, flags); > > > > return 0; > > + > > +err: > > + /* > > + * Attempt to reset the controller's state. Likely no > > + * communication can be established until the host > > + * performs a port reset. > > + */ > > + if (dwc->softconnect) If we check dwc->softconnect at start, we can skip the check here. > > + dwc3_gadget_soft_connect(dwc); > > + > > + return ret; > > } > > > > int dwc3_gadget_resume(struct dwc3 *dwc) > > { > > - int ret; > > - > > if (!dwc->gadget_driver || !dwc->softconnect) > > return 0; > > > > - ret = __dwc3_gadget_start(dwc); > > - if (ret < 0) > > - goto err0; > > - > > - ret = dwc3_gadget_run_stop(dwc, true); > > - if (ret < 0) > > - goto err1; > > - > > - return 0; > > - > > -err1: > > - __dwc3_gadget_stop(dwc); > > - > > -err0: > > - return ret; > > + return dwc3_gadget_soft_connect(dwc); > > } > > > > void dwc3_gadget_process_pending_events(struct dwc3 *dwc) > > Everything else looks ok. I will send a patch soon. > Thanks, Thinh