From: David Brownell Date: Thu, 9 Nov 2006 20:19:31 +0000 (+0200) Subject: musb_hdrc: small fixes, mostly suspend/resume X-Git-Tag: v2.6.19-omap1~78 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=b659d87e222109552a0b4fdca21407981eb1372a;p=linux-2.6-omap-h63xx.git musb_hdrc: small fixes, mostly suspend/resume 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 --- diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 0bdcf19a4c9..7ff55ddae2b 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -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)); } } diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c index 9b8994068bc..07133c2ccf4 100644 --- a/drivers/usb/musb/musb_procfs.c +++ b/drivers/usb/musb/musb_procfs.c @@ -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" diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index f0a01b112fe..dd471d059d7 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -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, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2eee49bc74c..0e3b444edd6 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -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); diff --git a/drivers/usb/musb/virthub.c b/drivers/usb/musb/virthub.c index cdee1b9888c..618251f4c17 100644 --- a/drivers/usb/musb/virthub.c +++ b/drivers/usb/musb/virthub.c @@ -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)); } }