diff options
author | Fabrice Gasnier <fabrice.gasnier@st.com> | 2019-04-17 16:46:13 +0200 |
---|---|---|
committer | Marek Vasut <marex@denx.de> | 2019-04-21 10:26:52 +0200 |
commit | 7fd9f31c6bd13609da61b985cf8f5f65ebebd913 (patch) | |
tree | a067d20542b434eaa47e8b28ef0fb4f3d250b4f5 /drivers/usb/gadget | |
parent | 85d0aec0fda21da28ac886b2bf973f0e1c1b8d41 (diff) | |
download | u-boot-7fd9f31c6bd13609da61b985cf8f5f65ebebd913.zip u-boot-7fd9f31c6bd13609da61b985cf8f5f65ebebd913.tar.gz u-boot-7fd9f31c6bd13609da61b985cf8f5f65ebebd913.tar.bz2 |
usb: dwc2: fix gadget disconnect
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@st.com>
Reviewed-by: Marek Vasut <marex@denx.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg_regs.h | 6 | ||||
-rw-r--r-- | 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 b2a28d7..434db5b 100644 --- a/drivers/usb/gadget/dwc2_udc_otg_regs.h +++ b/drivers/usb/gadget/dwc2_udc_otg_regs.h @@ -94,6 +94,9 @@ struct dwc2_usbotg_reg { #define A_VALOVAL BIT(5) #define A_VALOEN BIT(4) +/* 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) @@ -126,6 +129,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) @@ -254,7 +258,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(®->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, ®->gotgint); } if (intr_status & INT_RESUME) { |