]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
musb_hdrc: DaVinci-specific updates and cleanups
authorDavid Brownell <david-b@pacbell.net>
Thu, 28 Sep 2006 14:46:12 +0000 (17:46 +0300)
committerTony Lindgren <tony@atomide.com>
Thu, 28 Sep 2006 14:46:12 +0000 (17:46 +0300)
 - 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 <dbrownell@users.sourceforge.net>
drivers/usb/musb/davinci.c
drivers/usb/musb/davinci.h
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_procfs.c
drivers/usb/musb/plat_uds.c

index 4acf7e895f5d075989f233bbf9f8a66685911901..dbf261a5b17c696098e09563f9b7d1c56d8b959f 100644 (file)
 #include <asm/io.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/memory.h>
-// #include <asm/arch/gpio.h>
+#include <asm/arch/gpio.h>
 #include <asm/mach-types.h>
 
 #include "musbdefs.h"
 
 
-#ifdef CONFIG_ARCH_DAVINCI
-
 #ifdef CONFIG_MACH_DAVINCI_EVM
 #include <asm/arch/i2c-client.h>
 #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;
 }
index b55112eeb2e841273e4488a306f99027261b4ae9..cfb5184ed1b65f0f617dbda4e6b429df4d4a54f4 100644 (file)
@@ -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
index e7f22d048e6a30c71c154ec0669f90f887e5e740..3f2e6f4201ab631f09c43ccd9590633e85e63eb5 100644 (file)
@@ -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:
index a5df442020dcfadaac8d8b2c017dd1eabbe327e8..b7ea711612726f7bde82c02b57d30c140d781d80 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <asm/uaccess.h>       /* FIXME remove procfs writes */
+#include <asm/arch/hardware.h>
 
 #include "musbdefs.h"
 
index 8abdd6d2c45ba4ca77ccf8f5782c0338a1d31ada..f2951136f48305bc70ecec9bc44d7a2d6f81358d 100644 (file)
  *        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)