diff options
author | Tony Liu <b08287@freescale.com> | 2011-08-24 12:56:24 +0800 |
---|---|---|
committer | Tony Liu <b08287@freescale.com> | 2011-08-31 09:17:20 +0800 |
commit | 6ec1029b83d635e7eb3c100112b43b336dd6001d (patch) | |
tree | add8b895e7dcc88d4a1875b107f9e0265d628622 | |
parent | 10a3a3e8efd31483c62a9b4c29034ddba4e89cbd (diff) |
ENGR00155268 usb-gadget: fix potential risk between rapid usb cable plug in/out
- When usb cable is plugged out, the B_SESSION_VALID interrupt is disabled to
avoid usb disconection work queue interrupting. After usb disconection work
queue task is finished, re-enable it to generate interrupt.
Signed-off-by: Tony Liu <b08287@freescale.com>
-rw-r--r-- | drivers/usb/gadget/arcotg_udc.c | 21 |
1 files changed, 20 insertions, 1 deletions
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c index 7ac9a9d6ac63..0f5db69964aa 100644 --- a/drivers/usb/gadget/arcotg_udc.c +++ b/drivers/usb/gadget/arcotg_udc.c @@ -2088,17 +2088,25 @@ static void fsl_gadget_event(struct work_struct *work) { struct fsl_udc *udc = udc_controller; unsigned long flags; + u32 tmp; if (udc->driver) udc->driver->disconnect(&udc->gadget); spin_lock_irqsave(&udc->lock, flags); /* update port status */ fsl_udc_speed_update(udc); + udc->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); - udc->stopped = 1; /* enable wake up */ dr_wake_up_enable(udc, true); + /* here we need to enable the B_SESSION_IRQ + * to enable the following device attach + */ + tmp = fsl_readl(&dr_regs->otgsc); + if (!(tmp & (OTGSC_B_SESSION_VALID_IRQ_EN))) + fsl_writel(tmp | (OTGSC_B_SESSION_VALID_IRQ_EN), + &dr_regs->otgsc); /* close USB PHY clock */ dr_phy_low_power_mode(udc, true); /* close dr controller clock */ @@ -2137,6 +2145,17 @@ bool try_wake_up_udc(struct fsl_udc *udc) printk(KERN_DEBUG "%s: udc out low power mode\n", __func__); } else { fsl_writel(tmp & ~USB_CMD_RUN_STOP, &dr_regs->usbcmd); + /* here we need disable B_SESSION_IRQ, after + * schedule_work finished, it need to be enabled again. + * Doing like this can avoid conflicting between rapid + * plug in/out. + */ + tmp = fsl_readl(&dr_regs->otgsc); + if (tmp & (OTGSC_B_SESSION_VALID_IRQ_EN)) + fsl_writel(tmp & + (~OTGSC_B_SESSION_VALID_IRQ_EN), + &dr_regs->otgsc); + schedule_work(&udc->gadget_work); return false; } |