spin_lock_irqsave(&ep->udc->lock, flags);
stopped = ep->stopped;
udc = ep->udc;
- if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
- spin_unlock_irqrestore(&ep->udc->lock, flags);
- return -ESHUTDOWN;
- }
/* Stop the ep before we deal with the queue */
ep->stopped = 1;
for (pipe = 0; pipe < udc->max_pipes; pipe++)
udc_reset_ep_queue(udc, pipe);
+ spin_unlock(&udc->lock);
/* report disconnect; the driver is already quiesced */
udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
return 0;
}
udc->usb_state = USB_STATE_DEFAULT;
}
-static void fsl_gadget_event(struct work_struct *work)
+#define FSL_DP_CHANGE_TIMEOUT (msecs_to_jiffies(1000)) /* 1000 ms */
+static void gadget_wait_line_to_se0(void)
+{
+ unsigned long timeout;
+ timeout = jiffies + FSL_DP_CHANGE_TIMEOUT;
+ /* Wait for DP to SE0 */
+ while (!((fsl_readl(&dr_regs->portsc1) &
+ (u32)((1 << 10) | (1 << 11))) == PORTSCX_LINE_STATUS_SE0)) {
+ if (time_after(jiffies, timeout)) {
+ pr_warning(KERN_ERR "wait dp to SE0 timeout, please check"
+ " your hardware design!\n");
+ break;
+ }
+ msleep(10);
+ }
+}
+
+#define FSL_WAIT_CLASS_DRIVER_TIMEOUT (msecs_to_jiffies(3000)) /* 3s */
+static void gadget_wait_class_driver_finish(void)
+{
+ unsigned long timeout;
+ struct fsl_udc *udc = udc_controller;
+ struct fsl_ep *ep;
+ int i = 2;
+ timeout = jiffies + FSL_WAIT_CLASS_DRIVER_TIMEOUT;
+ /* for non-control endpoints */
+ while (i < (int)(udc_controller->max_ep)) {
+ ep = &udc->eps[i++];
+ if (ep->stopped == 0) {
+ if (time_after(timeout, jiffies)) {
+ i = 2;
+ msleep(10);
+ continue;
+ } else {
+ pr_warning(KERN_WARNING "We have waited 3s, but the class driver"
+ " has still not finishes!\n");
+ break;
+ }
+ }
+ }
+}
+
+static void fsl_gadget_disconnect_event(struct work_struct *work)
{
struct fsl_udc *udc = udc_controller;
unsigned long flags;
+ struct fsl_usb2_platform_data *pdata;
u32 tmp;
- if (udc->driver)
- udc->driver->disconnect(&udc->gadget);
+ pdata = udc->pdata;
+
+ /* enable pulldown dp */
+ if (pdata->gadget_discharge_dp)
+ pdata->gadget_discharge_dp(true);
+ /*
+ * Some boards are very slow change line state from J to SE0 for DP,
+ * So, we need to discharge DP, otherwise there is a wakeup interrupt
+ * after we enable the wakeup function.
+ */
+ gadget_wait_line_to_se0();
+
+ /* Disable pulldown dp */
+ if (pdata->gadget_discharge_dp)
+ pdata->gadget_discharge_dp(false);
+
+ /*
+ * Wait class drivers finish, an well-behaviour class driver should
+ * call ep_disable when it is notified to be disconnected.
+ */
+ gadget_wait_class_driver_finish();
+
spin_lock_irqsave(&udc->lock, flags);
- /* update port status */
- fsl_udc_speed_update(udc);
- 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
*/
if (!(tmp & (OTGSC_B_SESSION_VALID_IRQ_EN)))
fsl_writel(tmp | (OTGSC_B_SESSION_VALID_IRQ_EN),
&dr_regs->otgsc);
+ udc->stopped = 1;
+ /* enable wake up */
+ dr_wake_up_enable(udc, true);
+ spin_unlock_irqrestore(&udc->lock, flags);
/* close USB PHY clock */
dr_phy_low_power_mode(udc, true);
/* close dr controller clock */
fsl_writel(tmp &
(~OTGSC_B_SESSION_VALID_IRQ_EN),
&dr_regs->otgsc);
- /*here we need delay 30 ms for avoid exception usb vbus falling interrupt
- Once we clear the RS bit, D+ D- need about 20 ms to SE0 modet ,during this period
- we can not enable device wake up
- */
- schedule_delayed_work(&udc->gadget_delay_work, msecs_to_jiffies(30));
+
+ /* update port status */
+ fsl_udc_speed_update(udc);
+ spin_unlock(&udc->lock);
+ if (udc->driver)
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ schedule_work(&udc->gadget_disconnect_schedule);
return false;
}
}
ep->ep.name = ep->name;
ep->ep.ops = &fsl_ep_ops;
- ep->stopped = 0;
+ /*
+ * For ep0, the endpoint is enabled after controller initialization
+ * For non-ep0, the endpoint is stopped default, and will be enabled
+ * by class driver when needed.
+ */
+ if (index)
+ ep->stopped = 1;
+ else
+ ep->stopped = 0;
/* for ep0: maxP defined in desc
* for other eps, maxP is set by epautoconfig() called by gadget layer
}
}
- INIT_DELAYED_WORK(&udc_controller->gadget_delay_work, fsl_gadget_event);
+ INIT_WORK(&udc_controller->gadget_disconnect_schedule, fsl_gadget_disconnect_event);
#ifdef POSTPONE_FREE_LAST_DTD
last_free_td = NULL;
#endif
fsl_otg_clk_gate(true);
/* Wait for vbus change to B_SESSION_VALID complete */
timeout = jiffies + FSL_VBUS_CHANGE_TIMEOUT;
- while ((le32_to_cpu(usb_dr_regs->otgsc)&OTGSC_INTSTS_B_SESSION_VALID) == !on) {
+ while ((le32_to_cpu(usb_dr_regs->otgsc)&OTGSC_STS_B_SESSION_VALID) != (on << 11)) {
if (time_after(jiffies, timeout)) {
printk(KERN_ERR"wait otg vbus change timeout! \n");
fsl_otg_clk_gate(false);
- break;
+ return;
}
msleep(10);
}
* so suspend the host after a short delay.
*/
otg_dev->host_working = 1;
- if (otg_dev->fsm.id)
+
+ if (otg_dev->fsm.id) {
+ otg_dev->host_first_call = true;
schedule_otg_work(&otg_dev->otg_event, 100);
+ }
else {
/* if the device is already at the port */
otg_drv_vbus(&otg_dev->fsm, 1);
if (fsm->id) { /* switch to gadget */
fsl_otg_start_host(fsm, 0);
otg_drv_vbus(fsm, 0);
- fsl_otg_wait_stable_vbus(false);
- fsl_otg_wait_dischrg_vbus();
+ if (og->host_first_call == false) {
+ fsl_otg_wait_dischrg_vbus();
+ fsl_otg_wait_stable_vbus(false);
+ } else {
+ og->host_first_call = false;
+ }
b_session_irq_enable(false);
fsl_otg_start_gadget(fsm, 1);
} else { /* switch to host */
fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp;
fsl_otg_tc->otg.start_srp = fsl_otg_start_srp;
fsl_otg_tc->otg.dev = &pdev->dev;
+ fsl_otg_tc->host_first_call = false;
fsl_otg_dev = fsl_otg_tc;