usb: musb: handle irqs in the order dictated by programming guide
authorArnaud Mandy <ext-arnaud.2.mandy@nokia.com>
Mon, 28 Dec 2009 11:40:40 +0000 (13:40 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 2 Mar 2010 22:53:47 +0000 (14:53 -0800)
MUSB's programming guide dictates how we should handle its
irqs and in which order. Follow that.

Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@nokia.com>
Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/musb/musb_core.c

index c4893267b4e2e6fd67079464ab82a42bac151250..bd14e816df9341903bf455994719578b03a11f78 100644 (file)
@@ -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;
 }