]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
musb_hdrc: davinci
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 9 Nov 2006 20:19:53 +0000 (22:19 +0200)
committerTony Lindgren <tony@atomide.com>
Thu, 9 Nov 2006 20:19:53 +0000 (22:19 +0200)
Update how DaVinci handles powering up VBUS, and make cable based role
switching work more or less as well as on TUSB6010.

  - Work around VBUS_ERROR IRQs triggering during enumeration, which
    seems to be a design botch from Mentor (TUSB 6010 has it too).

This is particularly nasty on the EVM (30 second delay) because
of differences between its RTL and the newer stuff in tusb6010.

  - Work around lack of ID change IRQ, a design botch in how the Mentor
    core was integrated into DaVinci.

This builds on the polling mechanism used to cope with VBUS
errors.  OTG devices will probe every few seconds until either
the ID pin grounds, or the device leaves B_IDLE.

  - Rename vbus_power() to source_power(), better matching its role.
    (DaVinci doesn't sink VBUS e.g. to charge batteries -- yet.)

  - Provide more complete tracing of IRQ events.

  - Provide a symbol for the DRVVBUS irq.

Basically, with this driver's core behaving better (thanks to OTG/host
updates for TUSB), the DaVinci stuff doesn't need to fight them so much;
and it's more clear when problems are in the core vs its integration.

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

index 8890f21c30ffa868877e06b25c6176b5c1f6c848..9e3b0f5543515356dafb4c111ef2d7c34f08e476 100644 (file)
@@ -95,6 +95,11 @@ void musb_platform_enable(struct musb *musb)
                                __FILE__, __FUNCTION__);
        else
                dma_off = 0;
+
+       /* force a DRVVBUS irq so we can start polling for ID change */
+       if (is_otg_enabled(musb))
+               musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+                       DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);
 }
 
 /*
@@ -119,12 +124,8 @@ void musb_platform_disable(struct musb *musb)
 }
 
 
-/* REVISIT this file shouldn't modify the OTG state machine ...
- *
- * The OTG infrastructure needs updating, to include things like
- * offchip DRVVBUS support and replacing MGC_OtgMachineInputs with
- * musb struct members (so e.g. vbus_state vanishes).
- */
+/* REVISIT it's not clear whether DaVinci can support full OTG.  */
+
 static int vbus_state = -1;
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -133,50 +134,6 @@ static int vbus_state = -1;
 #define        portstate(stmt)
 #endif
 
-static void session(struct musb *musb, int is_on)
-{
-       void    *__iomem mregs = musb->pRegs;
-
-       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 {
-               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);
-       }
-
-       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 */
 
@@ -189,25 +146,22 @@ 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_IDLE);
-
-       davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
-       vbus_state = is_on;
-       session(musb, is_on);
+       davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state);
+       vbus_state = !vbus_state;
 }
 DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus, 0);
 
 #endif /* modified board */
 #endif /* EVM */
 
-static void davinci_vbus_power(struct musb *musb, int is_on, int immediate)
+static void davinci_source_power(struct musb *musb, int is_on, int immediate)
 {
        if (is_on)
                is_on = 1;
 
        if (vbus_state == is_on)
                return;
+       vbus_state = !is_on;            /* 0/1 vs "-1 == unknown/init" */
 
 #ifdef CONFIG_MACH_DAVINCI_EVM
        if (machine_is_davinci_evm()) {
@@ -228,21 +182,77 @@ static void davinci_vbus_power(struct musb *musb, int is_on, int immediate)
 #endif
        }
 #endif
-       if (immediate) {
+       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).
-                */
-       }
 }
 
 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);
+       return davinci_source_power(musb, is_on, 0);
+}
+
+
+#define        POLL_SECONDS    2
+
+static struct timer_list otg_workaround;
+
+static void otg_timer(unsigned long _musb)
+{
+       struct musb             *musb = (void *)_musb;
+       void    *__iomem        mregs = musb->pRegs;
+       u8                      devctl;
+       unsigned long           flags;
+
+       /* We poll because DaVinci's won't expose several OTG-critical
+       * status change events (from the transceiver) otherwise.
+        */
+       devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
+       DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
+
+       spin_lock_irqsave(&musb->Lock, flags);
+       switch (musb->xceiv.state) {
+       case OTG_STATE_A_WAIT_VFALL:
+               /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
+                * seems to mis-handle session "start" otherwise (or in our
+                * case "recover"), in routine "VBUS was valid by the time
+                * VBUSERR got reported during enumeration" cases.
+                */
+               if (devctl & MGC_M_DEVCTL_VBUS) {
+                       mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+                       break;
+               }
+               musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+               musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+                       MGC_M_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
+               break;
+       case OTG_STATE_B_IDLE:
+               if (!is_peripheral_enabled(musb))
+                       break;
+
+               /* There's no ID-changed IRQ, so we have no good way to tell
+                * when to switch to the A-Default state machine (by setting
+                * the DEVCTL.SESSION flag).
+                *
+                * Workaround:  whenever we're in B_IDLE, try setting the
+                * session flag every few seconds.  If it works, ID was
+                * grounded and we're now in the A-Default state machine.
+                *
+                * NOTE setting the session flag is _supposed_ to trigger
+                * SRP, but clearly it doesn't.
+                */
+               musb_writeb(mregs, MGC_O_HDRC_DEVCTL,
+                               devctl | MGC_M_DEVCTL_SESSION);
+               devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
+               if (devctl & MGC_M_DEVCTL_BDEVICE)
+                       mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+               else
+                       musb->xceiv.state = OTG_STATE_A_IDLE;
+               break;
+       default:
+               break;
+       }
+       spin_unlock_irqrestore(&musb->Lock, flags);
 }
 
 static irqreturn_t davinci_interrupt(int irq, void *__hci)
@@ -272,7 +282,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
                u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
 
                if (cppi_tx || cppi_rx) {
-                       DBG(4, "<== CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
+                       DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
                        cppi_completion(musb, cppi_rx, cppi_tx);
                        retval = IRQ_HANDLED;
                }
@@ -281,6 +291,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
        /* ack and handle non-CPPI interrupts */
        tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
        musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp);
+       DBG(4, "IRQ %08x\n", tmp);
 
        musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)
                        >> DAVINCI_USB_RXINT_SHIFT;
@@ -289,23 +300,58 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
        musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK)
                        >> DAVINCI_USB_USBINT_SHIFT;
 
-       /* treat DRVVBUS irq like an ID change IRQ (for now) */
-       if (tmp & (1 << (8 + DAVINCI_USB_USBINT_SHIFT))) {
+       /* DRVVBUS irqs are the only proxy we have (a very poor one!) for
+        * DaVinci's missing ID change IRQ.  We need an ID change IRQ to
+        * switch appropriately between halves of the OTG state machine.
+        * Managing DEVCTL.SESSION per Mentor docs requires we know its
+        * value, but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
+        * Also, DRVVBUS pulses for SRP (but not at 5V) ...
+        */
+       if (tmp & (DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT)) {
                int     drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG);
-
-               if (drvvbus) {
+               void    *__iomem mregs = musb->pRegs;
+               u8      devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
+               int     err = musb->int_usb & MGC_M_INTR_VBUSERROR;
+
+               err = is_host_enabled(musb)
+                               && (musb->int_usb & MGC_M_INTR_VBUSERROR);
+               if (err) {
+                       /* The Mentor core doesn't debounce VBUS as needed
+                        * to cope with device connect current spikes. This
+                        * means it's not uncommon for bus-powered devices
+                        * to get VBUS errors during enumeration.
+                        *
+                        * This is a workaround, but newer RTL from Mentor
+                        * seems to lalow a better one: "re"starting sessions
+                        * without waiting (on EVM, a **long** time) for VBUS
+                        * to stop registering in devctl.
+                        */
+                       musb->int_usb &= ~MGC_M_INTR_VBUSERROR;
+                       musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+                       mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+                       WARN("VBUS error workaround (delay coming)\n");
+               } else if (is_host_enabled(musb) && drvvbus) {
+                       musb->is_active = 1;
                        MUSB_HST_MODE(musb);
                        musb->xceiv.default_a = 1;
-                       musb->xceiv.state = OTG_STATE_A_IDLE;
+                       musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+                       portstate(musb->port1_status |= USB_PORT_STAT_POWER);
+                       del_timer(&otg_workaround);
                } else {
+                       musb->is_active = 0;
                        MUSB_DEV_MODE(musb);
                        musb->xceiv.default_a = 0;
                        musb->xceiv.state = OTG_STATE_B_IDLE;
+                       portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
                }
 
                /* NOTE:  this must complete poweron within 100 msec */
-               davinci_vbus_power(musb, drvvbus, 0);
-               DBG(2, "DRVVBUS %d (%s)\n", drvvbus, otg_state_string(musb));
+               davinci_source_power(musb, drvvbus, 0);
+               DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
+                               drvvbus ? "on" : "off",
+                               otg_state_string(musb),
+                               err ? " ERROR" : "",
+                               devctl);
                retval = IRQ_HANDLED;
        }
 
@@ -315,6 +361,11 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
        /* irq stays asserted until EOI is written */
        musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
 
+       /* poll for ID change */
+       if (is_otg_enabled(musb)
+                       && musb->xceiv.state == OTG_STATE_B_IDLE)
+               mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+
        spin_unlock_irqrestore(&musb->Lock, flags);
 
        /* REVISIT we sometimes get unhandled IRQs
@@ -354,8 +405,11 @@ int __devinit musb_platform_init(struct musb *musb)
                evm_vbus_work.data = musb;
 #endif
 
+       if (is_host_enabled(musb))
+               setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
+
        musb->board_set_vbus = davinci_set_vbus;
-       davinci_vbus_power(musb, 0, 1);
+       davinci_source_power(musb, 0, 1);
 
        /* reset the controller */
        musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
@@ -377,7 +431,10 @@ int __devinit musb_platform_init(struct musb *musb)
 
 int musb_platform_exit(struct musb *musb)
 {
-       davinci_vbus_power(musb, 0 /*off*/, 1);
+       if (is_host_enabled(musb))
+               del_timer_sync(&otg_workaround);
+
+       davinci_source_power(musb, 0 /*off*/, 1);
 
        /* delay, to avoid problems with module reload */
        if (is_host_enabled(musb)) {
index cfb5184ed1b65f0f617dbda4e6b429df4d4a54f4..b5910ed0d797a0e45b97d579a36dd775baa98b81 100644 (file)
@@ -108,6 +108,8 @@ struct cppi_rx_stateram {
 #define DAVINCI_USB_TXINT_SHIFT                0
 #define DAVINCI_USB_RXINT_SHIFT                8
 
+#define DAVINCI_INTR_DRVVBUS           0x0100
+
 #define DAVINCI_USB_USBINT_MASK                0x01ff0000      /* 8 Mentor, DRVVBUS */
 #define DAVINCI_USB_TXINT_MASK \
        (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT)
index 7ff55ddae2bef9092140af5ace74df39754748b8..1daecc67e4fb732873eadb4cb6a9780a8efd717e 100644 (file)
@@ -1458,7 +1458,9 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
                goto done;
        case OTG_STATE_B_IDLE:
                /* REVISIT we might be able to do SRP even without OTG,
-                * though Linux doesn't yet expose that capability
+                * though Linux doesn't yet expose that capability.  SRP
+                * starts by setting DEVCTL.SESSION (not POWER.RESUME);
+                * though DaVinci can't do it.
                 */
                if (is_otg_enabled(musb)) {
                        musb->xceiv.state = OTG_STATE_B_SRP_INIT;
index b345d6f7fae8a613118c069789cbb359c9ece058..5bcee89bd6540eaddb0ea4a991039dcc85777d77 100644 (file)
@@ -694,8 +694,6 @@ void musb_start(struct musb *musb)
 
        musb_writeb(regs, MGC_O_HDRC_TESTMODE, 0);
 
-       musb_platform_enable(musb);
-
        /* put into basic highspeed mode and start session */
        musb_writeb(regs, MGC_O_HDRC_POWER, MGC_M_POWER_ISOUPDATE
                                                | MGC_M_POWER_SOFTCONN
@@ -727,6 +725,7 @@ void musb_start(struct musb *musb)
                if ((devctl & MGC_M_DEVCTL_VBUS) == MGC_M_DEVCTL_VBUS)
                        musb->is_active = 1;
        }
+       musb_platform_enable(musb);
        musb_writeb(regs, MGC_O_HDRC_DEVCTL, devctl);
 }