[U-Boot] [PATCH] usb: dwc2: fix gadget disconnect

Marek Vasut marex at denx.de
Wed Apr 17 18:54:58 UTC 2019


On 4/17/19 4:46 PM, Fabrice Gasnier wrote:
> This fixes a disconnect issue detected with fastboot command, when using
> dwc2 driver.
> - On u-boot side:
> uboot>$ fastboot 0
> - On USB host PC side, few seconds after
> PC>$ fastboot reboot # Get stuck, uboot target never reboots
> 
> By enabling DEBUG_ISR logs, the bus suspend interrupt is seen before the
> PC command has been issued. When the USB bus suspend occurs, there's a HACK
> that disables the fastboot (composite driver). Here is the call stack
> upon USB bus suspend:
> - dwc2_handle_usb_suspend_intr()
>   - dev->driver->disconnect()
>     - composite_disconnect()
>       - reset_config()
>         - f->disable()
>           - fastboot_disable()
>             - usb_ep_disable(f_fb->out_ep);
>             - usb_ep_disable(f_fb->in_ep);
>             .. other disable calls.
> 
> When the resume interrupt happens, everything has been disabled, then
> nothing happens. fastboot command gets stuck on HOST side.
> 
> Remove original HACK, that disconnects the composite driver upon
> USB bus suspend. Implement disconnect detection instead:
> - check GINTSTS OTG interrupt
> - read GOTGINT register
> - check GOTGINT, SesEndDet bit (e.g. session end)
> This is inspired by what is implemented currently in Linux dwc2 driver.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier at st.com>

Reviewed-by: Marek Vasut <marex at denx.de>

This is Lukasz's topic, so you need his RB too.

Thanks

> ---
> 
>  drivers/usb/gadget/dwc2_udc_otg_regs.h     |  6 +++++-
>  drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c | 14 ++++++++++++--
>  2 files changed, 17 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/gadget/dwc2_udc_otg_regs.h b/drivers/usb/gadget/dwc2_udc_otg_regs.h
> index a1829b3..b685256 100644
> --- a/drivers/usb/gadget/dwc2_udc_otg_regs.h
> +++ b/drivers/usb/gadget/dwc2_udc_otg_regs.h
> @@ -86,6 +86,9 @@ struct dwc2_usbotg_reg {
>  #define B_SESSION_VALID		(0x1<<19)
>  #define A_SESSION_VALID		(0x1<<18)
>  
> +/* DWC2_UDC_OTG_GOTINT */
> +#define GOTGINT_SES_END_DET		(1<<2)
> +
>  /* DWC2_UDC_OTG_GAHBCFG */
>  #define PTXFE_HALF			(0<<8)
>  #define PTXFE_ZERO			(1<<8)
> @@ -118,6 +121,7 @@ struct dwc2_usbotg_reg {
>  #define INT_NP_TX_FIFO_EMPTY		(0x1<<5)
>  #define INT_RX_FIFO_NOT_EMPTY		(0x1<<4)
>  #define INT_SOF			(0x1<<3)
> +#define INT_OTG			(0x1<<2)
>  #define INT_DEV_MODE			(0x0<<0)
>  #define INT_HOST_MODE			(0x1<<1)
>  #define INT_GOUTNakEff			(0x01<<7)
> @@ -246,7 +250,7 @@ struct dwc2_usbotg_reg {
>  
>  /* Masks definitions */
>  #define GINTMSK_INIT	(INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\
> -			| INT_RESET | INT_SUSPEND)
> +			| INT_RESET | INT_SUSPEND | INT_OTG)
>  #define DOEPMSK_INIT	(CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE)
>  #define DIEPMSK_INIT	(NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE)
>  #define GAHBCFG_INIT	(PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\
> diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
> index a75af49..7eb632d 100644
> --- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
> +++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
> @@ -467,7 +467,7 @@ static void process_ep_out_intr(struct dwc2_udc *dev)
>  static int dwc2_udc_irq(int irq, void *_dev)
>  {
>  	struct dwc2_udc *dev = _dev;
> -	u32 intr_status;
> +	u32 intr_status, gotgint;
>  	u32 usb_status, gintmsk;
>  	unsigned long flags = 0;
>  
> @@ -521,14 +521,24 @@ static int dwc2_udc_irq(int irq, void *_dev)
>  		    && dev->driver) {
>  			if (dev->driver->suspend)
>  				dev->driver->suspend(&dev->gadget);
> +		}
> +	}
> +
> +	if (intr_status & INT_OTG) {
> +		gotgint = readl(&reg->gotgint);
> +		debug_cond(DEBUG_ISR,
> +			   "\tOTG interrupt: (GOTGINT):0x%x\n", gotgint);
>  
> -			/* HACK to let gadget detect disconnected state */
> +		if (gotgint & GOTGINT_SES_END_DET) {
> +			debug_cond(DEBUG_ISR, "\t\tSession End Detected\n");
> +			/* Let gadget detect disconnected state */
>  			if (dev->driver->disconnect) {
>  				spin_unlock_irqrestore(&dev->lock, flags);
>  				dev->driver->disconnect(&dev->gadget);
>  				spin_lock_irqsave(&dev->lock, flags);
>  			}
>  		}
> +		writel(gotgint, &reg->gotgint);
>  	}
>  
>  	if (intr_status & INT_RESUME) {
> 


-- 
Best regards,
Marek Vasut


More information about the U-Boot mailing list