]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
musb_hdrc: Add support for SRP, HNP and basic host idling
authorTony Lindgren <tony@atomide.com>
Fri, 29 Jun 2007 07:09:52 +0000 (00:09 -0700)
committerTony Lindgren <tony@atomide.com>
Fri, 29 Jun 2007 07:09:52 +0000 (00:09 -0700)
Several changes, mostly affecting tusb:

- Make SRP and HNP mostly work
- Add a timer for handling OTG events
- Add a timeout for turning off VBUS in host mode via sysfs

Only tested on tusb.

Signed-off-by: Tony Lindgren <tony@atomide.com>
drivers/usb/musb/g_ep0.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_procfs.c
drivers/usb/musb/musbdefs.h
drivers/usb/musb/plat_uds.c
drivers/usb/musb/tusb6010.c
drivers/usb/musb/virthub.c

index aad53fdd2a3041967b2e249c77368c06a663a455..644aada1bc1888e44bb840be732273299d719c06 100644 (file)
@@ -200,6 +200,19 @@ static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req)
        musb_g_giveback(&musb->aLocalEnd[0].ep_in, req, 0);
 }
 
+/*
+ * Tries to start B-device HNP negotiation if enabled via sysfs
+ */
+static inline void musb_try_b_hnp_enable(struct musb *musb)
+{
+       void __iomem    *pBase = musb->pRegs;
+       u8              devctl;
+
+       DBG(1, "HNP: Setting HR\n");
+       devctl = musb_readb(pBase, MGC_O_HDRC_DEVCTL);
+       musb_writeb(pBase, MGC_O_HDRC_DEVCTL, devctl | MGC_M_DEVCTL_HR);
+}
+
 /*
  * Handle all control requests with no DATA stage, including standard
  * requests such as:
@@ -326,17 +339,8 @@ __acquires(musb->Lock)
                                case USB_DEVICE_B_HNP_ENABLE:
                                        if (!musb->g.is_otg)
                                                goto stall;
-                                       { u8 devctl;
                                        musb->g.b_hnp_enable = 1;
-                                       devctl = musb_readb(pBase,
-                                                       MGC_O_HDRC_DEVCTL);
-                                       /* NOTE:  at least DaVinci doesn't
-                                        * like to set HR ...
-                                        */
-                                       DBG(1, "set HR\n");
-                                       musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
-                                               devctl | MGC_M_DEVCTL_HR);
-                                       }
+                                       musb_try_b_hnp_enable(musb);
                                        break;
                                case USB_DEVICE_A_HNP_SUPPORT:
                                        if (!musb->g.is_otg)
index b466495debf0fe8ea25f8b9d71885ab5be91c71b..ce389cdbb9f8625f7c0aee5ee33c801206119dda 100644 (file)
@@ -1462,10 +1462,15 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
                goto done;
        case OTG_STATE_B_IDLE:
                /* Start SRP ... OTG not required. */
+               DBG(2, "Sending SRP\n");
                devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
                devctl |= MGC_M_DEVCTL_SESSION;
                musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl);
-               DBG(2, "SRP\n");
+
+               /* Block idling for at least 1s */
+               musb_platform_try_idle(musb,
+                       msecs_to_jiffies(1 * HZ));
+
                status = 0;
                goto done;
        default:
@@ -1689,7 +1694,7 @@ int __init musb_gadget_setup(struct musb *musb)
        musb_g_init_endpoints(musb);
 
        musb->is_active = 0;
-       musb_platform_try_idle(musb);
+       musb_platform_try_idle(musb, 0);
 
        status = device_register(&musb->g.dev);
        if (status != 0)
@@ -1872,6 +1877,11 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
         */
 
        spin_lock_irqsave(&musb->Lock, flags);
+
+#ifdef CONFIG_USB_MUSB_OTG
+       musb_hnp_stop(musb);
+#endif
+
        if (musb->pGadgetDriver == driver) {
                musb->xceiv.state = OTG_STATE_UNDEFINED;
                stop_activity(musb, driver);
@@ -1885,7 +1895,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
                musb->g.dev.driver = NULL;
 
                musb->is_active = 0;
-               musb_platform_try_idle(musb);
+               musb_platform_try_idle(musb, 0);
        } else
                retval = -EINVAL;
        spin_unlock_irqrestore(&musb->Lock, flags);
@@ -1958,6 +1968,12 @@ void musb_g_suspend(struct musb *musb)
        }
 }
 
+/* Called during SRP. Caller must hold lock */
+void musb_g_wakeup(struct musb *musb)
+{
+       musb_gadget_wakeup(&musb->g);
+}
+
 /* called when VBUS drops below session threshold, and in other cases */
 void musb_g_disconnect(struct musb *musb)
 {
@@ -1984,6 +2000,9 @@ void musb_g_disconnect(struct musb *musb)
 #ifdef CONFIG_USB_MUSB_OTG
                musb->xceiv.state = OTG_STATE_A_IDLE;
                break;
+       case OTG_STATE_A_PERIPHERAL:
+               musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+               break;
        case OTG_STATE_B_WAIT_ACON:
        case OTG_STATE_B_HOST:
 #endif
index 5bddf30a3fd6bed14de60892ab30ff346831ed6a..857881e6ab97ce04f54481a628fc1eb66f5c0e08 100644 (file)
@@ -773,7 +773,7 @@ static int musb_proc_write(struct file *file, const char __user *buffer,
                break;
        }
 
-       musb_platform_try_idle(musb);
+       musb_platform_try_idle(musb, 0);
 
        return count;
 }
@@ -811,7 +811,7 @@ static int musb_proc_read(char *page, char **start,
                }
        }
 
-       musb_platform_try_idle(pThis);
+       musb_platform_try_idle(pThis, 0);
 
        spin_unlock_irqrestore(&pThis->Lock, flags);
        *eof = 1;
index b534d796035fd209b532ea6e64158f7dfb358e6c..876798228ba4196324ac763745e08286ef63204d 100644 (file)
@@ -124,6 +124,7 @@ extern void musb_g_rx(struct musb *, u8);
 extern void musb_g_reset(struct musb *);
 extern void musb_g_suspend(struct musb *);
 extern void musb_g_resume(struct musb *);
+extern void musb_g_wakeup(struct musb *);
 extern void musb_g_disconnect(struct musb *);
 
 #else
@@ -134,6 +135,7 @@ static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
 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) {}
+static inline void musb_g_wakeup(struct musb *m) {}
 static inline void musb_g_disconnect(struct musb *m) {}
 
 #endif
@@ -417,6 +419,9 @@ struct musb {
        unsigned bIsHost:1;
        unsigned bIgnoreDisconnect:1;   /* during bus resets */
 
+       int                     a_wait_bcon;    /* VBUS timeout in msecs */
+       unsigned long           idle_timeout;   /* Next timeout in jiffies */
+
 #ifdef C_MP_TX
        unsigned bBulkSplit:1;
 #define        can_bulk_split(musb,type) \
@@ -506,12 +511,14 @@ extern irqreturn_t musb_interrupt(struct musb *);
 extern void musb_platform_enable(struct musb *musb);
 extern void musb_platform_disable(struct musb *musb);
 
+extern void musb_hnp_stop(struct musb *musb);
+
 #ifdef CONFIG_USB_TUSB6010
-extern void musb_platform_try_idle(struct musb *musb);
+extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
 extern int musb_platform_get_vbus_status(struct musb *musb);
 extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
 #else
-#define musb_platform_try_idle(x)              do {} while (0)
+#define musb_platform_try_idle(x, y)           do {} while (0)
 #define musb_platform_get_vbus_status(x)       0
 #define musb_platform_set_mode(x, y)           do {} while (0)
 #endif
index 1b7d96697115b9b0ac0f7839e34719a4405d8d28..3aca510af90618f2882c301c07035faadfd39f46 100644 (file)
@@ -281,6 +281,69 @@ void musb_load_testpacket(struct musb *musb)
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_MUSB_OTG
+
+/*
+ * See also USB_OTG_1-3.pdf 6.6.5 Timers
+ * REVISIT: Are the other timers done in the hardware?
+ */
+#define TB_ASE0_BRST           100     /* Min 3.125 ms */
+
+/*
+ * Handles OTG hnp timeouts, such as b_ase0_brst
+ */
+void musb_otg_timer_func(unsigned long data)
+{
+       struct musb     *musb = (struct musb *)data;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&musb->Lock, flags);
+       if (musb->xceiv.state == OTG_STATE_B_WAIT_ACON) {
+               DBG(1, "HNP: B_WAIT_ACON timeout, going back to B_PERIPHERAL\n");
+               musb_g_disconnect(musb);
+               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               musb->is_active = 0;
+       }
+       spin_unlock_irqrestore(&musb->Lock, flags);
+}
+
+static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0);
+
+/*
+ * Stops the B-device HNP state. Caller must take care of locking.
+ */
+void musb_hnp_stop(struct musb *musb)
+{
+       struct usb_hcd  *hcd = musb_to_hcd(musb);
+       void __iomem    *pBase = musb->pRegs;
+       u8      reg;
+
+       switch (musb->xceiv.state) {
+       case OTG_STATE_A_PERIPHERAL:
+       case OTG_STATE_A_WAIT_VFALL:
+               DBG(1, "HNP: Switching back to A-host\n");
+               musb_g_disconnect(musb);
+               musb_root_disconnect(musb);
+               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->is_active = 0;
+               break;
+       case OTG_STATE_B_HOST:
+               DBG(1, "HNP: Disabling HR\n");
+               hcd->self.is_b_host = 0;
+               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               reg = musb_readb(pBase, MGC_O_HDRC_POWER);
+               reg |= MGC_M_POWER_SUSPENDM;
+               musb_writeb(pBase, MGC_O_HDRC_POWER, reg);
+               /* REVISIT: Start SESSION_REQUEST here? */
+               break;
+       default:
+               DBG(1, "HNP: Stopping in unknown state %s\n",
+                       otg_state_string(musb));
+       }
+}
+
+#endif
+
 /*
  * Interrupt Service Routine to record USB "global" interrupts.
  * Since these do not happen often and signify things of
@@ -519,12 +582,16 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
                /* indicate new connection to OTG machine */
                switch (pThis->xceiv.state) {
                case OTG_STATE_B_WAIT_ACON:
+                       DBG(1, "HNP: Waiting to switch to b_host state\n");
                        pThis->xceiv.state = OTG_STATE_B_HOST;
+                       hcd->self.is_b_host = 1;
                        break;
                default:
                        if ((devctl & MGC_M_DEVCTL_VBUS)
-                                       == (3 << MGC_S_DEVCTL_VBUS))
+                                       == (3 << MGC_S_DEVCTL_VBUS)) {
                                pThis->xceiv.state = OTG_STATE_A_HOST;
+                               hcd->self.is_b_host = 0;
+                       }
                        break;
                }
                DBG(1, "CONNECT (%s) devctl %02x\n",
@@ -633,11 +700,17 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
                case OTG_STATE_A_HOST:
                case OTG_STATE_A_SUSPEND:
                        musb_root_disconnect(pThis);
+                       if (pThis->a_wait_bcon != 0)
+                               musb_platform_try_idle(pThis, jiffies
+                                       + msecs_to_jiffies(pThis->a_wait_bcon));
                        break;
 #endif /* HOST */
 #ifdef CONFIG_USB_MUSB_OTG
-               case OTG_STATE_A_PERIPHERAL:
                case OTG_STATE_B_HOST:
+                       musb_hnp_stop(pThis);
+                       break;
+                       /* FALLTHROUGH */
+               case OTG_STATE_A_PERIPHERAL:
                        musb_root_disconnect(pThis);
                        /* FALLTHROUGH */
                case OTG_STATE_B_WAIT_ACON:
@@ -662,20 +735,37 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
                handled = IRQ_HANDLED;
 
                switch (pThis->xceiv.state) {
+               case OTG_STATE_A_PERIPHERAL:
+                       musb_hnp_stop(pThis);
+                       break;
                case OTG_STATE_B_PERIPHERAL:
                        musb_g_suspend(pThis);
                        pThis->is_active = is_otg_enabled(pThis)
                                        && pThis->xceiv.gadget->b_hnp_enable;
                        if (pThis->is_active) {
                                pThis->xceiv.state = OTG_STATE_B_WAIT_ACON;
-                               /* REVISIT timeout for b_ase0_brst, etc */
+#ifdef CONFIG_USB_MUSB_OTG
+                               DBG(1, "HNP: Setting timer for b_ase0_brst\n");
+                               musb_otg_timer.data = (unsigned long)pThis;
+                               mod_timer(&musb_otg_timer, jiffies
+                                       + msecs_to_jiffies(TB_ASE0_BRST));
+#endif
                        }
                        break;
+               case OTG_STATE_A_WAIT_BCON:
+                       if (pThis->a_wait_bcon != 0)
+                               musb_platform_try_idle(pThis, jiffies
+                                       + msecs_to_jiffies(pThis->a_wait_bcon));
+                       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;
+               case OTG_STATE_B_HOST:
+                       /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
+                       DBG(1, "REVISIT: SUSPEND as B_HOST\n");
+                       break;
                default:
                        /* "should not happen" */
                        pThis->is_active = 0;
@@ -783,7 +873,7 @@ void musb_stop(struct musb *musb)
         *    OTG mode, gadget driver module rmmod/modprobe cycles that
         *  - ...
         */
-       musb_platform_try_idle(musb);
+       musb_platform_try_idle(musb, 0);
 }
 
 static void musb_shutdown(struct platform_device *pdev)
@@ -1567,13 +1657,72 @@ musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf)
        } else  /* VBUS level below A-Valid */
                v2 = "disconnected";
 #endif
-       musb_platform_try_idle(musb);
+       musb_platform_try_idle(musb, 0);
        spin_unlock_irqrestore(&musb->Lock, flags);
 
        return sprintf(buf, "%s%s\n", v1, v2);
 }
 static DEVICE_ATTR(cable, S_IRUGO, musb_cable_show, NULL);
 
+static ssize_t
+musb_vbus_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t n)
+{
+       struct musb     *musb = dev_to_musb(dev);
+       unsigned long   flags;
+       unsigned long   val;
+
+       spin_lock_irqsave(&musb->Lock, flags);
+       if (sscanf(buf, "%lu", &val) < 1) {
+               printk(KERN_ERR "Invalid VBUS timeout ms value\n");
+               return -EINVAL;
+       }
+       musb->a_wait_bcon = val;
+       if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
+               musb->is_active = 0;
+       musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
+       spin_unlock_irqrestore(&musb->Lock, flags);
+
+       return n;
+}
+
+static ssize_t
+musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct musb     *musb = dev_to_musb(dev);
+       unsigned long   flags;
+       unsigned long   val;
+
+       spin_lock_irqsave(&musb->Lock, flags);
+       val = musb->a_wait_bcon;
+       spin_unlock_irqrestore(&musb->Lock, flags);
+
+       return sprintf(buf, "%lu\n", val);
+}
+static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
+
+static ssize_t
+musb_srp_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t n)
+{
+       struct musb     *musb=dev_to_musb(dev);
+       unsigned long   flags;
+       unsigned short  srp;
+
+       if (sscanf(buf, "%hu", &srp) != 1
+                       || (srp != 1)) {
+               printk (KERN_ERR "SRP: Value must be 1\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&musb->Lock, flags);
+       if (srp == 1)
+               musb_g_wakeup(musb);
+       spin_unlock_irqrestore(&musb->Lock, flags);
+
+       return n;
+}
+static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
 #endif
 
 /* Only used to provide cable state change events */
@@ -1643,6 +1792,10 @@ static void musb_free(struct musb *musb)
 #ifdef CONFIG_SYSFS
        device_remove_file(musb->controller, &dev_attr_mode);
        device_remove_file(musb->controller, &dev_attr_cable);
+       device_remove_file(musb->controller, &dev_attr_vbus);
+#ifdef CONFIG_USB_MUSB_OTG
+       device_remove_file(musb->controller, &dev_attr_srp);
+#endif
 #endif
 
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
@@ -1871,6 +2024,10 @@ fail:
 #ifdef CONFIG_SYSFS
        status = device_create_file(dev, &dev_attr_mode);
        status = device_create_file(dev, &dev_attr_cable);
+       status = device_create_file(dev, &dev_attr_vbus);
+#ifdef CONFIG_USB_MUSB_OTG
+       status = device_create_file(dev, &dev_attr_srp);
+#endif /* CONFIG_USB_MUSB_OTG */
        status = 0;
 #endif
 
index f3028a469f84d71661d0dee5101c006d405179ad..ddeca4a3a612e469a5d999bf5655754c02b48608 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "musbdefs.h"
 
+static void tusb_source_power(struct musb *musb, int is_on);
+
 /*
  * Checks the revision. We need to use the DMA register as 3.0 does not
  * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV.
@@ -71,7 +73,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled)
 {
        void __iomem    *base = musb->ctrl_base;
        static u32      phy_otg_ctrl = 0, phy_otg_ena = 0;
-       u32             int_src, tmp;
+       u32             tmp;
 
        if (enabled) {
                phy_otg_ctrl = musb_readl(base, TUSB_PHY_OTG_CTRL);
@@ -321,7 +323,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode)
  * USB link is not suspended ... and tells us the relevant wakeup
  * events.  SW_EN for voltage is handled separately.
  */
-static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
+void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
 {
        void __iomem    *base = musb->ctrl_base;
        u32             reg;
@@ -393,6 +395,23 @@ static void musb_do_idle(unsigned long _musb)
        unsigned long   flags;
 
        spin_lock_irqsave(&musb->Lock, flags);
+
+       switch (musb->xceiv.state) {
+       case OTG_STATE_A_WAIT_BCON:
+               if ((musb->a_wait_bcon != 0)
+                       && (musb->idle_timeout == 0
+                               || time_after(jiffies, musb->idle_timeout))) {
+                       DBG(4, "Nothing connected %s, turning off VBUS\n",
+                                       otg_state_string(musb));
+                       tusb_source_power(musb, 0);
+                       musb->xceiv.state = OTG_STATE_A_IDLE;
+                       musb->is_active = 0;
+               }
+               break;
+       default:
+               break;
+       }
+
        if (!musb->is_active) {
                u32     wakeups;
 
@@ -434,12 +453,35 @@ done:
  * we don't want to treat that full speed J as a wakeup event.
  * ... peripherals must draw only suspend current after 10 msec.
  */
-void musb_platform_try_idle(struct musb *musb)
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
 {
-       if (musb->is_active)
+       unsigned long           default_timeout = jiffies + msecs_to_jiffies(3);
+       static unsigned long    last_timer = 0;
+
+       if (timeout == 0)
+               timeout = default_timeout;
+
+       if (musb->is_active) {
+               DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
                del_timer(&musb_idle_timer);
-       else
-               mod_timer(&musb_idle_timer, jiffies + msecs_to_jiffies(3));
+               last_timer = jiffies;
+               return;
+       }
+
+       if (time_after(last_timer, timeout)) {
+               if (!timer_pending(&musb_idle_timer))
+                       last_timer = timeout;
+               else {
+                       DBG(4, "Longer idle timer already pending, ignoring\n");
+                       return;
+               }
+       }
+       last_timer = timeout;
+
+       DBG(4, "%s inactive, for idle timer for %lu ms\n",
+               otg_state_string(musb),
+               (unsigned long)jiffies_to_msecs(timeout - jiffies));
+       mod_timer(&musb_idle_timer, timeout);
 }
 
 /* ticks of 60 MHz clock */
@@ -467,7 +509,6 @@ static void tusb_source_power(struct musb *musb, int is_on)
        if (is_on) {
                musb->is_active = 1;
                timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
-
                musb->xceiv.default_a = 1;
                musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
                devctl |= MGC_M_DEVCTL_SESSION;
@@ -575,10 +616,11 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
                        "otg_stat: %08x\n", otg_stat);
 }
 
-static inline void
+static inline unsigned long
 tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
 {
-       u32     otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT);
+       u32             otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT);
+       unsigned long   idle_timeout = 0;
 
        /* ID pin */
        if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) {
@@ -591,6 +633,10 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
                musb->xceiv.default_a = default_a;
                tusb_source_power(musb, default_a);
+
+               /* Don't allow idling immediately */
+               if (default_a)
+                       idle_timeout = jiffies + (HZ * 3);
        }
 
        /* VBUS state change */
@@ -621,13 +667,34 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                        }
                        DBG(2, "vbus change, %s, otg %03x\n",
                                otg_state_string(musb), otg_stat);
+                       idle_timeout = jiffies + (1 * HZ);
                        schedule_work(&musb->irq_work);
 
                } else /* A-dev state machine */ {
+                       u8      devctl;
+
                        DBG(2, "vbus change, %s, otg %03x\n",
                                otg_state_string(musb), otg_stat);
 
                        switch (musb->xceiv.state) {
+                       case OTG_STATE_A_IDLE:
+                               DBG(2, "Got SRP, turning on VBUS\n");
+                               devctl = musb_readb(musb->pRegs,
+                                                       MGC_O_HDRC_DEVCTL);
+                               devctl |= MGC_M_DEVCTL_SESSION;
+                               musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL,
+                                                       devctl);
+                               musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+
+                               /* CONNECT can wake if a_wait_bcon is set */
+                               if (musb->a_wait_bcon != 0)
+                                       musb->is_active = 0;
+                               else
+                                       musb->is_active = 1;
+
+                               idle_timeout = jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon);
+                               break;
                        case OTG_STATE_A_WAIT_VRISE:
                                /* ignore; A-session-valid < VBUS_VALID/2,
                                 * we monitor this with the timer
@@ -661,25 +728,19 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                         */
                        devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
                        if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
-                               u32     timer;
-
                                if ((devctl & MGC_M_DEVCTL_VBUS)
                                                != MGC_M_DEVCTL_VBUS) {
                                        DBG(2, "devctl %02x\n", devctl);
                                        break;
                                }
                                musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
-
-                               /* REVISIT: if nothing is connected yet,
-                                * mark controller as inactive so that
-                                * we can suspend the TUSB chip.
-                                */
-
-                               /* timeout 0 == infinite (like non-OTG hosts) */
-                               timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_BCON);
-                               if (timer)
-                                       musb_writel(base, TUSB_DEV_OTG_TIMER,
-                                                       timer);
+                               /* CONNECT can wake if a_wait_bcon is set */
+                               if (musb->a_wait_bcon != 0)
+                                       musb->is_active = 0;
+                               else
+                                       musb->is_active = 1;
+                               idle_timeout = jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon);
                        } else {
                                /* REVISIT report overcurrent to hub? */
                                ERR("vbus too slow, devctl %02x\n", devctl);
@@ -687,8 +748,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                        }
                        break;
                case OTG_STATE_A_WAIT_BCON:
-                       if (OTG_TIME_A_WAIT_BCON)
-                               tusb_source_power(musb, 0);
+                       if (musb->a_wait_bcon != 0)
+                               idle_timeout = jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon);
                        break;
                case OTG_STATE_A_SUSPEND:
                        break;
@@ -698,13 +760,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
                        break;
                }
        }
+
+       return idle_timeout;
 }
 
 static irqreturn_t tusb_interrupt(int irq, void *__hci)
 {
        struct musb     *musb = __hci;
        void __iomem    *base = musb->ctrl_base;
-       unsigned long   flags;
+       unsigned long   flags, idle_timeout = 0;
        u32             int_mask, int_src;
 
        spin_lock_irqsave(&musb->Lock, flags);
@@ -753,11 +817,14 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci)
                // REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS
        }
 
+       if (int_src & TUSB_INT_SRC_USB_IP_CONN)
+               del_timer(&musb_idle_timer);
+
        /* OTG state change reports (annoyingly) not issued by Mentor core */
        if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
                                | TUSB_INT_SRC_OTG_TIMEOUT
                                | TUSB_INT_SRC_ID_STATUS_CHNG))
-               tusb_otg_ints(musb, int_src, base);
+               idle_timeout = tusb_otg_ints(musb, int_src, base);
 
        /* TX dma callback must be handled here, RX dma callback is
         * handled in tusb_omap_dma_cb.
@@ -799,7 +866,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci)
        musb_writel(base, TUSB_INT_SRC_CLEAR,
                int_src & ~TUSB_INT_MASK_RESERVED_BITS);
 
-       musb_platform_try_idle(musb);
+       musb_platform_try_idle(musb, idle_timeout);
 
        musb_writel(base, TUSB_INT_MASK, int_mask);
        spin_unlock_irqrestore(&musb->Lock, flags);
index 6a65f4fd1c07f23fd91f4e059b55e32199fdf93d..5b1163b031c9203168154817fb76de8c66a60235 100644 (file)
@@ -61,10 +61,20 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
         */
        power = musb_readb(pBase, MGC_O_HDRC_POWER);
        if (bSuspend) {
+               int retries = 10000;
+
                power &= ~MGC_M_POWER_RESUME;
                power |= MGC_M_POWER_SUSPENDM;
                musb_writeb(pBase, MGC_O_HDRC_POWER, power);
 
+               /* Needed for OPT A tests */
+               power = musb_readb(pBase, MGC_O_HDRC_POWER);
+               while (power & MGC_M_POWER_SUSPENDM) {
+                       power = musb_readb(pBase, MGC_O_HDRC_POWER);
+                       if (retries-- < 1)
+                               break;
+               }
+
                DBG(3, "Root port suspended, power %02x\n", power);
 
                musb->port1_status |= USB_PORT_STAT_SUSPEND;
@@ -73,7 +83,7 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
                        musb->xceiv.state = OTG_STATE_A_SUSPEND;
                        musb->is_active = is_otg_enabled(musb)
                                        && musb->xceiv.host->b_hnp_enable;
-                       musb_platform_try_idle(musb);
+                       musb_platform_try_idle(musb, 0);
                        break;
                case OTG_STATE_B_HOST:
                        musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
@@ -362,7 +372,11 @@ int musb_hub_control(
                                temp = MGC_M_TEST_FORCE_HOST
                                        | MGC_M_TEST_FORCE_HS;
 
-                               /* FIXME and enable a session too */
+                               musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, MGC_M_DEVCTL_SESSION);
+                               break;
+                       case 6:
+                               pr_debug("TEST_FIFO_ACCESS\n");
+                               temp = MGC_M_TEST_FIFO_ACCESS;
                                break;
                        default:
                                goto error;