]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
musb_hdrc: small fixes, mostly suspend/resume
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 9 Nov 2006 20:19:31 +0000 (22:19 +0200)
committerTony Lindgren <tony@atomide.com>
Thu, 9 Nov 2006 20:19:31 +0000 (22:19 +0200)
A collection of mostly orthogonal and small fixes:

 - Update various corner cases in the side suspend/resume code,
   triggered mostly on the host side:
     * avoid spurious gadget driver resume callbacks in B_IDLE state
     * avoid spurious gadget driver suspend callbacks in A_HOST state
     * cope better with some spurious resume irqs
     * partial fix for disconnection issues in A_SUSPEND

 - Resolve some TUSB rmmod races by disabling IRQs at the proper moment,
   and killing the idle timer.

 - Only mark TUSB device as wakeup-capable if it's connected to an
   IRQ which allows that.

 - Don't idle the TUSB chip until khubd handles all pending port
   status change events; dump that status in procfs.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_procfs.c
drivers/usb/musb/plat_uds.c
drivers/usb/musb/tusb6010.c
drivers/usb/musb/virthub.c

index 0bdcf19a4c9d9ec2aab5c76e89f1340bc88e0213..7ff55ddae2bef9092140af5ace74df39754748b8 100644 (file)
@@ -1903,11 +1903,21 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
 
 void musb_g_resume(struct musb *pThis)
 {
-       DBG(4, "<==\n");
-       if (pThis->pGadgetDriver && pThis->pGadgetDriver->resume) {
-               spin_unlock(&pThis->Lock);
-               pThis->pGadgetDriver->resume(&pThis->g);
-               spin_lock(&pThis->Lock);
+       switch (pThis->xceiv.state) {
+       case OTG_STATE_B_IDLE:
+               break;
+       case OTG_STATE_B_WAIT_ACON:
+       case OTG_STATE_B_PERIPHERAL:
+               pThis->is_active = 1;
+               if (pThis->pGadgetDriver && pThis->pGadgetDriver->resume) {
+                       spin_unlock(&pThis->Lock);
+                       pThis->pGadgetDriver->resume(&pThis->g);
+                       spin_lock(&pThis->Lock);
+               }
+               break;
+       default:
+               WARN("unhandled RESUME transition (%s)\n",
+                               otg_state_string(pThis));
        }
 }
 
index 9b8994068bc67f80f79626d61cbf5f98e97cfbb1..07133c2ccf40eed62d4ecfb012a7bb72f0970c5c 100644 (file)
@@ -534,6 +534,15 @@ static int dump_header_stats(struct musb *pThis, char *buffer)
        count += code;
        buffer += code;
 
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+       code = sprintf(buffer, "Root port status: %08x\n",
+                       pThis->port1_status);
+       if (code <= 0)
+               goto done;
+       buffer += code;
+       count += code;
+#endif
+
 #ifdef CONFIG_ARCH_DAVINCI
        code = sprintf(buffer,
                        "DaVinci: ctrl=%02x stat=%1x phy=%03x\n"
index f0a01b112fe7f337aa7d105936756bb083ceab3d..dd471d059d7c4dc9d7056489e686fc221a59538f 100644 (file)
@@ -316,45 +316,76 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
        DBG(3, "<== Power=%02x, DevCtl=%02x, bIntrUSB=0x%x\n", power, devctl,
                bIntrUSB);
 
-       /* in host mode when a device resume me (from power save)
-        * in device mode when the host resume me; it shold not change
-        * "identity".
+       /* in host mode, the peripheral may issue remote wakeup.
+        * in peripheral mode, the host may resume the link.
+        * spurious RESUME irqs happen too, paired with SUSPEND.
         */
        if (bIntrUSB & MGC_M_INTR_RESUME) {
                handled = IRQ_HANDLED;
-               DBG(3, "RESUME\n");
-               pThis->is_active = 1;
+               DBG(3, "RESUME (%s)\n", otg_state_string(pThis));
 
                if (devctl & MGC_M_DEVCTL_HM) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
-                       power &= ~MGC_M_POWER_SUSPENDM;
-                       musb_writeb(pBase, MGC_O_HDRC_POWER,
-                               power | MGC_M_POWER_RESUME);
-
-                       /* later, GetPortStatus will stop RESUME signaling */
-                       pThis->port1_status |= MUSB_PORT_STAT_RESUME;
-                       pThis->rh_timer = jiffies + msecs_to_jiffies(20);
-
-                       /* should now be A_SUSPEND */
                        switch (pThis->xceiv.state) {
                        case OTG_STATE_A_SUSPEND:
-                               pThis->xceiv.state = OTG_STATE_A_HOST;
-                               usb_hcd_resume_root_hub(musb_to_hcd(pThis));
+                               /* remote wakeup?  later, GetPortStatus
+                                * will stop RESUME signaling
+                                */
+                               if (power & MGC_M_POWER_RESUME) {
+                                       power &= ~MGC_M_POWER_SUSPENDM;
+                                       musb_writeb(pBase, MGC_O_HDRC_POWER,
+                                               power | MGC_M_POWER_RESUME);
+
+                                       pThis->port1_status |=
+                                                 MUSB_PORT_STAT_RESUME
+                                               | USB_PORT_STAT_C_SUSPEND;
+                                       pThis->rh_timer = jiffies
+                                               + msecs_to_jiffies(20);
+
+                                       pThis->xceiv.state = OTG_STATE_A_HOST;
+                                       pThis->is_active = 1;
+                                       usb_hcd_resume_root_hub(
+                                                       musb_to_hcd(pThis));
+
+                               } else if (power & MGC_M_POWER_SUSPENDM) {
+                                       /* spurious */
+                                       pThis->int_usb &= ~MGC_M_INTR_SUSPEND;
+                               }
                                break;
                        case OTG_STATE_B_WAIT_ACON:
                                pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               pThis->is_active = 1;
                                MUSB_DEV_MODE(pThis);
                                break;
                        default:
-                               WARN("bogus RESUME, from  %s\n",
+                               WARN("bogus %s RESUME (%s)\n",
+                                       "host",
                                        otg_state_string(pThis));
                        }
 #endif
                } else {
+                       switch (pThis->xceiv.state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+                       case OTG_STATE_A_SUSPEND:
+                               /* possibly DISCONNECT is upcoming */
+                               pThis->xceiv.state = OTG_STATE_A_HOST;
+                               usb_hcd_resume_root_hub(musb_to_hcd(pThis));
+                               break;
+#endif
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
-                       MUSB_DEV_MODE(pThis);   /* unnecessary */
+                       case OTG_STATE_B_WAIT_ACON:
+                       case OTG_STATE_B_PERIPHERAL:
+                               musb_g_resume(pThis);
+                               break;
+                       case OTG_STATE_B_IDLE:
+                               pThis->int_usb &= ~MGC_M_INTR_SUSPEND;
+                               break;
 #endif
-                       musb_g_resume(pThis);
+                       default:
+                               WARN("bogus %s RESUME (%s)\n",
+                                       "peripheral",
+                                       otg_state_string(pThis));
+                       }
                }
        }
 
@@ -552,18 +583,33 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
        }
 
        if (bIntrUSB & MGC_M_INTR_SUSPEND) {
-               DBG(1, "SUSPEND, devctl %02x\n", devctl);
+               DBG(1, "SUSPEND (%s) devctl %02x\n",
+                               otg_state_string(pThis), devctl);
                handled = IRQ_HANDLED;
 
-               /* peripheral suspend, may trigger HNP */
-               if (!(devctl & MGC_M_DEVCTL_HM)) {
+               switch (pThis->xceiv.state) {
+               case OTG_STATE_B_PERIPHERAL:
                        musb_g_suspend(pThis);
                        pThis->is_active = is_otg_enabled(pThis)
                                        && pThis->xceiv.gadget->b_hnp_enable;
-               } else
+                       if (pThis->is_active) {
+                               pThis->xceiv.state = OTG_STATE_B_WAIT_ACON;
+                               /* REVISIT timeout for b_ase0_brst, etc */
+                       }
+                       break;
+               case OTG_STATE_A_HOST:
+                       pThis->xceiv.state = OTG_STATE_A_SUSPEND;
+                       pThis->is_active = is_otg_enabled(pThis)
+                                       && pThis->xceiv.host->b_hnp_enable;
+                       break;
+               default:
+                       /* "should not happen" */
                        pThis->is_active = 0;
+                       break;
+               }
        }
 
+
        return handled;
 }
 
@@ -650,11 +696,11 @@ static void musb_generic_disable(struct musb *pThis)
  * with controller locked, irqs blocked
  * acts as a NOP unless some role activated the hardware
  */
-void musb_stop(struct musb * pThis)
+void musb_stop(struct musb *musb)
 {
        /* stop IRQs, timers, ... */
-       musb_platform_disable(pThis);
-       musb_generic_disable(pThis);
+       musb_platform_disable(musb);
+       musb_generic_disable(musb);
        DBG(3, "HDRC disabled\n");
 
        /* FIXME
@@ -664,14 +710,7 @@ void musb_stop(struct musb * pThis)
         *    OTG mode, gadget driver module rmmod/modprobe cycles that
         *  - ...
         */
-
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-       if (is_host_enabled(pThis)) {
-               /* REVISIT aren't there some paths where this is wrong?  */
-               dev_warn(pThis->controller, "%s, root hub still active\n",
-                               __FUNCTION__);
-       }
-#endif
+       musb_platform_try_idle(musb);
 }
 
 static void musb_shutdown(struct platform_device *pdev)
@@ -1651,9 +1690,9 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
                status = -ENODEV;
                goto fail2;
        }
-       (void) enable_irq_wake(nIrq);
        pThis->nIrq = nIrq;
-       device_init_wakeup(dev, 1);
+       if (enable_irq_wake(nIrq) == 0)
+               device_init_wakeup(dev, 1);
 
        pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n",
                        musb_driver_name,
index 2eee49bc74ca906e0524bec8be291a56600dcd96..0e3b444edd679edd850daf53c6adf4d43f534a2d 100644 (file)
@@ -277,6 +277,12 @@ static void musb_do_idle(unsigned long _musb)
        if (!musb->is_active) {
                u32     wakeups;
 
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+               /* wait until khubd handles port change status */
+               if (is_host_active(musb) && (musb->port1_status >> 16))
+                       goto done;
+#endif
+
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
                if (is_peripheral_enabled(musb) && !musb->pGadgetDriver)
                        wakeups = 0;
@@ -292,6 +298,7 @@ static void musb_do_idle(unsigned long _musb)
 #endif
                tusb_allow_idle(musb, wakeups);
        }
+done:
        spin_unlock_irqrestore(&musb->Lock, flags);
 }
 
@@ -636,8 +643,18 @@ void musb_platform_enable(struct musb * musb)
  */
 void musb_platform_disable(struct musb *musb)
 {
+       void __iomem    *base = musb->ctrl_base;
+
        /* FIXME stop DMA, IRQs, timers, ... */
 
+       /* disable all IRQs */
+       musb_writel(base, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
+       musb_writel(base, TUSB_USBIP_INT_MASK, 0);
+       musb_writel(base, TUSB_DMA_INT_MASK, 0x7fffffff);
+       musb_writel(base, TUSB_GPIO_INT_MASK, 0x1ff);
+
+       del_timer(&musb_idle_timer);
+
        if (is_dma_capable() && !dma_off) {
                printk(KERN_WARNING "%s %s: dma still active\n",
                                __FILE__, __FUNCTION__);
@@ -811,6 +828,8 @@ int __devinit musb_platform_init(struct musb *musb)
 
 int musb_platform_exit(struct musb *musb)
 {
+       del_timer_sync(&musb_idle_timer);
+
        if (musb->board_set_power)
                musb->board_set_power(0);
 
index cdee1b9888c82e40314786314e1be4ea7f5ccc8a..618251f4c17412305d946063467b530b0168c359 100644 (file)
@@ -105,8 +105,7 @@ static void musb_port_reset(struct musb *musb, u8 bReset)
        u8 devctl = musb_readb(pBase, MGC_O_HDRC_DEVCTL);
 
        if (musb->bDelayPortPowerOff || !(devctl & MGC_M_DEVCTL_HM)) {
-//             return;
-               DBG(1, "what?\n");
+               return;
        }
 #endif
 
@@ -158,13 +157,14 @@ void musb_root_disconnect(struct musb *musb)
 
        switch (musb->xceiv.state) {
        case OTG_STATE_A_HOST:
+       case OTG_STATE_A_SUSPEND:
                musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
                break;
        case OTG_STATE_A_WAIT_VFALL:
                musb->xceiv.state = OTG_STATE_B_IDLE;
                break;
        default:
-               DBG(1, "host disconnect, state %d\n", musb->xceiv.state);
+               DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
        }
 }