From cc37e7e127678adba31ce522d2d8f1febcf68cf7 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Thu, 15 Jan 2015 15:00:05 -0600 Subject: [PATCH] usb: dwc3: keep a copy of IDDIG status for DRD mode switch 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] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 40.219874] [] (show_stack) from [] (dump_stack+0x78/0x94) [ 40.227556] [] (dump_stack) from [] (kobject_init+0x74/0x94) [ 40.235404] [] (kobject_init) from [] (device_initialize+0x20/0xcc) [ 40.243878] [] (device_initialize) from [] (device_register+0xc/0x18) [ 40.252578] [] (device_register) from [] (usb_add_gadget_udc_release+0xbc/0x1ec) [ 40.262276] [] (usb_add_gadget_udc_release) from [] (usb_drd_start_udc+0x54/0x9c) [ 40.272056] [] (usb_drd_start_udc) from [] (dwc3_otg_thread_interrupt+0x178/0x198 [dwc3]) [ 40.282611] [] (dwc3_otg_thread_interrupt [dwc3]) from [] (irq_thread_fn+0x1c/0x34) [ 40.292584] [] (irq_thread_fn) from [] (irq_thread+0x120/0x180) [ 40.300691] [] (irq_thread) from [] (kthread+0xc4/0xe0) [ 40.308098] [] (kthread) from [] (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 [nsekhar@ti.com: line over 80 char checkpatch warning fix] Signed-off-by: Sekhar Nori --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/otg.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index bb73aba174639..9bc15d1c09904 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -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 diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c index 542f7ff0c07b7..9434562da78ab 100644 --- a/drivers/usb/dwc3/otg.c +++ b/drivers/usb/dwc3/otg.c @@ -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); @@ -81,6 +88,7 @@ static irqreturn_t dwc3_otg_thread_interrupt(int irq, void *_dwc) DWC3_OEVTEN_CONIDSTSCHNGEN); } +out: return IRQ_HANDLED; } @@ -150,6 +158,8 @@ int dwc3_otg_init(struct dwc3 *dwc) DWC3_OEVTEN_CONIDSTSCHNGEN); } + dwc->iddig = reg & DWC3_OSTS_CONIDSTS; + return 0; }