|
@@ -557,6 +557,69 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
|
|
handled = IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ if (int_usb & MUSB_INTR_SUSPEND) {
|
|
|
+ DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
|
|
|
+ otg_state_string(musb), devctl, power);
|
|
|
+ handled = IRQ_HANDLED;
|
|
|
+
|
|
|
+ switch (musb->xceiv->state) {
|
|
|
+#ifdef CONFIG_USB_MUSB_OTG
|
|
|
+ case OTG_STATE_A_PERIPHERAL:
|
|
|
+ /* We also come here if the cable is removed, since
|
|
|
+ * this silicon doesn't report ID-no-longer-grounded.
|
|
|
+ *
|
|
|
+ * We depend on T(a_wait_bcon) to shut us down, and
|
|
|
+ * hope users don't do anything dicey during this
|
|
|
+ * undesired detour through A_WAIT_BCON.
|
|
|
+ */
|
|
|
+ musb_hnp_stop(musb);
|
|
|
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
|
|
+ musb_root_disconnect(musb);
|
|
|
+ musb_platform_try_idle(musb, jiffies
|
|
|
+ + msecs_to_jiffies(musb->a_wait_bcon
|
|
|
+ ? : OTG_TIME_A_WAIT_BCON));
|
|
|
+
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ case OTG_STATE_B_IDLE:
|
|
|
+ if (!musb->is_active)
|
|
|
+ break;
|
|
|
+ case OTG_STATE_B_PERIPHERAL:
|
|
|
+ musb_g_suspend(musb);
|
|
|
+ musb->is_active = is_otg_enabled(musb)
|
|
|
+ && musb->xceiv->gadget->b_hnp_enable;
|
|
|
+ if (musb->is_active) {
|
|
|
+#ifdef CONFIG_USB_MUSB_OTG
|
|
|
+ musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
|
|
|
+ DBG(1, "HNP: Setting timer for b_ase0_brst\n");
|
|
|
+ mod_timer(&musb->otg_timer, jiffies
|
|
|
+ + msecs_to_jiffies(
|
|
|
+ OTG_TIME_B_ASE0_BRST));
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case OTG_STATE_A_WAIT_BCON:
|
|
|
+ if (musb->a_wait_bcon != 0)
|
|
|
+ musb_platform_try_idle(musb, jiffies
|
|
|
+ + msecs_to_jiffies(musb->a_wait_bcon));
|
|
|
+ break;
|
|
|
+ case OTG_STATE_A_HOST:
|
|
|
+ musb->xceiv->state = OTG_STATE_A_SUSPEND;
|
|
|
+ musb->is_active = is_otg_enabled(musb)
|
|
|
+ && musb->xceiv->host->b_hnp_enable;
|
|
|
+ break;
|
|
|
+ case OTG_STATE_B_HOST:
|
|
|
+ /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
|
|
+ DBG(1, "REVISIT: SUSPEND as B_HOST\n");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* "should not happen" */
|
|
|
+ musb->is_active = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (int_usb & MUSB_INTR_CONNECT) {
|
|
|
struct usb_hcd *hcd = musb_to_hcd(musb);
|
|
|
|
|
@@ -625,10 +688,61 @@ b_host:
|
|
|
}
|
|
|
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
|
|
|
|
|
|
+ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
|
|
|
+ DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
|
|
|
+ otg_state_string(musb),
|
|
|
+ MUSB_MODE(musb), devctl);
|
|
|
+ handled = IRQ_HANDLED;
|
|
|
+
|
|
|
+ switch (musb->xceiv->state) {
|
|
|
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
|
|
+ case OTG_STATE_A_HOST:
|
|
|
+ case OTG_STATE_A_SUSPEND:
|
|
|
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
|
|
+ musb_root_disconnect(musb);
|
|
|
+ if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
|
|
|
+ musb_platform_try_idle(musb, jiffies
|
|
|
+ + msecs_to_jiffies(musb->a_wait_bcon));
|
|
|
+ break;
|
|
|
+#endif /* HOST */
|
|
|
+#ifdef CONFIG_USB_MUSB_OTG
|
|
|
+ case OTG_STATE_B_HOST:
|
|
|
+ /* REVISIT this behaves for "real disconnect"
|
|
|
+ * cases; make sure the other transitions from
|
|
|
+ * from B_HOST act right too. The B_HOST code
|
|
|
+ * in hnp_stop() is currently not used...
|
|
|
+ */
|
|
|
+ musb_root_disconnect(musb);
|
|
|
+ musb_to_hcd(musb)->self.is_b_host = 0;
|
|
|
+ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
|
|
+ MUSB_DEV_MODE(musb);
|
|
|
+ musb_g_disconnect(musb);
|
|
|
+ break;
|
|
|
+ case OTG_STATE_A_PERIPHERAL:
|
|
|
+ musb_hnp_stop(musb);
|
|
|
+ musb_root_disconnect(musb);
|
|
|
+ /* FALLTHROUGH */
|
|
|
+ case OTG_STATE_B_WAIT_ACON:
|
|
|
+ /* FALLTHROUGH */
|
|
|
+#endif /* OTG */
|
|
|
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
|
|
+ case OTG_STATE_B_PERIPHERAL:
|
|
|
+ case OTG_STATE_B_IDLE:
|
|
|
+ musb_g_disconnect(musb);
|
|
|
+ break;
|
|
|
+#endif /* GADGET */
|
|
|
+ default:
|
|
|
+ WARNING("unhandled DISCONNECT transition (%s)\n",
|
|
|
+ otg_state_string(musb));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* mentor saves a bit: bus reset and babble share the same irq.
|
|
|
* only host sees babble; only peripheral sees bus reset.
|
|
|
*/
|
|
|
if (int_usb & MUSB_INTR_RESET) {
|
|
|
+ handled = IRQ_HANDLED;
|
|
|
if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
|
|
|
/*
|
|
|
* Looks like non-HS BABBLE can be ignored, but
|
|
@@ -641,7 +755,7 @@ b_host:
|
|
|
DBG(1, "BABBLE devctl: %02x\n", devctl);
|
|
|
else {
|
|
|
ERR("Stopping host session -- babble\n");
|
|
|
- musb_writeb(mbase, MUSB_DEVCTL, 0);
|
|
|
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
|
|
}
|
|
|
} else if (is_peripheral_capable()) {
|
|
|
DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
|
|
@@ -686,29 +800,7 @@ b_host:
|
|
|
otg_state_string(musb));
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- handled = IRQ_HANDLED;
|
|
|
}
|
|
|
- schedule_work(&musb->irq_work);
|
|
|
-
|
|
|
- return handled;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Interrupt Service Routine to record USB "global" interrupts.
|
|
|
- * Since these do not happen often and signify things of
|
|
|
- * paramount importance, it seems OK to check them individually;
|
|
|
- * the order of the tests is specified in the manual
|
|
|
- *
|
|
|
- * @param musb instance pointer
|
|
|
- * @param int_usb register contents
|
|
|
- * @param devctl
|
|
|
- * @param power
|
|
|
- */
|
|
|
-static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
|
|
- u8 devctl, u8 power)
|
|
|
-{
|
|
|
- irqreturn_t handled = IRQ_NONE;
|
|
|
|
|
|
#if 0
|
|
|
/* REVISIT ... this would be for multiplexing periodic endpoints, or
|
|
@@ -755,117 +847,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
|
|
|
- DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
|
|
|
- otg_state_string(musb),
|
|
|
- MUSB_MODE(musb), devctl);
|
|
|
- handled = IRQ_HANDLED;
|
|
|
-
|
|
|
- switch (musb->xceiv->state) {
|
|
|
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
|
|
- case OTG_STATE_A_HOST:
|
|
|
- case OTG_STATE_A_SUSPEND:
|
|
|
- usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
|
|
- musb_root_disconnect(musb);
|
|
|
- if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
|
|
|
- musb_platform_try_idle(musb, jiffies
|
|
|
- + msecs_to_jiffies(musb->a_wait_bcon));
|
|
|
- break;
|
|
|
-#endif /* HOST */
|
|
|
-#ifdef CONFIG_USB_MUSB_OTG
|
|
|
- case OTG_STATE_B_HOST:
|
|
|
- /* REVISIT this behaves for "real disconnect"
|
|
|
- * cases; make sure the other transitions from
|
|
|
- * from B_HOST act right too. The B_HOST code
|
|
|
- * in hnp_stop() is currently not used...
|
|
|
- */
|
|
|
- musb_root_disconnect(musb);
|
|
|
- musb_to_hcd(musb)->self.is_b_host = 0;
|
|
|
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
|
|
- MUSB_DEV_MODE(musb);
|
|
|
- musb_g_disconnect(musb);
|
|
|
- break;
|
|
|
- case OTG_STATE_A_PERIPHERAL:
|
|
|
- musb_hnp_stop(musb);
|
|
|
- musb_root_disconnect(musb);
|
|
|
- /* FALLTHROUGH */
|
|
|
- case OTG_STATE_B_WAIT_ACON:
|
|
|
- /* FALLTHROUGH */
|
|
|
-#endif /* OTG */
|
|
|
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
|
|
- case OTG_STATE_B_PERIPHERAL:
|
|
|
- case OTG_STATE_B_IDLE:
|
|
|
- musb_g_disconnect(musb);
|
|
|
- break;
|
|
|
-#endif /* GADGET */
|
|
|
- default:
|
|
|
- WARNING("unhandled DISCONNECT transition (%s)\n",
|
|
|
- otg_state_string(musb));
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- schedule_work(&musb->irq_work);
|
|
|
- }
|
|
|
-
|
|
|
- if (int_usb & MUSB_INTR_SUSPEND) {
|
|
|
- DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
|
|
|
- otg_state_string(musb), devctl, power);
|
|
|
- handled = IRQ_HANDLED;
|
|
|
-
|
|
|
- switch (musb->xceiv->state) {
|
|
|
-#ifdef CONFIG_USB_MUSB_OTG
|
|
|
- case OTG_STATE_A_PERIPHERAL:
|
|
|
- /* We also come here if the cable is removed, since
|
|
|
- * this silicon doesn't report ID-no-longer-grounded.
|
|
|
- *
|
|
|
- * We depend on T(a_wait_bcon) to shut us down, and
|
|
|
- * hope users don't do anything dicey during this
|
|
|
- * undesired detour through A_WAIT_BCON.
|
|
|
- */
|
|
|
- musb_hnp_stop(musb);
|
|
|
- usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
|
|
- musb_root_disconnect(musb);
|
|
|
- musb_platform_try_idle(musb, jiffies
|
|
|
- + msecs_to_jiffies(musb->a_wait_bcon
|
|
|
- ? : OTG_TIME_A_WAIT_BCON));
|
|
|
- break;
|
|
|
-#endif
|
|
|
- case OTG_STATE_B_PERIPHERAL:
|
|
|
- musb_g_suspend(musb);
|
|
|
- musb->is_active = is_otg_enabled(musb)
|
|
|
- && musb->xceiv->gadget->b_hnp_enable;
|
|
|
- if (musb->is_active) {
|
|
|
-#ifdef CONFIG_USB_MUSB_OTG
|
|
|
- musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
|
|
|
- DBG(1, "HNP: Setting timer for b_ase0_brst\n");
|
|
|
- mod_timer(&musb->otg_timer, jiffies
|
|
|
- + msecs_to_jiffies(
|
|
|
- OTG_TIME_B_ASE0_BRST));
|
|
|
-#endif
|
|
|
- }
|
|
|
- break;
|
|
|
- case OTG_STATE_A_WAIT_BCON:
|
|
|
- if (musb->a_wait_bcon != 0)
|
|
|
- musb_platform_try_idle(musb, jiffies
|
|
|
- + msecs_to_jiffies(musb->a_wait_bcon));
|
|
|
- break;
|
|
|
- case OTG_STATE_A_HOST:
|
|
|
- musb->xceiv->state = OTG_STATE_A_SUSPEND;
|
|
|
- musb->is_active = is_otg_enabled(musb)
|
|
|
- && musb->xceiv->host->b_hnp_enable;
|
|
|
- break;
|
|
|
- case OTG_STATE_B_HOST:
|
|
|
- /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
|
|
- DBG(1, "REVISIT: SUSPEND as B_HOST\n");
|
|
|
- break;
|
|
|
- default:
|
|
|
- /* "should not happen" */
|
|
|
- musb->is_active = 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- schedule_work(&musb->irq_work);
|
|
|
- }
|
|
|
-
|
|
|
+ schedule_work(&musb->irq_work);
|
|
|
|
|
|
return handled;
|
|
|
}
|
|
@@ -1597,11 +1579,6 @@ irqreturn_t musb_interrupt(struct musb *musb)
|
|
|
ep_num++;
|
|
|
}
|
|
|
|
|
|
- /* finish handling "global" interrupts after handling fifos */
|
|
|
- if (musb->int_usb)
|
|
|
- retval |= musb_stage2_irq(musb,
|
|
|
- musb->int_usb, devctl, power);
|
|
|
-
|
|
|
return retval;
|
|
|
}
|
|
|
|