From: David Brownell Date: Thu, 28 Sep 2006 14:46:12 +0000 (+0300) Subject: musb_hdrc: DaVinci-specific updates and cleanups X-Git-Tag: v2.6.18-omap1~35 X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=5261debe61c746ceda0ce5b305536056b577404c;p=linux-2.6-omap-h63xx.git musb_hdrc: DaVinci-specific updates and cleanups - Add header needed for DaVinci build. - Remove some partially obsolete comments. - Cleaner and more reproducible host side VBUS handling, including waiting for it to fall after powerdown on rmmod. - Tighten up various state machine transitions and diagnostics Plus removing some generic #ifdeffery. The vbus updates make it work more like the TUSB code, and the cleanups etc make upstream merging more practical. Signed-off-by: David Brownell --- diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 4acf7e895f5..dbf261a5b17 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -35,26 +35,26 @@ #include #include #include -// #include +#include #include #include "musbdefs.h" -#ifdef CONFIG_ARCH_DAVINCI - #ifdef CONFIG_MACH_DAVINCI_EVM #include #endif #include "davinci.h" -#endif - -#ifdef CONFIG_USB_TI_CPPI_DMA #include "cppi_dma.h" -#endif +/* REVISIT (PM) we should be able to keep the PHY in low power mode most + * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0 + * and, when in host mode, autosuspending idle root ports... PHYPLLON + * (overriding SUSPENDM?) then likely needs to stay off. + */ + static inline void phy_on(void) { /* start the on-chip PHY and its PLL */ @@ -68,7 +68,7 @@ static inline void phy_on(void) static inline void phy_off(void) { /* powerdown the on-chip PHY and its oscillator */ - __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYSPDWN, + __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, IO_ADDRESS(USBPHY_CTL_PADDR)); } @@ -138,26 +138,52 @@ static int vbus_state = -1; static void session(struct musb *musb, int is_on) { void *__iomem mregs = musb->pRegs; - u8 devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); - /* NOTE: after drvvbus off the state _could_ be A_IDLE; - * but the silicon seems to couple vbus to "ID grounded". - */ - devctl |= MGC_M_DEVCTL_SESSION; + if (musb->xceiv.default_a) { + u8 devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); + + if (is_on) + devctl |= MGC_M_DEVCTL_SESSION; + else + devctl &= ~MGC_M_DEVCTL_SESSION; + musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl); + } else + is_on = 0; + if (is_on) { + /* NOTE: assumes VBUS already exceeds A-valid */ musb->xceiv.state = OTG_STATE_A_WAIT_BCON; portstate(musb->port1_status |= USB_PORT_STAT_POWER); + MUSB_HST_MODE(musb); } else { - musb->xceiv.state = OTG_STATE_B_IDLE; + switch (musb->xceiv.state) { + case OTG_STATE_UNDEFINED: + case OTG_STATE_B_IDLE: + MUSB_DEV_MODE(musb); + musb->xceiv.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_IDLE: + break; + default: + musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + break; + } portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl); + + DBG(2, "Default-%c, VBUS power %s, %s, devctl %02x, %s\n", + musb->xceiv.default_a ? 'A' : 'B', + is_on ? "on" : "off", + MUSB_MODE(musb), + musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL), + otg_state_string(musb)); } /* VBUS SWITCHING IS BOARD-SPECIFIC */ #ifdef CONFIG_MACH_DAVINCI_EVM +#ifndef CONFIG_MACH_DAVINCI_EVM_OTG /* I2C operations are always synchronous, and require a task context. * With unloaded systems, using the shared workqueue seems to suffice @@ -166,7 +192,7 @@ static void session(struct musb *musb, int is_on) static void evm_deferred_drvvbus(void *_musb) { struct musb *musb = _musb; - int is_on = (musb->xceiv.state == OTG_STATE_A_WAIT_VRISE); + int is_on = (musb->xceiv.state == OTG_STATE_A_IDLE); davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on); vbus_state = is_on; @@ -174,9 +200,10 @@ static void evm_deferred_drvvbus(void *_musb) } DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus, 0); -#endif +#endif /* modified board */ +#endif /* EVM */ -static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping) +static void davinci_vbus_power(struct musb *musb, int is_on, int immediate) { if (is_on) is_on = 1; @@ -184,24 +211,6 @@ static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping) if (vbus_state == is_on) return; - if (is_on) { - musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; - MUSB_HST_MODE(musb); - } else { - switch (musb->xceiv.state) { - case OTG_STATE_UNDEFINED: - case OTG_STATE_B_IDLE: - MUSB_DEV_MODE(musb); - musb->xceiv.state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_A_IDLE: - break; - default: - musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; - break; - } - } - #ifdef CONFIG_MACH_DAVINCI_EVM if (machine_is_davinci_evm()) { #ifdef CONFIG_MACH_DAVINCI_EVM_OTG @@ -212,25 +221,29 @@ static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping) gpio_set(GPIO(6)); else gpio_clear(GPIO(6)); + immediate = 1; #else - if (sleeping) + if (immediate) davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on); else schedule_work(&evm_vbus_work); #endif } #endif - if (sleeping) { + if (immediate) { vbus_state = is_on; session(musb, is_on); + } else { + /* REVISIT: if is_on, start in A_WAIT_VRISE, then OTG timer + * should watch for session valid before calling session(). + * EVM charges C133 VERY quickly (but discharge is sloooow). + */ } - - DBG(2, "VBUS power %s, %s\n", is_on ? "on" : "off", - sleeping ? "immediate" : "deferred"); } static void davinci_set_vbus(struct musb *musb, int is_on) { + WARN_ON(is_on && is_peripheral_active(musb)); return davinci_vbus_power(musb, is_on, 0); } @@ -281,12 +294,23 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci, struct pt_regs *r) >> DAVINCI_USB_USBINT_SHIFT; musb->int_regs = r; + /* treat DRVVBUS irq like an ID change IRQ (for now) */ if (tmp & (1 << (8 + DAVINCI_USB_USBINT_SHIFT))) { int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG); + if (drvvbus) { + MUSB_HST_MODE(musb); + musb->xceiv.default_a = 1; + musb->xceiv.state = OTG_STATE_A_IDLE; + } else { + MUSB_DEV_MODE(musb); + musb->xceiv.default_a = 0; + musb->xceiv.state = OTG_STATE_B_IDLE; + } + /* NOTE: this must complete poweron within 100 msec */ davinci_vbus_power(musb, drvvbus, 0); - DBG(2, "DRVVBUS %d (state %d)\n", drvvbus, musb->xceiv.state); + DBG(2, "DRVVBUS %d (%s)\n", drvvbus, otg_state_string(musb)); retval = IRQ_HANDLED; } @@ -331,16 +355,13 @@ int __devinit musb_platform_init(struct musb *musb) if (revision == 0) return -ENODEV; - /* note that transceiver issues make us want to charge - * VBUS only when the PHY PLL is not active. - */ #ifdef CONFIG_MACH_DAVINCI_EVM - evm_vbus_work.data = musb; + if (machine_is_davinci_evm()) + evm_vbus_work.data = musb; #endif - davinci_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); - if (is_host_enabled(musb)) - musb->board_set_vbus = davinci_set_vbus; + musb->board_set_vbus = davinci_set_vbus; + davinci_vbus_power(musb, 0, 1); /* reset the controller */ musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1); @@ -362,7 +383,33 @@ int __devinit musb_platform_init(struct musb *musb) int musb_platform_exit(struct musb *musb) { - phy_off(); davinci_vbus_power(musb, 0 /*off*/, 1); + + /* delay, to avoid problems with module reload */ + if (is_host_enabled(musb)) { + int maxdelay = 30; + u8 devctl, warn = 0; + + /* if there's no peripheral connected, this can take a + * long time to fall, especially on EVM with huge C133. + */ + do { + devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL); + if (!(devctl & MGC_M_DEVCTL_VBUS)) + break; + if ((devctl & MGC_M_DEVCTL_VBUS) != warn) { + warn = devctl & MGC_M_DEVCTL_VBUS; + DBG(1, "VBUS %d\n", warn >> MGC_S_DEVCTL_VBUS); + } + msleep(1000); + maxdelay--; + } while (maxdelay > 0); + + /* in OTG mode, another host might be connected */ + if (devctl & MGC_M_DEVCTL_VBUS) + DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); + } + + phy_off(); return 0; } diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h index b55112eeb2e..cfb5184ed1b 100644 --- a/drivers/usb/musb/davinci.h +++ b/drivers/usb/musb/davinci.h @@ -20,9 +20,9 @@ #define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ #define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ #define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ -#define USBPHY_CLK01SEL (1 << 3) +#define USBPHY_CLKO1SEL (1 << 3) #define USBPHY_OSCPDWN (1 << 2) -#define USBPHY_PHYSPDWN (1 << 0) +#define USBPHY_PHYPDWN (1 << 0) /* For now include usb OTG module registers here */ #define DAVINCI_USB_VERSION_REG 0x00 diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e7f22d048e6..3f2e6f4201a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -214,15 +214,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) if (is_in) return; - /* TODO: with CPPI DMA, once DMA is setup and DmaReqEnable in TxCSR - * is set (which is the case) transfer is initiated. For periodic - * transfer support, add another field in pEnd struct which will - * serve as a flag. If CPPI DMA is programmed for the transfer set - * this flag and disable DMAReqEnab while programming TxCSR in - * programEnd() Once we reach the appropriate time, enable DMA Req - * instead of calling musb_h_tx_start() function - */ - /* determine if the time is right for a periodic transfer */ switch (qh->type) { case USB_ENDPOINT_XFER_ISOC: diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c index a5df442020d..b7ea7116127 100644 --- a/drivers/usb/musb/musb_procfs.c +++ b/drivers/usb/musb/musb_procfs.c @@ -42,6 +42,7 @@ #include #include #include /* FIXME remove procfs writes */ +#include #include "musbdefs.h" diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index 8abdd6d2c45..f2951136f48 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -78,17 +78,6 @@ * exist tend to be severely undercommitted. You can't yet hook * up both a keyboard and a mouse to an external USB hub. * - * * Host side doesn't understand that hardware endpoints have two - * directions, so it uses only half the resources available on - * chips like DaVinci or TUSB 6010. - * - * +++ PARTIALLY RESOLVED +++ - * - * RESULT: On DaVinci (and TUSB 6010), only one external device may - * use periodic transfers, other than the hub used to connect it. - * (And if it were to understand, there would still be limitations - * because of the lack of periodic endpoint scheduling.) - * * - Provides its own OTG bits. These are untested, and many of them * seem to be superfluous code bloat given what usbcore does. (They * have now been partially removed.) @@ -416,7 +405,7 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, #ifdef CONFIG_USB_MUSB_HDRC_HCD /* see manual for the order of the tests */ if (bIntrUSB & MGC_M_INTR_SESSREQ) { - DBG(1, "SESSION_REQUEST (%d)\n", pThis->xceiv.state); + DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(pThis)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -660,11 +649,9 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, void musb_start(struct musb *musb) { void __iomem *regs = musb->pRegs; - u8 devctl; + u8 devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL); - DBG(2, "<==\n"); - - /* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */ + DBG(2, "<== devctl %02x\n", devctl); /* Set INT enable registers, enable interrupts */ musb_writew(regs, MGC_O_HDRC_INTRTXE, musb->wEndMask); @@ -675,30 +662,37 @@ void musb_start(struct musb *musb) musb_platform_enable(musb); - /* enable high-speed/low-power and start session */ - musb_writeb(regs, MGC_O_HDRC_POWER, - MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB); + /* put into basic highspeed mode and start session */ + musb_writeb(regs, MGC_O_HDRC_POWER, MGC_M_POWER_ISOUPDATE + | MGC_M_POWER_SOFTCONN + | MGC_M_POWER_HSENAB + // | MGC_M_POWER_ENSUSPEND + ); musb->is_active = 0; - switch (musb->board_mode) { - case MUSB_HOST: - musb_set_vbus(musb, 1); - break; - case MUSB_OTG: + devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL); + devctl &= ~MGC_M_DEVCTL_SESSION; + + if (is_otg_enabled(pThis)) { /* session started after: * (a) ID-grounded irq, host mode; * (b) vbus present/connect IRQ, peripheral mode; * (c) peripheral initiates, using SRP */ - break; - case MUSB_PERIPHERAL: - devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL); if ((devctl & MGC_M_DEVCTL_VBUS) == MGC_M_DEVCTL_VBUS) musb->is_active = 1; - musb_writeb(regs, MGC_O_HDRC_DEVCTL, - devctl & ~MGC_M_DEVCTL_SESSION); - break; + else + devctl |= MGC_M_DEVCTL_SESSION; + + } else if (is_host_enabled(pThis)) { + /* assume ID pin is hard-wired to ground */ + devctl |= MGC_M_DEVCTL_SESSION; + + } else /* peripheral is enabled */ { + if ((devctl & MGC_M_DEVCTL_VBUS) == MGC_M_DEVCTL_VBUS) + musb->is_active = 1; } + musb_writeb(regs, MGC_O_HDRC_DEVCTL, devctl); } @@ -1514,7 +1508,7 @@ musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf) break; case MUSB_OTG: v1 = "Mini-"; - v2 = (vbus & MGC_M_DEVCTL_BDEVICE) ? "A" : "B"; + v2 = (vbus & MGC_M_DEVCTL_BDEVICE) ? "B" : "A"; break; } } else /* VBUS level below A-Valid */ @@ -1613,7 +1607,11 @@ static void musb_free(struct musb *musb) (void) c->stop(c->pPrivateData); dma_controller_factory.destroy(c); } + + musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, 0); musb_platform_exit(musb); + musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, 0); + if (musb->clock) { clk_disable(musb->clock); clk_put(musb->clock); @@ -1762,16 +1760,25 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) #endif /* For the host-only role, we can activate right away. + * (We expect the ID pin to be forcibly grounded!!) * Otherwise, wait till the gadget driver hooks up. - * - * REVISIT switch to compile-time is_role_host() etc - * to get rid of #ifdeffery */ - switch (pThis->board_mode) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - case MUSB_HOST: + pThis->xceiv.state = OTG_STATE_B_IDLE; + pThis->xceiv.default_a = 0; + + if (is_otg_enabled(pThis)) { + MUSB_OTG_MODE(pThis); + status = musb_gadget_setup(pThis); + + DBG(1, "%s mode, status %d, dev%02x\n", + "OTG", status, + musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL)); + + } else if (is_host_enabled(pThis)) { MUSB_HST_MODE(pThis); + pThis->xceiv.default_a = 1; pThis->xceiv.state = OTG_STATE_A_IDLE; + status = usb_add_hcd(musb_to_hcd(pThis), -1, 0); DBG(1, "%s mode, status %d, devctl %02x %c\n", @@ -1780,28 +1787,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) (musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL) & MGC_M_DEVCTL_BDEVICE ? 'B' : 'A')); - break; -#endif -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - case MUSB_PERIPHERAL: + + } else /* peripheral is enabled */ { MUSB_DEV_MODE(pThis); status = musb_gadget_setup(pThis); DBG(1, "%s mode, status %d, dev%02x\n", "PERIPHERAL", status, musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL)); - break; -#endif -#ifdef CONFIG_USB_MUSB_OTG - case MUSB_OTG: - MUSB_OTG_MODE(pThis); - status = musb_gadget_setup(pThis); - DBG(1, "%s mode, status %d, dev%02x\n", - "OTG", status, - musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL)); -#endif - break; } if (status == 0)