usb: dwc2: fix gadget disconnect
authorFabrice Gasnier <fabrice.gasnier@st.com>
Wed, 17 Apr 2019 14:46:13 +0000 (16:46 +0200)
committerMarek Vasut <marex@denx.de>
Sun, 21 Apr 2019 08:26:52 +0000 (10:26 +0200)
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>
drivers/usb/gadget/dwc2_udc_otg_regs.h
drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c

index b2a28d7a5dd6af6464362718dd6ad107672fb3b4..434db5ba39ad4b1d12b6ebf8776fcdd7fb7f69fa 100644 (file)
@@ -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\
index a75af4987f8f91d1fc1a928f4159068b8850a68b..7eb632d3b1417ba76e8a45d72df9bf21a2d5b243 100644 (file)
@@ -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) {