--- zzzz-none-000/linux-4.4.271/drivers/usb/dwc3/gadget.c 2021-06-03 06:22:09.000000000 +0000 +++ hawkeye-5590-750/linux-4.4.271/drivers/usb/dwc3/gadget.c 2023-04-19 10:22:29.000000000 +0000 @@ -35,6 +35,12 @@ #include "gadget.h" #include "io.h" +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc); +static int __dwc3_gadget_start(struct dwc3 *dwc); +static void dwc3_gadget_disable_irq(struct dwc3 *dwc); +static irqreturn_t dwc3_interrupt(int irq, void *_dwc); +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); + /** * dwc3_gadget_set_test_mode - Enables USB2 Test Modes * @dwc: pointer to our context structure @@ -362,6 +368,7 @@ udelay(1); } while (1); } +EXPORT_SYMBOL(dwc3_send_gadget_ep_cmd); static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, struct dwc3_trb *trb) @@ -1513,6 +1520,9 @@ reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + if (dwc->revision <= DWC3_REVISION_187A) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; @@ -1527,6 +1537,10 @@ dwc->pullups_connected = true; } else { + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + reg &= ~DWC3_DCTL_RUN_STOP; if (dwc->has_hibernation && !suspend) @@ -1568,6 +1582,16 @@ is_on = !!is_on; + dwc->softconnect = is_on; + + if ((dwc->is_drd && !dwc->vbus_active) || !dwc->gadget_driver) { + /* + * Need to wait for vbus_session(on) from otg driver or to + * the udc_start. + */ + return 0; + } + spin_lock_irqsave(&dwc->lock, flags); ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1599,39 +1623,55 @@ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); } -static irqreturn_t dwc3_interrupt(int irq, void *_dwc); -static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); - -static int dwc3_gadget_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active) { - struct dwc3 *dwc = gadget_to_dwc(g); - struct dwc3_ep *dep; - unsigned long flags; - int ret = 0; - int irq; - u32 reg; + struct dwc3 *dwc = gadget_to_dwc(_gadget); + unsigned long flags; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, - IRQF_SHARED, "dwc3", dwc); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err0; - } + if (!dwc->is_drd) + return -EPERM; + + is_active = !!is_active; spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->driver.name); - ret = -EBUSY; - goto err1; + /* Mark that the vbus was powered */ + dwc->vbus_active = is_active; + + /* + * Check if upper level usb_gadget_driver was already registered with + * this udc controller driver (if dwc3_gadget_start was called) + */ + if (dwc->gadget_driver && dwc->softconnect) { + if (dwc->vbus_active) { + /* + * Both vbus was activated by otg and pullup was + * signaled by the gadget driver. + */ + dwc3_gadget_run_stop(dwc, 1, false); + } else { + dwc3_gadget_run_stop(dwc, 0, false); + } } - dwc->gadget_driver = driver; + /* + * Clearing run/stop bit might occur before disconnect event is seen. + * Make sure to let gadget driver know in that case. + */ + if (!dwc->vbus_active) { + dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__); + dwc3_gadget_disconnect_interrupt(dwc); + } + + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; +} + +static int __dwc3_gadget_start(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret = 0; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -1678,7 +1718,7 @@ false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err2; + return ret; } dep = dwc->eps[1]; @@ -1686,7 +1726,8 @@ false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err3; + __dwc3_gadget_ep_disable(dwc->eps[0]); + return ret; } /* begin to receive SETUP packets */ @@ -1696,19 +1737,54 @@ dwc3_gadget_enable_irq(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + return ret; +} - return 0; +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret = 0; + int irq; -err3: - __dwc3_gadget_ep_disable(dwc->eps[0]); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + if (dwc->emulation) + ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, "dwc3", + dwc); + else + ret = request_threaded_irq(irq, dwc3_interrupt, + dwc3_thread_interrupt, + IRQF_SHARED, "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err0; + } -err2: - dwc->gadget_driver = NULL; + spin_lock_irqsave(&dwc->lock, flags); -err1: + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err1; + } + + dwc->gadget_driver = driver; + + /* + * For DRD, this might get called by gadget driver during bootup + * even though host mode might be active. Don't actually perform + * device-specific initialization until device mode is activated. + * In that case __dwc3_gadget_start() will handle it. + */ spin_unlock_irqrestore(&dwc->lock, flags); + return 0; +err1: + spin_unlock_irqrestore(&dwc->lock, flags); free_irq(irq, dwc); err0: @@ -1737,13 +1813,24 @@ return 0; } +static int dwc3_gadget_restart_usb_session(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + dev_warn(dwc->dev, "%s not supported\n",__func__); + + return -EINVAL; +} + static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, .wakeup = dwc3_gadget_wakeup, .set_selfpowered = dwc3_gadget_set_selfpowered, + .vbus_session = dwc3_gadget_vbus_session, .pullup = dwc3_gadget_pullup, .udc_start = dwc3_gadget_start, .udc_stop = dwc3_gadget_stop, + .restart = dwc3_gadget_restart_usb_session, }; /* -------------------------------------------------------------------------- */ @@ -2654,6 +2741,12 @@ { trace_dwc3_event(event->raw); + /* skip event processing in absence of vbus */ + if (!dwc->vbus_active) { + dev_dbg(dwc->dev, "SKIP EVT : event->raw: %x\n", event->raw); + return; + } + /* Endpoint IRQ, handle it and return early */ if (event->type.is_devspec == 0) { /* depevt */ @@ -2768,8 +2861,12 @@ irqreturn_t status; status = dwc3_check_event_buf(dwc, i); - if (status == IRQ_WAKE_THREAD) - ret = status; + if (status == IRQ_WAKE_THREAD) { + if (dwc->emulation) + ret = dwc3_thread_interrupt(irq, _dwc); + else + ret = status; + } } return ret;