]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
MUSB: More TUSB OTG support
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 14 Sep 2006 15:08:32 +0000 (18:08 +0300)
committerTony Lindgren <tony@atomide.com>
Thu, 14 Sep 2006 15:08:32 +0000 (18:08 +0300)
Add more host/OTG functionality.

New Features:
   - Cable based role switching (using the ID pin) mostly works.  This gives
much of the end-user value for USB OTG; there's also dynamic role
switching (using special HNP protocol with another OTG device), which
surely won't work yet.
   - Root can write 0/N to /sys/module/musb_hdrc/parameters/musb_otg to
    disable OTG functionality, forcing B-Default mode (with optional SRP
support, but without 8mA current limits) when it's set.  Change this
before the gadget driver initializes (e.g. before "modprobe").

Bugfixes:
   - Fix many OTG mode startup glitches, notably for the root hub.
   - Add workaround for "issue 4".

Cleanups:
   - Remove more mode-specific #ifdeffery
   - ReMoveSomeCamelCasing
   - Quieter startup
   - Quash another Kconfig rebellion
   - Add missing bit declaration
   - Tersify TUSB irq messages

Open Issues:
   - Nothing passes remote wakeup down to root hub (unchanged)
   - Strange VBUS errors (unchanged; workaround: use hub modified for
N770, providing 5V supply till TUSB board does so reliably);
   - Plug in/out as peripheral many times; fine.  As host (with modified
hub), ditto.  Now peripheral again ... fails.

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

index 6aac44d3d22c2b67e2c8c23f22dfb965e34d3260..253a46f7c21d9445f51528b5e3495187f9b513f7 100644 (file)
@@ -1598,8 +1598,6 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 bEnd, int is_in)
                ep->end_point.ops = &musb_ep_ops;
                list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list);
        }
-       DBG(4, "periph: %s, maxpacket %d\n", ep->end_point.name,
-                       ep->end_point.maxpacket);
 }
 
 /*
@@ -1634,8 +1632,6 @@ static inline void __devinit musb_g_init_endpoints(struct musb *pThis)
                        }
                }
        }
-       DBG(2, "initialized %d (max %d) endpoints\n", count,
-                       MUSB_C_NUM_EPS * 2 - 1);
 }
 
 /* called once during driver setup to initialize and link into
@@ -1656,10 +1652,6 @@ int __devinit musb_gadget_setup(struct musb *pThis)
        pThis->g.ops = &musb_gadget_operations;
        pThis->g.is_dualspeed = 1;
        pThis->g.speed = USB_SPEED_UNKNOWN;
-#ifdef CONFIG_USB_MUSB_OTG
-       if (pThis->board_mode == MUSB_OTG)
-               pThis->g.is_otg = 1;
-#endif
 
        /* this "gadget" abstracts/virtualizes the controller */
        strcpy(pThis->g.dev.bus_id, "gadget");
@@ -1762,11 +1754,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
                 * hosts only see fully functional devices.
                 */
 
-               musb_start(pThis);
+               if (!is_otg_enabled(pThis))
+                       musb_start(pThis);
+
                spin_unlock_irqrestore(&pThis->Lock, flags);
 
-#ifdef CONFIG_USB_MUSB_OTG
-               if (pThis->board_mode == MUSB_OTG) {
+               if (is_otg_enabled(pThis)) {
                        DBG(3, "OTG startup...\n");
 
                        /* REVISIT:  funcall to other code, which also
@@ -1784,7 +1777,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
                                spin_unlock_irqrestore(&pThis->Lock, flags);
                        }
                }
-#endif
        }
 
        return retval;
@@ -1872,15 +1864,14 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
                retval = -EINVAL;
        spin_unlock_irqrestore(&musb->Lock, flags);
 
-#ifdef CONFIG_USB_MUSB_OTG
-       if (retval == 0 && musb->board_mode == MUSB_OTG) {
+       if (is_otg_enabled(musb) && retval == 0) {
                usb_remove_hcd(musb_to_hcd(musb));
                /* FIXME we need to be able to register another
                 * gadget driver here and have everything work;
                 * that currently misbehaves.
                 */
        }
-#endif
+
        return retval;
 }
 EXPORT_SYMBOL(usb_gadget_unregister_driver);
@@ -2002,13 +1993,16 @@ __acquires(pThis->Lock)
        pThis->g.a_alt_hnp_support = 0;
        pThis->g.a_hnp_support = 0;
 
+       if (is_otg_enabled(pThis))
+               pThis->g.is_otg = !!musb_otg;
+
        /* Normal reset, as B-Device;
         * or else after HNP, as A-Device
         */
        if (devctl & MGC_M_DEVCTL_BDEVICE) {
                pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;
                pThis->g.is_a_peripheral = 0;
-       } else if (is_otg_enabled(pThis) && pThis->board_mode == MUSB_OTG) {
+       } else if (is_otg_enabled(pThis) && musb_otg) {
                pThis->xceiv.state = OTG_STATE_A_PERIPHERAL;
                pThis->g.is_a_peripheral = 1;
        } else
@@ -2016,5 +2010,5 @@ __acquires(pThis->Lock)
 
        /* start with default limits on VBUS power draw */
        (void) musb_gadget_vbus_draw(&pThis->g,
-                       is_otg_enabled(pThis) ? 8 : 100);
+                       (is_otg_enabled(pThis) && musb_otg) ? 8 : 100);
 }
index 0319bd5388e6e449a789baf1482c04d32b5af276..5f1375c56f8dbb053ac13ce78dd583e82ff820bf 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/errno.h>
 #include <linux/device.h>
 #include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb.h>
 #include <linux/usb_otg.h>
 #include <linux/usb/musb.h>
 
@@ -67,13 +69,17 @@ struct musb_ep;
 #include "plat_arc.h"
 #include "musbhdrc.h"
 
+#include "musb_gadget.h"
+#include "../core/hcd.h"
+#include "musb_host.h"
+#include "otg.h"
+
 
 /* REVISIT tune this */
 #define        MIN_DMA_REQUEST         1       /* use PIO below this xfer size */
 
 
 #ifdef CONFIG_USB_MUSB_OTG
-#include "otg.h"
 
 #define        is_peripheral_enabled(musb)     ((musb)->board_mode != MUSB_HOST)
 #define        is_host_enabled(musb)           ((musb)->board_mode != MUSB_PERIPHERAL)
@@ -82,15 +88,8 @@ struct musb_ep;
 /* NOTE:  otg and peripheral-only state machines start at B_IDLE.
  * OTG or host-only go to A_IDLE when ID is sensed.
  */
-#define is_peripheral_active(m)        (is_peripheral_capable() && !(m)->bIsHost)
-#define is_host_active(m)      (is_host_capable() && (m)->bIsHost)
-
-/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't really
- * override that choice selection (often USB_GADGET_DUMMY_HCD).
- */
-#ifndef CONFIG_USB_GADGET_MUSB_HDRC
-#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
-#endif
+#define is_peripheral_active(m)                (!(m)->bIsHost)
+#define is_host_active(m)              ((m)->bIsHost)
 
 #else
 #define        is_peripheral_enabled(musb)     is_peripheral_capable()
@@ -101,6 +100,15 @@ struct musb_ep;
 #define        is_host_active(musb)            is_host_capable()
 #endif
 
+#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
+/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
+ * override that choice selection (often USB_GADGET_DUMMY_HCD).
+ */
+#ifndef CONFIG_USB_GADGET_MUSB_HDRC
+#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
+#endif
+#endif /* need MUSB gadget selection */
+
 
 #ifdef CONFIG_PROC_FS
 #include <linux/fs.h>
@@ -111,9 +119,6 @@ struct musb_ep;
 
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
 
-#include <linux/usb_gadget.h>
-#include "musb_gadget.h"
-
 #define        is_peripheral_capable() (1)
 
 extern irqreturn_t musb_g_ep0_irq(struct musb *);
@@ -129,8 +134,6 @@ extern void musb_g_disconnect(struct musb *);
 #define        is_peripheral_capable() (0)
 
 static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
-static inline void musb_g_tx(struct musb *m, u8 e) {}
-static inline void musb_g_rx(struct musb *m, u8 e) {}
 static inline void musb_g_reset(struct musb *m) {}
 static inline void musb_g_suspend(struct musb *m) {}
 static inline void musb_g_resume(struct musb *m) {}
@@ -142,10 +145,6 @@ static inline void musb_g_disconnect(struct musb *m) {}
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
 
-#include <linux/usb.h>
-#include "../core/hcd.h"
-#include "musb_host.h"
-
 #define        is_host_capable()       (1)
 
 extern irqreturn_t musb_h_ep0_irq(struct musb *);
@@ -160,8 +159,6 @@ static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
 static inline void musb_host_tx(struct musb *m, u8 e) {}
 static inline void musb_host_rx(struct musb *m, u8 e) {}
 
-static inline void musb_root_disconnect(struct musb *musb) { BUG(); }
-
 #endif
 
 
index a62961bffe6206b12e01d203ae2e0cc5176e00ef..cbea5d7b2e633af2f7c66866ad67df39762ebce7 100644 (file)
@@ -55,6 +55,9 @@
  * Finally, it provides the necessary bus control service.
  */
 
+/* sysfs flag to seletively force peripheral-only operation */
+extern int musb_otg;
+
 /****************************** CONSTANTS ********************************/
 
 /*
index b7318faeaa101e87fe4ef2b0d4cfc0e5a005a8ca..93bfa0bb105af47f835020e40084a3b85060afe1 100644 (file)
@@ -171,6 +171,19 @@ MODULE_LICENSE("GPL");
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_MUSB_OTG
+
+/* For debugging/prototyping:  allow disabling host side support on boards
+ * with Mini-AB (or Mini-A) connectors, making peripheral side support look
+ * like pure peripherals (not reporting OTG capabilities, and able to
+ * draw a full 100mA unit load).
+ */
+int musb_otg = 1;
+
+module_param(musb_otg, bool, 0600);
+MODULE_PARM_DESC(musb_otg, "enable/disable OTG capabilities");
+#endif
+
 static inline struct musb *dev_to_musb(struct device *dev)
 {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -629,12 +642,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
                /* peripheral suspend, may trigger HNP */
                if (!(devctl & MGC_M_DEVCTL_HM)) {
                        musb_g_suspend(pThis);
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
                        pThis->is_active = is_otg_enabled(pThis)
                                        && pThis->xceiv.gadget->b_hnp_enable;
-#else
-                       pThis->is_active = 0;
-#endif
                        otg_input_changed(pThis, devctl, FALSE, FALSE, TRUE);
                } else
                        pThis->is_active = 0;
@@ -648,39 +657,44 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
 /*
 * Program the HDRC to start (enable interrupts, dma, etc.).
 */
-void musb_start(struct musb * pThis)
+void musb_start(struct musb *musb)
 {
-       void __iomem *pBase = pThis->pRegs;
-       u8 state;
+       void __iomem    *regs = musb->pRegs;
+       u8              devctl;
 
        DBG(2, "<==\n");
 
        /* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */
 
        /*  Set INT enable registers, enable interrupts */
-       musb_writew(pBase, MGC_O_HDRC_INTRTXE, pThis->wEndMask);
-       musb_writew(pBase, MGC_O_HDRC_INTRRXE, pThis->wEndMask & 0xfffe);
-       musb_writeb(pBase, MGC_O_HDRC_INTRUSBE, 0xf7);
+       musb_writew(regs, MGC_O_HDRC_INTRTXE, musb->wEndMask);
+       musb_writew(regs, MGC_O_HDRC_INTRRXE, musb->wEndMask & 0xfffe);
+       musb_writeb(regs, MGC_O_HDRC_INTRUSBE, 0xf7);
 
-       musb_writeb(pBase, MGC_O_HDRC_TESTMODE, 0);
+       musb_writeb(regs, MGC_O_HDRC_TESTMODE, 0);
 
-       musb_platform_enable(pThis);
+       musb_platform_enable(musb);
 
        /* enable high-speed/low-power and start session */
-       musb_writeb(pBase, MGC_O_HDRC_POWER,
+       musb_writeb(regs, MGC_O_HDRC_POWER,
                MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB);
 
-       switch (pThis->board_mode) {
+       musb->is_active = 0;
+       switch (musb->board_mode) {
        case MUSB_HOST:
-               musb_set_vbus(pThis, 1);
+               musb_set_vbus(musb, 1);
                break;
        case MUSB_OTG:
-               WARN("how to start OTG session?\n");
+               /* 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:
-               state = musb_readb(pBase, MGC_O_HDRC_DEVCTL);
-               musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
-                       state & ~MGC_M_DEVCTL_SESSION);
+               devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
+               musb_writeb(regs, MGC_O_HDRC_DEVCTL,
+                       devctl & ~MGC_M_DEVCTL_SESSION);
                break;
        }
 }
@@ -958,7 +972,7 @@ static const struct fifo_cfg __devinitdata ep0_cfg = {
 static int __devinit ep_config_from_table(struct musb *musb)
 {
        const struct fifo_cfg   *cfg;
-       unsigned                n;
+       unsigned                i, n;
        int                     offset;
        struct musb_hw_ep       *hw_ep = musb->aLocalEnd;
 
@@ -995,13 +1009,13 @@ static int __devinit ep_config_from_table(struct musb *musb)
        offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
        // assert(offset > 0)
 
-       while (n--) {
+       for (i = 0; i < n; i++) {
                u8      epn = cfg->hw_ep_num;
 
                if (epn >= MUSB_C_NUM_EPS) {
                        pr_debug( "%s: invalid ep %d\n",
                                        musb_driver_name, epn);
-                       return -EINVAL;
+                       continue;
                }
                offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
                if (offset < 0) {
@@ -1015,7 +1029,7 @@ static int __devinit ep_config_from_table(struct musb *musb)
 
        printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
                        musb_driver_name,
-                       musb->bEndCount, MUSB_C_NUM_EPS * 2 - 1,
+                       n + 1, MUSB_C_NUM_EPS * 2 - 1,
                        offset, DYN_FIFO_SIZE);
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -1346,10 +1360,13 @@ irqreturn_t musb_interrupt(struct musb *musb)
                        // MGC_SelectEnd(musb->pRegs, ep_num);
                        /* REVISIT just retval = ep->rx_irq(...) */
                        retval = IRQ_HANDLED;
-                       if (devctl & MGC_M_DEVCTL_HM)
-                               musb_host_rx(musb, ep_num);
-                       else
-                               musb_g_rx(musb, ep_num);
+                       if (devctl & MGC_M_DEVCTL_HM) {
+                               if (is_host_capable())
+                                       musb_host_rx(musb, ep_num);
+                       } else {
+                               if (is_peripheral_capable())
+                                       musb_g_rx(musb, ep_num);
+                       }
                }
 
                reg >>= 1;
@@ -1364,10 +1381,13 @@ irqreturn_t musb_interrupt(struct musb *musb)
                        // MGC_SelectEnd(musb->pRegs, ep_num);
                        /* REVISIT just retval |= ep->tx_irq(...) */
                        retval = IRQ_HANDLED;
-                       if (devctl & MGC_M_DEVCTL_HM)
-                               musb_host_tx(musb, ep_num);
-                       else
-                               musb_g_tx(musb, ep_num);
+                       if (devctl & MGC_M_DEVCTL_HM) {
+                               if (is_host_capable())
+                                       musb_host_tx(musb, ep_num);
+                       } else {
+                               if (is_peripheral_capable())
+                                       musb_g_tx(musb, ep_num);
+                       }
                }
                reg >>= 1;
                ep_num++;
@@ -1406,16 +1426,22 @@ void musb_dma_completion(struct musb *musb, u8 bLocalEnd, u8 bTransmit)
        } else {
                /* endpoints 1..15 */
                if (bTransmit) {
-                       if (devctl & MGC_M_DEVCTL_HM)
-                               musb_host_tx(musb, bLocalEnd);
-                       else
-                               musb_g_tx(musb, bLocalEnd);
+                       if (devctl & MGC_M_DEVCTL_HM) {
+                               if (is_host_capable())
+                                       musb_host_tx(musb, bLocalEnd);
+                       } else {
+                               if (is_peripheral_capable())
+                                       musb_g_tx(musb, bLocalEnd);
+                       }
                } else {
                        /* receive */
-                       if (devctl & MGC_M_DEVCTL_HM)
-                               musb_host_rx(musb, bLocalEnd);
-                       else
-                               musb_g_rx(musb, bLocalEnd);
+                       if (devctl & MGC_M_DEVCTL_HM) {
+                               if (is_host_capable())
+                                       musb_host_rx(musb, bLocalEnd);
+                       } else {
+                               if (is_peripheral_capable())
+                                       musb_g_rx(musb, bLocalEnd);
+                       }
                }
        }
 }
index bf16d2d510d1b107cfcefd77b680d3330fdd01c5..f436e5332ce46c83b605d41446ccf5c10580ede0 100644 (file)
@@ -171,7 +171,7 @@ static int tusb_set_power(struct otg_transceiver *x, unsigned mA)
                reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
        musb_writel(base, TUSB_PRCM_MNGMT, reg);
 
-       DBG(3, "draw max %d mA VBUS\n", mA);
+       DBG(2, "draw max %d mA VBUS\n", mA);
        return 0;
 }
 
@@ -209,20 +209,21 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
        wakeup_enables |= TUSB_PRCM_WNORCS;
        musb_writel(base, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables);
 
-// FIXME issue 4, when host (driving vbus), enable hipower comparator
-
        /* REVISIT writeup of WLD implies that if WLD set and ID is grounded,
         * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared.
         * Presumably that's mostly to save power, hence WLD is immaterial ...
         */
 
        reg = musb_readl(base, TUSB_PRCM_MNGMT);
-       /* issue 4: when driving vbus, leave hipower comparator active */
-       if (!is_host_active(musb))
+       /* issue 4: when driving vbus, use hipower (vbus_det) comparator */
+       if (is_host_active(musb)) {
+               reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
+               reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
+       } else {
+               reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
                reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
-       reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN
-                       | TUSB_PRCM_MNGMT_PM_IDLE
-                       | TUSB_PRCM_MNGMT_DEV_IDLE;
+       }
+       reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
        musb_writel(base, TUSB_PRCM_MNGMT, reg);
 
        DBG(2, "idle, wake on %02x\n", wakeup_enables);
@@ -344,8 +345,10 @@ static void tusb_set_vbus(struct musb *musb, int is_on)
                if (musb->xceiv.default_a) {
                        musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
                        devctl &= ~MGC_M_DEVCTL_SESSION;
-               } else
+               } else {
+                       musb->xceiv.state = OTG_STATE_B_IDLE;
                        musb->is_active = 0;
+               }
        }
        prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
 
@@ -355,7 +358,7 @@ static void tusb_set_vbus(struct musb *musb, int is_on)
        musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);
 
        DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
-               is_on ? "on" : "off",
+               otg_state_string(musb),
                musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL),
                musb_readl(base, TUSB_DEV_OTG_STAT),
                conf, prcm);
@@ -371,13 +374,12 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                int     default_a;
 
                if (is_otg_enabled(musb))
-                       default_a = !!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
+                       default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
                else
                        default_a = is_host_enabled(musb);
-               if (default_a != musb->xceiv.default_a) {
-                       musb->xceiv.default_a = default_a;
-                       tusb_set_vbus(musb, default_a);
-               }
+               DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
+               musb->xceiv.default_a = default_a;
+               tusb_set_vbus(musb, default_a);
        }
 
        /* VBUS state change */
@@ -387,21 +389,16 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
                                || !is_host_enabled(musb)) {
 
-                       /* REVISIT use the b_sess_valid comparator, not
-                        * lowpower one; TUSB_DEV_OTG_STAT_SESS_VALID ?
-                        */
-
-                       if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_SENSE) {
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
-                               musb->is_active = 1;
-                               /* REVISIT start the session? */
-                       } else {
-                               musb->xceiv.state = OTG_STATE_B_IDLE;
+                       if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
+                               if (musb->xceiv.state != OTG_STATE_B_IDLE) {
+                                       /* INTR_DISCONNECT can hide... */
+                                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                                       musb->int_usb |= MGC_M_INTR_DISCONNECT;
+                               }
                                musb->is_active = 0;
                        }
-                       DBG(1, "%s\n", musb->is_active
-                                       ? "b_peripheral" : "b_idle");
-
+                       DBG(2, "vbus change, %s, otg %03x\n",
+                               otg_state_string(musb), otg_stat);
                        schedule_work(&musb->irq_work);
 
                } else /* A-dev state machine */ {
@@ -432,16 +429,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
 
        /* OTG timer expiration */
        if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) {
+               u8      devctl;
+
                DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
 
                switch (musb->xceiv.state) {
                case OTG_STATE_A_WAIT_VRISE:
                        /* VBUS has probably been valid for a while now */
+                       devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
                        if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
-                               u8      devctl;
-
-                               devctl = musb_readb(musb->pRegs,
-                                               MGC_O_HDRC_DEVCTL);
                                if ((devctl & MGC_M_DEVCTL_VBUS)
                                                != MGC_M_DEVCTL_VBUS) {
                                        DBG(2, "devctl %02x\n", devctl);
@@ -463,7 +459,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                                else
                                        musb_writel(base, TUSB_DEV_OTG_TIMER, 0);
                        } else {
-                               ERR("vbus rise time too slow\n");
+                               ERR("vbus too slow, devctl %02x\n", devctl);
                                tusb_set_vbus(musb, 0);
                        }
                        break;
@@ -487,17 +483,15 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
        struct musb     *musb = __hci;
        void __iomem    *base = musb->ctrl_base;
        unsigned long   flags;
-       u32             dma_src = 0, int_src;
+       u32             int_src;
 
        spin_lock_irqsave(&musb->Lock, flags);
 
        int_src = musb_readl(base, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS;
-       if (int_src & TUSB_INT_SRC_TXRX_DMA_DONE)
-               dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
-
-       DBG(3, "TUSB interrupt dma: %08x int: %08x\n", dma_src, int_src);
+       DBG(3, "TUSB IRQ %08x\n", int_src);
 
        musb->int_regs = r;
+       musb->int_usb = (u8) int_src;
 
        /* Acknowledge wake-up source interrupts */
        if (int_src & TUSB_INT_SRC_DEV_WAKEUP) {
@@ -540,11 +534,13 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
        /* TX dma callback must be handled here, RX dma callback is
         * handled in tusb_omap_dma_cb.
         */
-       if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE) && dma_src) {
-               u32 real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK);
+       if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
+               u32     dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
+               u32     real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK);
 
+               DBG(3, "DMA IRQ %08x\n", dma_src);
                real_dma_src = ~real_dma_src & dma_src;
-               if (tusb_dma_omap()) {
+               if (tusb_dma_omap() && real_dma_src) {
                        int     tx_source = (real_dma_src & 0xffff);
                        int     i;
 
@@ -568,10 +564,8 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
        } else
                musb->int_rx = musb->int_tx = 0;
 
-       if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) {
-               musb->int_usb = (u8) int_src;
+       if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff))
                musb_interrupt(musb);
-       }
 
        /* Acknowledge TUSB interrupts. Clear only non-reserved bits */
        musb_writel(base, TUSB_INT_SRC_CLEAR,
@@ -619,6 +613,9 @@ void musb_platform_enable(struct musb * musb)
 
        set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
 
+       /* kickstart:  force into the correct OTG state machine */
+       musb_writel(base, TUSB_INT_SRC_SET, TUSB_INT_SRC_ID_STATUS_CHNG);
+
        if (is_dma_capable() && dma_off)
                printk(KERN_WARNING "%s %s: dma not reactivated\n",
                                __FILE__, __FUNCTION__);
index cf3b250f51b3f9f11e4c32d6da63f90f19f6b354..a44a4b3479ec3ea2ff551fd03c26c62f6e171c5c 100644 (file)
@@ -75,6 +75,7 @@
 #define                TUSB_DEV_OTG_STAT_VBUS_VALID            (1 << 5)
 #define                TUSB_DEV_OTG_STAT_VBUS_SENSE            (1 << 4)
 #define                TUSB_DEV_OTG_STAT_ID_STATUS             (1 << 3)
+#define                TUSB_DEV_OTG_STAT_HOST_DISCON           (1 << 2)
 #define                TUSB_DEV_OTG_STAT_LINE_STATE            (3 << 0)
 #define                TUSB_DEV_OTG_STAT_DP_ENABLE             (1 << 1)
 #define                TUSB_DEV_OTG_STAT_DM_ENABLE             (1 << 0)
index 63f31fd1cd543cd49062b3de98c4b17d731da332..a1c7152bb3153ae82b7c5617a36ec242884d17f2 100644 (file)
@@ -51,6 +51,9 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
        u8              power;
        void __iomem    *pBase = musb->pRegs;
 
+       if (!is_host_active(musb))
+               return;
+
        power = musb_readb(pBase, MGC_O_HDRC_POWER);
 
        if (bSuspend) {
@@ -181,12 +184,11 @@ int musb_hub_control(
        int             retval = 0;
        unsigned long   flags;
 
-       if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)
-                       || !is_host_active(musb)))
+       if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
                return -ESHUTDOWN;
 
        /* hub features:  always zero, setting is a NOP
-        * port features: reported, sometimes updated
+        * port features: reported, sometimes updated when host is active
         * no indicators
         */
        spin_lock_irqsave(&musb->Lock, flags);
@@ -279,7 +281,8 @@ int musb_hub_control(
                         * initialization logic, e.g. for OTG, or change any
                         * logic relating to VBUS power-up.
                         */
-                       musb_start(musb);
+                       if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
+                               musb_start(musb);
                        break;
                case USB_PORT_FEAT_RESET:
                        musb_port_reset(musb, TRUE);
@@ -288,6 +291,9 @@ int musb_hub_control(
                        musb_port_suspend(musb, TRUE);
                        break;
                case USB_PORT_FEAT_TEST:
+                       if (unlikely(is_host_active(musb)))
+                               goto error;
+
                        wIndex >>= 8;
                        switch (wIndex) {
                        case 1: