Skip to content

Commit

Permalink
usb: dwc3: keep a copy of IDDIG status for DRD mode switch
Browse files Browse the repository at this point in the history
In some conditions of DRD mode switching transition, for example
plugging in or remove the USB cable slowly, the DWC3 controller can
trigger multiple otg interrupts while the ID status is not changed when
reading the OSTS register bit0.

Then dwc3_otg_thread_interrupt() then will try to stop the hcd or udc
multiple times while processing these OTG events, in which the second
time teardown causes kernel crash since the resource is no longer
available.

Following is an exmaple of crash log, while removing the microA-typeA
adaptor cable triggers the following two interrupts and hangs the kernel.

[   40.010169] dwc3 48390000.usb: OTG thread interrupt, OSTS 0x180f
[   40.020738] xhci-hcd xhci-hcd.0.auto: remove, state 4
[   40.026847] usb usb2: USB disconnect, device number 1
[   40.055775] xhci-hcd xhci-hcd.0.auto: USB bus 2 deregistered
[   40.067580] xhci-hcd xhci-hcd.0.auto: remove, state 1
[   40.073122] usb usb1: USB disconnect, device number 1
[   40.080046] usb 1-1: USB disconnect, device number 2
[   40.169225] xhci-hcd xhci-hcd.0.auto: USB bus 1 deregistered
[   40.183931] dwc3 48390000.usb: OTG thread interrupt, OSTS 0x819
[   40.194340] kobject (eb5e4c48): tried to init an initialized object, something is seriously wrong.
[   40.203831] CPU: 0 PID: 1018 Comm: irq/204-dwc3-ot Not tainted 3.14.26-dirty #10
[   40.211653] [<c0015ecc>] (unwind_backtrace) from [<c0012a10>] (show_stack+0x10/0x14)
[   40.219874] [<c0012a10>] (show_stack) from [<c0571244>] (dump_stack+0x78/0x94)
[   40.227556] [<c0571244>] (dump_stack) from [<c02e8e88>] (kobject_init+0x74/0x94)
[   40.235404] [<c02e8e88>] (kobject_init) from [<c036ab18>] (device_initialize+0x20/0xcc)
[   40.243878] [<c036ab18>] (device_initialize) from [<c036bf8c>] (device_register+0xc/0x18)
[   40.252578] [<c036bf8c>] (device_register) from [<c042bcec>] (usb_add_gadget_udc_release+0xbc/0x1ec)
[   40.262276] [<c042bcec>] (usb_add_gadget_udc_release) from [<c042c43c>] (usb_drd_start_udc+0x54/0x9c)
[   40.272056] [<c042c43c>] (usb_drd_start_udc) from [<bf1b3708>] (dwc3_otg_thread_interrupt+0x178/0x198 [dwc3])
[   40.282611] [<bf1b3708>] (dwc3_otg_thread_interrupt [dwc3]) from [<c0095f54>] (irq_thread_fn+0x1c/0x34)
[   40.292584] [<c0095f54>] (irq_thread_fn) from [<c00960e0>] (irq_thread+0x120/0x180)
[   40.300691] [<c00960e0>] (irq_thread) from [<c00650e0>] (kthread+0xc4/0xe0)
[   40.308098] [<c00650e0>] (kthread) from [<c000f068>] (ret_from_fork+0x14/0x2c)

The fix is to add a local copy of iddig in struct dwc3, and dwc3_otg_thread_interrupt()
will not take any action if the iddig is not changed.

Signed-off-by: Bin Liu <[email protected]>
[[email protected]: line over 80 char checkpatch warning fix]
Signed-off-by: Sekhar Nori <[email protected]>
  • Loading branch information
liubiin authored and nsekhar committed Feb 4, 2015
1 parent f53c2b8 commit cc37e7e
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ struct dwc3 {
u32 u1u2;
u32 maximum_speed;
u32 revision;
u32 iddig;

#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
Expand Down
12 changes: 11 additions & 1 deletion drivers/usb/dwc3/otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ static irqreturn_t dwc3_otg_thread_interrupt(int irq, void *_dwc)
struct dwc3 *dwc = _dwc;
u32 reg = dwc3_readl(dwc->regs, DWC3_OSTS);

dev_vdbg(dwc->dev, "OTG thread interrupt\n");
dev_vdbg(dwc->dev, "OTG thread interrupt, OSTS 0x04%x, prev iddig %d\n",
reg, dwc->iddig);

if ((reg & DWC3_OSTS_CONIDSTS) == dwc->iddig)
goto out;
else
dwc->iddig = reg & DWC3_OSTS_CONIDSTS;

if ((reg & DWC3_OSTS_CONIDSTS)) {
usb_drd_stop_hcd(dwc->dev);
dwc3_writel(dwc->regs, DWC3_OCFG, DWC3_OCFG_SFTRSTMASK);
Expand Down Expand Up @@ -81,6 +88,7 @@ static irqreturn_t dwc3_otg_thread_interrupt(int irq, void *_dwc)
DWC3_OEVTEN_CONIDSTSCHNGEN);
}

out:
return IRQ_HANDLED;
}

Expand Down Expand Up @@ -150,6 +158,8 @@ int dwc3_otg_init(struct dwc3 *dwc)
DWC3_OEVTEN_CONIDSTSCHNGEN);
}

dwc->iddig = reg & DWC3_OSTS_CONIDSTS;

return 0;
}

Expand Down

0 comments on commit cc37e7e

Please sign in to comment.