From 2e6a2a5f93f97ecde9be46aab8e8e3d749d05c3f Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 13 Feb 2020 20:44:09 +0530 Subject: [PATCH] usb: dwc3: gadget: Ensure minimum delay between run/stop Some controller hardware, such as DWC3, need a minimum delay between removing and re-applying the pullups in order for the host to properly detect a soft disconnect and for subsequent re-enumeration to succeed. Keep track of the last time run_stop was updated and delay if necessary so that enough time will have elapsed before calling run_stop clear/set again. Change-Id: I8ca13304edf4c9fcc3093a063a6004bd8b76ed44 Signed-off-by: Ajay Agarwal Signed-off-by: Sriharsha Allenki --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e160d3319ea2..88fe72f4612f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1149,6 +1149,7 @@ struct dwc3_scratchpad_array { * @bh_completion_time: time taken for taklet completion * @bh_handled_evt_cnt: no. of events handled by tasklet per interrupt * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt + * @last_run_stop: timestamp denoting the last run_stop update */ struct dwc3 { struct work_struct drd_work; @@ -1393,6 +1394,7 @@ struct dwc3 { u32 gen2_tx_de_emph1; u32 gen2_tx_de_emph2; u32 gen2_tx_de_emph3; + ktime_t last_run_stop; }; #define INCRX_BURST_MODE 0 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c9e4bd51d802..cf971482188e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2047,6 +2047,8 @@ done: return 0; } +#define MIN_RUN_STOP_DELAY_MS 50 + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg, reg1; @@ -2156,6 +2158,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; int ret; + ktime_t diff; is_on = !!is_on; dwc->softconnect = is_on; @@ -2174,6 +2177,15 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) dbg_event(0xFF, "Pullup gsync", atomic_read(&dwc->dev->power.usage_count)); + diff = ktime_sub(ktime_get(), dwc->last_run_stop); + if (ktime_to_ms(diff) < MIN_RUN_STOP_DELAY_MS) { + dbg_event(0xFF, "waitBefRun_Stop", + MIN_RUN_STOP_DELAY_MS - ktime_to_ms(diff)); + msleep(MIN_RUN_STOP_DELAY_MS - ktime_to_ms(diff)); + } + + dwc->last_run_stop = ktime_get(); + /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase.