Skip to content

Commit 538b77b

Browse files
committed
usb: dwc2: add a watchdog timer for a reset on the USB
When I do continuously and quickly hotplug test on RK3308 EVB OTG interface which is configured as uac1 function, I find that the USB Host does not see that the device is connected occasionally. In this case, it seems that the dwc2 controller run into soft disconnect state, although the SftDiscon bit of DCTL register is 0. We can't find the root cause for the time being, fortunately, the dwc2 controller can detect the session detected interrupt, so we can workaroud this issue depend on this interrupt. This patch adds a watchdog timer. When the dwc2 controller detects the session detected interrupt for device mode, start the watchdog timer to wait for a reset on USB. If wait for reset timeout, do soft disconnect and soft connect in watchdog timer function, and try to generate a device connect event to the USB Host. Change-Id: I9bd965b75ab3903d300302cf356cc1a843ad6e64 Signed-off-by: William Wu <william.wu@rock-chips.com>
1 parent 88a3518 commit 538b77b

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

drivers/usb/dwc2/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ struct dwc2_hregs_backup {
747747
* @wf_otg: Work object for handling Connector ID Status Change
748748
* interrupt
749749
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
750+
* @rst_complete_timer Timer object for a reset is detected on the USB
750751
* @lx_state: Lx state of connected device
751752
* @gregs_backup: Backup of global registers during suspend
752753
* @dregs_backup: Backup of device registers during suspend
@@ -887,6 +888,7 @@ struct dwc2_hsotg {
887888
struct workqueue_struct *wq_otg;
888889
struct work_struct wf_otg;
889890
struct timer_list wkp_timer;
891+
struct timer_list rst_complete_timer;
890892
enum dwc2_lx_state lx_state;
891893
struct dwc2_gregs_backup gr_backup;
892894
struct dwc2_dregs_backup dr_backup;
@@ -999,9 +1001,11 @@ struct dwc2_hsotg {
9991001
enum dwc2_ep0_state ep0_state;
10001002
u8 test_mode;
10011003

1004+
#define DWC2_WAIT_RESET_TIMEOUT 3000 /* milliseconds */
10021005
struct usb_gadget gadget;
10031006
unsigned int enabled:1;
10041007
unsigned int connected:1;
1008+
unsigned int rst_completed:1;
10051009
struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
10061010
struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
10071011
u32 g_using_dma;

drivers/usb/dwc2/core_intr.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
311311
*/
312312
static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
313313
{
314+
u32 dctl;
314315
int ret;
315316

316317
/* Clear interrupt */
@@ -332,6 +333,13 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
332333
* established
333334
*/
334335
dwc2_hsotg_disconnect(hsotg);
336+
337+
hsotg->rst_completed = 0;
338+
339+
dctl = dwc2_readl(hsotg->regs + DCTL);
340+
if (!(dctl & DCTL_SFTDISCON))
341+
mod_timer(&hsotg->rst_complete_timer, jiffies +
342+
msecs_to_jiffies(DWC2_WAIT_RESET_TIMEOUT));
335343
}
336344
}
337345

drivers/usb/dwc2/gadget.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,8 +2464,11 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
24642464
if (!hsotg->connected)
24652465
return;
24662466

2467+
del_timer(&hsotg->rst_complete_timer);
2468+
24672469
hsotg->connected = 0;
24682470
hsotg->test_mode = 0;
2471+
hsotg->rst_completed = 0;
24692472

24702473
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
24712474
if (hsotg->eps_in[ep])
@@ -2807,6 +2810,10 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
28072810
u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL);
28082811
u32 connected = hsotg->connected;
28092812

2813+
hsotg->rst_completed = 1;
2814+
/* Can't del_timer_sync in interrupt */
2815+
del_timer(&hsotg->rst_complete_timer);
2816+
28102817
dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
28112818
dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
28122819
dwc2_readl(hsotg->regs + GNPTXSTS));
@@ -3805,6 +3812,45 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
38053812
return 0;
38063813
}
38073814

3815+
/**
3816+
* dwc2_wait_reset_watchdog - Watchdog timer function for a reset on the USB
3817+
* @param: The device state
3818+
*
3819+
* Watchdog timer function for when the dwc2 controller fails to detect
3820+
* a reset on USB bus. In this case, we assume the dwc2 controller is broken,
3821+
* the host does not see that the device is connected, and the device does
3822+
* not receive reset signal on the USB. So we have to do soft disconnect
3823+
* and soft connect, and try to generate a device connect event to the USB
3824+
* host.
3825+
*/
3826+
static void dwc2_wait_reset_watchdog(unsigned long data)
3827+
{
3828+
struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data;
3829+
unsigned long flags;
3830+
u32 dctl;
3831+
3832+
spin_lock_irqsave(&hsotg->lock, flags);
3833+
3834+
if (!hsotg->rst_completed) {
3835+
dctl = dwc2_readl(hsotg->regs + DCTL);
3836+
3837+
/*
3838+
* According to the dwc2 controller databook,
3839+
* Table 5-55 lists the minimum duration under various
3840+
* conditions for which the Soft Disconnect bit must be
3841+
* set for the USB host to detect a device disconnect.
3842+
* We set minimum duration to 3 microseconds.
3843+
*/
3844+
if (!(dctl & DCTL_SFTDISCON)) {
3845+
dwc2_hsotg_core_disconnect(hsotg);
3846+
udelay(3);
3847+
dwc2_hsotg_core_connect(hsotg);
3848+
}
3849+
}
3850+
3851+
spin_unlock_irqrestore(&hsotg->lock, flags);
3852+
}
3853+
38083854
/**
38093855
* dwc2_hsotg_dump - dump state of the udc
38103856
* @param: The device state
@@ -4002,6 +4048,9 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
40024048
epnum, 0);
40034049
}
40044050

4051+
setup_timer(&hsotg->rst_complete_timer, dwc2_wait_reset_watchdog,
4052+
(unsigned long)hsotg);
4053+
40054054
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
40064055
if (ret) {
40074056
dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep,
@@ -4021,6 +4070,7 @@ int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg)
40214070
{
40224071
usb_del_gadget_udc(&hsotg->gadget);
40234072
dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req);
4073+
del_timer_sync(&hsotg->rst_complete_timer);
40244074

40254075
return 0;
40264076
}

0 commit comments

Comments
 (0)