Maemo patchset 20101501+0m5
[h-e-n] / drivers / usb / musb / musb_core.c
index 5280dba..6adab83 100644 (file)
 #include "davinci.h"
 #endif
 
+static struct musb *the_musb;
+static struct musb_ctx ctx;
 
+#ifndef CONFIG_MUSB_PIO_ONLY
+static int __initdata use_dma = 1;
+#else
+static int __initdata use_dma;
+#endif
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
 
-unsigned musb_debug;
-module_param(musb_debug, uint, S_IRUGO | S_IWUSR);
+unsigned musb_debug = 0;
+module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug message level. Default = 0");
 
 #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
@@ -133,6 +142,187 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
 
+static inline int musb_verify_charger(void __iomem *addr)
+{
+       u8 r, ret = 0;
+
+       /* Reset the transceiver */
+       r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+       r |= ISP1704_FUNC_CTRL_RESET;
+       musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+       msleep(1);
+
+       /* Set normal mode */
+       r &= ~(ISP1704_FUNC_CTRL_RESET | (3 << ISP1704_FUNC_CTRL_OPMODE));
+       musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+
+       /* Clear the DP and DM pull-down bits */
+       r = musb_ulpi_readb(addr, ISP1704_OTG_CTRL);
+       r &= ~(ISP1704_OTG_CTRL_DP_PULLDOWN | ISP1704_OTG_CTRL_DM_PULLDOWN);
+       musb_ulpi_writeb(addr, ISP1704_OTG_CTRL, r);
+
+       /* Enable strong pull-up on DP (1.5K) and reset */
+       r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+       r |= ISP1704_FUNC_CTRL_TERMSELECT | ISP1704_FUNC_CTRL_RESET;
+       musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+       msleep(1);
+
+       /* Read the line state */
+       if (musb_ulpi_readb(addr, ISP1704_DEBUG)) {
+               /* Is it a charger or PS2 connection */
+
+               /* Enable weak pull-up resistor on DP */
+               r = musb_ulpi_readb(addr, ISP1704_PWR_CTRL);
+               r |= ISP1704_PWR_CTRL_DP_WKPU_EN;
+               musb_ulpi_writeb(addr, ISP1704_PWR_CTRL, r);
+
+               /* Disable strong pull-up on DP (1.5K) */
+               r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+               r &= ~ISP1704_FUNC_CTRL_TERMSELECT;
+               musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+
+               /* Enable weak pull-down resistor on DM */
+               r = musb_ulpi_readb(addr, ISP1704_OTG_CTRL);
+               r |= ISP1704_OTG_CTRL_DM_PULLDOWN;
+               musb_ulpi_writeb(addr, ISP1704_OTG_CTRL, r);
+
+               /* It's a charger if the line states are clear */
+               if (!(musb_ulpi_readb(addr, ISP1704_DEBUG)))
+                       ret = 1;
+
+               /* Disable weak pull-up resistor on DP */
+               r = musb_ulpi_readb(addr, ISP1704_PWR_CTRL);
+               r &= ~ISP1704_PWR_CTRL_DP_WKPU_EN;
+               musb_ulpi_writeb(addr, ISP1704_PWR_CTRL, r);
+       } else {
+               ret = 1;
+
+               /* Disable strong pull-up on DP (1.5K) */
+               r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+               r &= ~ISP1704_FUNC_CTRL_TERMSELECT;
+               musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+       }
+
+       return ret;
+}
+
+/* Bad connections with the charger may lead into the transceiver
+ * thinking that a device was just connected. We can wait for 5 ms to
+ * ensure that these cases will generate SUSPEND interrupt and not
+ * RESET. Reading and writing to the transceiver may still cause
+ * RESET interrupts. We mask out RESET/RESUME interrupts to
+ * recover from this.
+ */
+static int check_charger;
+static int musb_charger_detect(struct musb *musb)
+{
+       unsigned long   timeout;
+
+       u8              vdat = 0;
+       u8              r;
+
+       msleep(5);
+
+       /* Using ulpi with musb is quite tricky. The following code
+        * was written based on the ulpi application note.
+        *
+        * The order of reads and writes and quite important, don't
+        * change it unless you really know what you're doing
+        */
+
+       switch(musb->xceiv->state) {
+               case OTG_STATE_B_IDLE:
+                       /* we always reset transceiver */
+                       check_charger = 1;
+
+                       /* HACK: ULPI tends to get stuck when booting with
+                        * the cable connected
+                        */
+                       r = musb_readb(musb->mregs, MUSB_DEVCTL);
+                       if ((r & MUSB_DEVCTL_VBUS)
+                                       == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+                               musb_save_ctx_and_suspend(&musb->g, 0);
+                               musb_restore_ctx_and_resume(&musb->g);
+                               if (musb->board && musb->board->set_pm_limits)
+                                       musb->board->set_pm_limits(
+                                                       musb->controller, 1);
+                       }
+
+                       /* disable RESET and RESUME interrupts */
+                       r = musb_readb(musb->mregs, MUSB_INTRUSBE);
+                       r &= ~(MUSB_INTR_RESUME | MUSB_INTR_RESET);
+                       musb_writeb(musb->mregs, MUSB_INTRUSBE, r);
+
+                       if (musb->board && musb->board->xceiv_reset)
+                               musb->board->xceiv_reset();
+
+                       /* then we resume to sync with controller */
+                       r = musb_readb(musb->mregs, MUSB_POWER);
+                       musb_writeb(musb->mregs, MUSB_POWER,
+                                       r | MUSB_POWER_RESUME);
+                       msleep(10);
+                       musb_writeb(musb->mregs, MUSB_POWER,
+                                       r & ~MUSB_POWER_RESUME);
+
+                       /* now we set SW control bit in PWR_CTRL register */
+                       musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL,
+                                       ISP1704_PWR_CTRL_SWCTRL);
+
+                       r = musb_ulpi_readb(musb->mregs, ISP1704_PWR_CTRL);
+                       r |= (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN);
+
+                       /* and finally enable manual charger detection */
+                       musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL, r);
+                       msleep(10);
+
+                       timeout = jiffies + msecs_to_jiffies(300);
+                       while (!time_after(jiffies, timeout)) {
+                               /* Check if there is a charger */
+                               vdat = !!(musb_ulpi_readb(musb->mregs, ISP1704_PWR_CTRL)
+                                                       & ISP1704_PWR_CTRL_VDAT_DET);
+                               if (vdat)
+                                       break;
+                       }
+                       if (vdat)
+                               vdat = musb_verify_charger(musb->mregs);
+
+                       r &= ~ISP1704_PWR_CTRL_DPVSRC_EN;
+
+                       /* Clear DPVSRC_EN, otherwise usb communication doesn't work */
+                       musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL, r);
+                       break;
+
+               default:
+                       vdat = 0;
+                       break;
+       }
+
+       if (vdat) {
+               /* REVISIT: This code works only with dedicated chargers!
+                * When support for HOST/HUB chargers is added, don't
+                * forget this.
+                */
+               musb_stop(musb);
+               /* Regulators off */
+               otg_set_suspend(musb->xceiv, 1);
+               musb->is_charger = 1;
+       } else {
+               /* enable interrupts */
+               musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe);
+
+               /* Make sure the communication starts normally */
+               r = musb_readb(musb->mregs, MUSB_POWER);
+               musb_writeb(musb->mregs, MUSB_POWER,
+                               r | MUSB_POWER_RESUME);
+               msleep(10);
+               musb_writeb(musb->mregs, MUSB_POWER,
+                               r & ~MUSB_POWER_RESUME);
+       }
+
+       check_charger = 0;
+
+       return vdat;
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -148,7 +338,8 @@ static inline struct musb *dev_to_musb(struct device *dev)
 
 /*-------------------------------------------------------------------------*/
 
-#ifndef CONFIG_USB_TUSB6010
+#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN)
+
 /*
  * Load an endpoint's FIFO
  */
@@ -266,7 +457,7 @@ void musb_load_testpacket(struct musb *musb)
 
 const char *otg_state_string(struct musb *musb)
 {
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_IDLE:          return "a_idle";
        case OTG_STATE_A_WAIT_VRISE:    return "a_wait_vrise";
        case OTG_STATE_A_WAIT_BCON:     return "a_wait_bcon";
@@ -301,11 +492,11 @@ void musb_otg_timer_func(unsigned long data)
        unsigned long   flags;
 
        spin_lock_irqsave(&musb->lock, flags);
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_B_WAIT_ACON:
                DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n");
                musb_g_disconnect(musb);
-               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                musb->is_active = 0;
                break;
        case OTG_STATE_A_WAIT_BCON:
@@ -330,20 +521,20 @@ void musb_hnp_stop(struct musb *musb)
        void __iomem    *mbase = musb->mregs;
        u8      reg;
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_PERIPHERAL:
        case OTG_STATE_A_WAIT_VFALL:
        case OTG_STATE_A_WAIT_BCON:
                DBG(1, "HNP: Switching back to A-host\n");
                musb_g_disconnect(musb);
-               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
                MUSB_HST_MODE(musb);
                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;
+               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                MUSB_DEV_MODE(musb);
                reg = musb_readb(mbase, MUSB_POWER);
                reg |= MUSB_POWER_SUSPENDM;
@@ -378,15 +569,12 @@ void musb_hnp_stop(struct musb *musb)
  * @param power
  */
 
-#define STAGE0_MASK (MUSB_INTR_RESUME | MUSB_INTR_SESSREQ \
-               | MUSB_INTR_VBUSERROR | MUSB_INTR_CONNECT \
-               | MUSB_INTR_RESET)
-
 static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                u8 devctl, u8 power)
 {
        irqreturn_t handled = IRQ_NONE;
        void __iomem *mbase = musb->mregs;
+       u8 r;
 
        DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
                int_usb);
@@ -401,7 +589,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 
                if (devctl & MUSB_DEVCTL_HM) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
                        case OTG_STATE_A_SUSPEND:
                                /* remote wakeup?  later, GetPortStatus
                                 * will stop RESUME signaling
@@ -424,12 +612,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                musb->rh_timer = jiffies
                                                + msecs_to_jiffies(20);
 
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                musb->is_active = 1;
                                usb_hcd_resume_root_hub(musb_to_hcd(musb));
                                break;
                        case OTG_STATE_B_WAIT_ACON:
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                musb->is_active = 1;
                                MUSB_DEV_MODE(musb);
                                break;
@@ -440,11 +628,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        }
 #endif
                } else {
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
                        case OTG_STATE_A_SUSPEND:
                                /* possibly DISCONNECT is upcoming */
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                usb_hcd_resume_root_hub(musb_to_hcd(musb));
                                break;
 #endif
@@ -486,12 +674,19 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                 *  - go through A_WAIT_VRISE
                 *  - ... to A_WAIT_BCON.
                 * a_wait_vrise_tmout triggers VBUS_ERROR transitions
+                * NOTE : Spurious SESS_REQ int's detected, which should
+                * be discarded silently.
                 */
-               musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
-               musb->ep0_stage = MUSB_EP0_START;
-               musb->xceiv.state = OTG_STATE_A_IDLE;
-               MUSB_HST_MODE(musb);
-               musb_set_vbus(musb, 1);
+               if ((devctl & MUSB_DEVCTL_VBUS)
+                   && !(devctl & MUSB_DEVCTL_BDEVICE)) {
+                       musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+                       musb->ep0_stage = MUSB_EP0_START;
+                       musb->xceiv->state = OTG_STATE_A_IDLE;
+                       MUSB_HST_MODE(musb);
+                       musb_set_vbus(musb, 1);
+               } else {
+                       DBG(5,"discarding SESSREQ INT: VBUS < SessEnd\n");
+               }
 
                handled = IRQ_HANDLED;
        }
@@ -515,7 +710,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                 * REVISIT:  do delays from lots of DEBUG_KERNEL checks
                 * make trouble here, keeping VBUS < 4.4V ?
                 */
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_A_HOST:
                        /* recovery is dicey once we've gotten past the
                         * initial stages of enumeration, but if VBUS
@@ -564,6 +759,58 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                handled = IRQ_HANDLED;
        }
 
+       if (int_usb & MUSB_INTR_SUSPEND) {
+               DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
+                               otg_state_string(musb), devctl, power);
+               handled = IRQ_HANDLED;
+
+               switch (musb->xceiv->state) {
+#ifdef CONFIG_USB_MUSB_OTG
+               case OTG_STATE_A_PERIPHERAL:
+                       /*
+                        * We cannot stop HNP here, devctl BDEVICE might be
+                        * still set.
+                        */
+                       break;
+#endif
+               case OTG_STATE_B_IDLE:
+                       if (!musb->is_active)
+                               break;
+               case OTG_STATE_B_PERIPHERAL:
+                       musb_g_suspend(musb);
+                       musb->is_active = is_otg_enabled(musb)
+                                       && musb->xceiv->gadget->b_hnp_enable;
+                       if (musb->is_active) {
+#ifdef CONFIG_USB_MUSB_OTG
+                               musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
+                               DBG(1, "HNP: Setting timer for b_ase0_brst\n");
+                               musb_otg_timer.data = (unsigned long)musb;
+                               mod_timer(&musb_otg_timer, jiffies
+                                       + msecs_to_jiffies(TB_ASE0_BRST));
+#endif
+                       }
+                       break;
+               case OTG_STATE_A_WAIT_BCON:
+                       if (musb->a_wait_bcon != 0)
+                               musb_platform_try_idle(musb, jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon));
+                       break;
+               case OTG_STATE_A_HOST:
+                       musb->xceiv->state = OTG_STATE_A_SUSPEND;
+                       musb->is_active = is_otg_enabled(musb)
+                                       && musb->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" */
+                       musb->is_active = 0;
+                       break;
+               }
+       }
+
        if (int_usb & MUSB_INTR_CONNECT) {
                struct usb_hcd *hcd = musb_to_hcd(musb);
 
@@ -601,11 +848,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                MUSB_HST_MODE(musb);
 
                /* indicate new connection to OTG machine */
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_B_PERIPHERAL:
                        if (int_usb & MUSB_INTR_SUSPEND) {
                                DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
-                               musb->xceiv.state = OTG_STATE_B_HOST;
+                               musb->xceiv->state = OTG_STATE_B_HOST;
                                hcd->self.is_b_host = 1;
                                int_usb &= ~MUSB_INTR_SUSPEND;
                        } else
@@ -613,13 +860,13 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        break;
                case OTG_STATE_B_WAIT_ACON:
                        DBG(1, "HNP: Waiting to switch to b_host state\n");
-                       musb->xceiv.state = OTG_STATE_B_HOST;
+                       musb->xceiv->state = OTG_STATE_B_HOST;
                        hcd->self.is_b_host = 1;
                        break;
                default:
                        if ((devctl & MUSB_DEVCTL_VBUS)
                                        == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                hcd->self.is_b_host = 0;
                        }
                        break;
@@ -629,10 +876,78 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
        }
 #endif /* CONFIG_USB_MUSB_HDRC_HCD */
 
+       if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
+               DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
+                               otg_state_string(musb),
+                               MUSB_MODE(musb), devctl);
+               handled = IRQ_HANDLED;
+
+               switch (musb->xceiv->state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+               case OTG_STATE_A_HOST:
+               case OTG_STATE_A_SUSPEND:
+                       usb_hcd_resume_root_hub(musb_to_hcd(musb));
+                       musb_root_disconnect(musb);
+                       if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
+                               musb_platform_try_idle(musb, jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon));
+                       break;
+#endif /* HOST */
+#ifdef CONFIG_USB_MUSB_OTG
+               case OTG_STATE_B_HOST:
+                       musb_hnp_stop(musb);
+                       break;
+               case OTG_STATE_A_PERIPHERAL:
+                       musb_hnp_stop(musb);
+                       musb_root_disconnect(musb);
+                       /* FALLTHROUGH */
+               case OTG_STATE_B_WAIT_ACON:
+                       /* FALLTHROUGH */
+#endif /* OTG */
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+               case OTG_STATE_B_PERIPHERAL:
+               case OTG_STATE_B_IDLE:
+                       /* Workaround for a problem of Vbus quickly dropping
+                        * during Certification tests.
+                        *
+                        * Undo the workaround on disconnect
+                        */
+
+                       /* Disable suspend so we can write to ULPI */
+                       r = musb_readb(musb->mregs, MUSB_POWER);
+                       musb_writeb(musb->mregs, MUSB_POWER,
+                                               r & ~MUSB_POWER_ENSUSPEND);
+                       musb_ulpi_writeb(musb->mregs,
+                                               ISP1704_USB_INTFALL, 0x1f);
+                       musb_ulpi_writeb(musb->mregs,
+                                               ISP1704_USB_INTRISE, 0x1f);
+                       musb_writeb(musb->mregs, MUSB_POWER,
+                                               r | MUSB_POWER_ENSUSPEND);
+
+                       musb_g_disconnect(musb);
+                       /** UGLY UGLY HACK: Windows problems with multiple
+                        * configurations.
+                        *
+                        * This is necessary to notify gadget driver this was
+                        * a physical disconnection and not only a port reset.
+                        */
+                       if (musb->gadget_driver->vbus_disconnect)
+                               musb->gadget_driver->vbus_disconnect(&musb->g);
+
+                       break;
+#endif /* GADGET */
+               default:
+                       WARNING("unhandled DISCONNECT transition (%s)\n",
+                               otg_state_string(musb));
+                       break;
+               }
+       }
+
        /* mentor saves a bit: bus reset and babble share the same irq.
         * only host sees babble; only peripheral sees bus reset.
         */
        if (int_usb & MUSB_INTR_RESET) {
+               handled = IRQ_HANDLED;
                if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
                        /*
                         * Looks like non-HS BABBLE can be ignored, but
@@ -645,11 +960,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                DBG(1, "BABBLE devctl: %02x\n", devctl);
                        else {
                                ERR("Stopping host session -- babble\n");
-                               musb_writeb(mbase, MUSB_DEVCTL, 0);
+                               musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
                        }
                } else if (is_peripheral_capable()) {
                        DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_OTG
                        case OTG_STATE_A_SUSPEND:
                                /* We need to ignore disconnect on suspend
@@ -672,14 +987,35 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        case OTG_STATE_B_WAIT_ACON:
                                DBG(1, "HNP: RESET (%s), to b_peripheral\n",
                                        otg_state_string(musb));
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                musb_g_reset(musb);
                                break;
 #endif
                        case OTG_STATE_B_IDLE:
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               /* Workaround the charger detection problems */
+                               if ((devctl & MUSB_DEVCTL_VBUS)
+                                       != (3 << MUSB_DEVCTL_VBUS_SHIFT))
+                                       break;
+                               if (check_charger)
+                                       break;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                /* FALLTHROUGH */
                        case OTG_STATE_B_PERIPHERAL:
+                       /* Workaround for a problem of Vbus quickly dropping
+                        * during Certification tests.
+                        *
+                        * The guess is that vbus drops due to the circuitry
+                        * for overcurrent protection and that makes transceiver
+                        * think VBUS is not valid anymore. Transceiver will
+                        * then send an RXCMD to PHY which will cause it to
+                        * disconnect from the bus even though we disable the
+                        * DISCONNECT IRQ
+                        */
+                               musb_ulpi_writeb(musb->mregs,
+                                                ISP1704_USB_INTFALL, 0x1d);
+                               musb_ulpi_writeb(musb->mregs,
+                                                ISP1704_USB_INTRISE, 0x1d);
+
                                musb_g_reset(musb);
                                break;
                        default:
@@ -687,29 +1023,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                        otg_state_string(musb));
                        }
                }
-
-               handled = IRQ_HANDLED;
        }
-       schedule_work(&musb->irq_work);
-
-       return handled;
-}
-
-/*
- * Interrupt Service Routine to record USB "global" interrupts.
- * Since these do not happen often and signify things of
- * paramount importance, it seems OK to check them individually;
- * the order of the tests is specified in the manual
- *
- * @param musb instance pointer
- * @param int_usb register contents
- * @param devctl
- * @param power
- */
-static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
-                               u8 devctl, u8 power)
-{
-       irqreturn_t handled = IRQ_NONE;
 
 #if 0
 /* REVISIT ... this would be for multiplexing periodic endpoints, or
@@ -756,98 +1070,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
        }
 #endif
 
-       if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
-               DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
-                               otg_state_string(musb),
-                               MUSB_MODE(musb), devctl);
-               handled = IRQ_HANDLED;
-
-               switch (musb->xceiv.state) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-               case OTG_STATE_A_HOST:
-               case OTG_STATE_A_SUSPEND:
-                       musb_root_disconnect(musb);
-                       if (musb->a_wait_bcon != 0)
-                               musb_platform_try_idle(musb, jiffies
-                                       + msecs_to_jiffies(musb->a_wait_bcon));
-                       break;
-#endif /* HOST */
-#ifdef CONFIG_USB_MUSB_OTG
-               case OTG_STATE_B_HOST:
-                       musb_hnp_stop(musb);
-                       break;
-               case OTG_STATE_A_PERIPHERAL:
-                       musb_hnp_stop(musb);
-                       musb_root_disconnect(musb);
-                       /* FALLTHROUGH */
-               case OTG_STATE_B_WAIT_ACON:
-                       /* FALLTHROUGH */
-#endif /* OTG */
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-               case OTG_STATE_B_PERIPHERAL:
-               case OTG_STATE_B_IDLE:
-                       musb_g_disconnect(musb);
-                       break;
-#endif /* GADGET */
-               default:
-                       WARNING("unhandled DISCONNECT transition (%s)\n",
-                               otg_state_string(musb));
-                       break;
-               }
-
-               schedule_work(&musb->irq_work);
-       }
-
-       if (int_usb & MUSB_INTR_SUSPEND) {
-               DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
-                               otg_state_string(musb), devctl, power);
-               handled = IRQ_HANDLED;
-
-               switch (musb->xceiv.state) {
-#ifdef CONFIG_USB_MUSB_OTG
-               case OTG_STATE_A_PERIPHERAL:
-                       /*
-                        * We cannot stop HNP here, devctl BDEVICE might be
-                        * still set.
-                        */
-                       break;
-#endif
-               case OTG_STATE_B_PERIPHERAL:
-                       musb_g_suspend(musb);
-                       musb->is_active = is_otg_enabled(musb)
-                                       && musb->xceiv.gadget->b_hnp_enable;
-                       if (musb->is_active) {
-#ifdef CONFIG_USB_MUSB_OTG
-                               musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
-                               DBG(1, "HNP: Setting timer for b_ase0_brst\n");
-                               musb_otg_timer.data = (unsigned long)musb;
-                               mod_timer(&musb_otg_timer, jiffies
-                                       + msecs_to_jiffies(TB_ASE0_BRST));
-#endif
-                       }
-                       break;
-               case OTG_STATE_A_WAIT_BCON:
-                       if (musb->a_wait_bcon != 0)
-                               musb_platform_try_idle(musb, jiffies
-                                       + msecs_to_jiffies(musb->a_wait_bcon));
-                       break;
-               case OTG_STATE_A_HOST:
-                       musb->xceiv.state = OTG_STATE_A_SUSPEND;
-                       musb->is_active = is_otg_enabled(musb)
-                                       && musb->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" */
-                       musb->is_active = 0;
-                       break;
-               }
-               schedule_work(&musb->irq_work);
-       }
-
+       schedule_work(&musb->irq_work);
 
        return handled;
 }
@@ -861,9 +1084,16 @@ void musb_start(struct musb *musb)
 {
        void __iomem    *regs = musb->mregs;
        u8              devctl = musb_readb(regs, MUSB_DEVCTL);
+       u8              power;
 
        DBG(2, "<== devctl %02x\n", devctl);
 
+       /* Ensure the clocks are on */
+       if (musb->set_clock)
+               musb->set_clock(musb->clock, 1);
+       else
+               clk_enable(musb->clock);
+
        /*  Set INT enable registers, enable interrupts */
        musb_writew(regs, MUSB_INTRTXE, musb->epmask);
        musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe);
@@ -871,13 +1101,15 @@ void musb_start(struct musb *musb)
 
        musb_writeb(regs, MUSB_TESTMODE, 0);
 
+       power = MUSB_POWER_ISOUPDATE | MUSB_POWER_SOFTCONN
+               | MUSB_POWER_HSENAB;
+
+       /* ENSUSPEND wedges tusb */
+       if (musb->suspendm)
+               power |= MUSB_POWER_ENSUSPEND;
+
        /* put into basic highspeed mode and start session */
-       musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
-                                               | MUSB_POWER_SOFTCONN
-                                               | MUSB_POWER_HSENAB
-                                               /* ENSUSPEND wedges tusb */
-                                               /* | MUSB_POWER_ENSUSPEND */
-                                               );
+       musb_writeb(regs, MUSB_POWER, power);
 
        musb->is_active = 0;
        devctl = musb_readb(regs, MUSB_DEVCTL);
@@ -912,6 +1144,12 @@ static void musb_generic_disable(struct musb *musb)
        void __iomem    *mbase = musb->mregs;
        u16     temp;
 
+       /* Clocks need to be turned on with OFF-mode */
+       if (musb->set_clock)
+               musb->set_clock(musb->clock, 1);
+       else
+               clk_enable(musb->clock);
+
        /* disable interrupts */
        musb_writeb(mbase, MUSB_INTRUSBE, 0);
        musb_writew(mbase, MUSB_INTRTXE, 0);
@@ -927,6 +1165,15 @@ static void musb_generic_disable(struct musb *musb)
 
 }
 
+void musb_emergency_stop(void)
+{
+       if (!the_musb)
+               return;
+
+       musb_stop(the_musb);
+}
+EXPORT_SYMBOL_GPL(musb_emergency_stop);
+
 /*
  * Make the HDRC stop (disable interrupts, etc.);
  * reversible by musb_start
@@ -1063,21 +1310,58 @@ static struct fifo_cfg __initdata mode_4_cfg[] = {
 { .hw_ep_num =  7, .style = FIFO_TX,   .maxpacket = 512, },
 { .hw_ep_num =  7, .style = FIFO_RX,   .maxpacket = 512, },
 { .hw_ep_num =  8, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num =  8, .style = FIFO_RX,   .maxpacket = 512, },
+{ .hw_ep_num =  8, .style = FIFO_RX,   .maxpacket = 64, },
 { .hw_ep_num =  9, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num =  9, .style = FIFO_RX,   .maxpacket = 512, },
+{ .hw_ep_num =  9, .style = FIFO_RX,   .maxpacket = 64, },
 { .hw_ep_num = 10, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 10, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 11, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 11, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 12, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 12, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 13, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 13, .style = FIFO_RX,   .maxpacket = 512, },
+{ .hw_ep_num = 10, .style = FIFO_RX,   .maxpacket = 64, },
+{ .hw_ep_num = 11, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 11, .style = FIFO_RX,   .maxpacket = 256, },
+{ .hw_ep_num = 12, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 12, .style = FIFO_RX,   .maxpacket = 256, },
+{ .hw_ep_num = 13, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 13, .style = FIFO_RX,   .maxpacket = 4096, },
 { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
 { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
 };
 
+/* mode 5 - fits in 16KB */
+static struct fifo_cfg __initdata mode_5_cfg[] = {
+/* phonet or mass storage */
+{ .hw_ep_num =  1, .style = FIFO_TX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  1, .style = FIFO_RX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* obex 1 */
+{ .hw_ep_num =  2, .style = FIFO_TX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  2, .style = FIFO_RX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* obex 2 */
+{ .hw_ep_num =  3, .style = FIFO_TX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  3, .style = FIFO_RX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* acm 1 */
+{ .hw_ep_num =  4, .style = FIFO_TX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  4, .style = FIFO_RX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  5, .style = FIFO_TX,   .maxpacket = 16, },
+
+/* ecm */
+{ .hw_ep_num =  6, .style = FIFO_TX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  5, .style = FIFO_RX,   .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num =  7, .style = FIFO_TX,   .maxpacket = 16, },
+
+/* extras */
+{ .hw_ep_num =  8, .style = FIFO_TX,   .maxpacket = 512, },
+{ .hw_ep_num =  6, .style = FIFO_RX,   .maxpacket = 512, },
+
+{ .hw_ep_num =  9, .style = FIFO_TX,   .maxpacket = 512, },
+{ .hw_ep_num =  7, .style = FIFO_RX,   .maxpacket = 512, },
+
+{ .hw_ep_num =  10, .style = FIFO_TX,  .maxpacket = 512, },
+{ .hw_ep_num =  8, .style = FIFO_RX,   .maxpacket = 512, },
+
+{ .hw_ep_num =  11, .style = FIFO_TX,  .maxpacket = 512, },
+{ .hw_ep_num =  9, .style = FIFO_RX,   .maxpacket = 512, },
+};
 
 /*
  * configure a fifo; for non-shared endpoints, this may be called
@@ -1124,28 +1408,38 @@ fifo_setup(struct musb *musb, struct musb_hw_ep  *hw_ep,
 #endif
        switch (cfg->style) {
        case FIFO_TX:
-               musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
-               musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+               musb_write_txfifosz(mbase, c_size);
+               musb_write_txfifoadd(mbase, c_off);
                hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
                hw_ep->max_packet_sz_tx = maxpacket;
+               ctx.txfifosz[hw_ep->epnum] = c_size;
+               ctx.txfifoadd[hw_ep->epnum] = c_off;
                break;
        case FIFO_RX:
-               musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
-               musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+               musb_write_rxfifosz(mbase, c_size);
+               musb_write_rxfifoadd(mbase, c_off);
                hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
                hw_ep->max_packet_sz_rx = maxpacket;
+               ctx.rxfifosz[hw_ep->epnum] = c_size;
+               ctx.rxfifoadd[hw_ep->epnum] = c_off;
                break;
        case FIFO_RXTX:
-               musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
-               musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+               musb_write_txfifosz(mbase, c_size);
+               musb_write_txfifoadd(mbase, c_off);
                hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
                hw_ep->max_packet_sz_rx = maxpacket;
 
-               musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
-               musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+               musb_write_rxfifosz(mbase, c_size);
+               musb_write_rxfifoadd(mbase, c_off);
                hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
                hw_ep->max_packet_sz_tx = maxpacket;
 
+               /* Save the context of endpoints. */
+               ctx.rxfifosz[hw_ep->epnum] = c_size;
+               ctx.txfifosz[hw_ep->epnum] = c_size;
+               ctx.txfifoadd[hw_ep->epnum] = c_off;
+               ctx.rxfifoadd[hw_ep->epnum] = c_off;
+
                hw_ep->is_shared_fifo = true;
                break;
        }
@@ -1169,6 +1463,9 @@ static int __init ep_config_from_table(struct musb *musb)
        int                     offset;
        struct musb_hw_ep       *hw_ep = musb->endpoints;
 
+       if (machine_is_nokia_rx51())
+               fifo_mode = 5;
+
        switch (fifo_mode) {
        default:
                fifo_mode = 0;
@@ -1193,6 +1490,10 @@ static int __init ep_config_from_table(struct musb *musb)
                cfg = mode_4_cfg;
                n = ARRAY_SIZE(mode_4_cfg);
                break;
+       case 5:
+               cfg = mode_5_cfg;
+               n = ARRAY_SIZE(mode_5_cfg);
+               break;
        }
 
        printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
@@ -1212,7 +1513,7 @@ static int __init ep_config_from_table(struct musb *musb)
                if (epn >= musb->config->num_eps) {
                        pr_debug("%s: invalid ep %d\n",
                                        musb_driver_name, epn);
-                       continue;
+                       return -EINVAL;
                }
                offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
                if (offset < 0) {
@@ -1246,9 +1547,10 @@ static int __init ep_config_from_table(struct musb *musb)
  */
 static int __init ep_config_from_hw(struct musb *musb)
 {
-       u8 epnum = 0, reg;
+       u8 epnum = 0;
        struct musb_hw_ep *hw_ep;
        void *mbase = musb->mregs;
+       int ret = 0;
 
        DBG(2, "<== static silicon ep config\n");
 
@@ -1258,26 +1560,9 @@ static int __init ep_config_from_hw(struct musb *musb)
                musb_ep_select(mbase, epnum);
                hw_ep = musb->endpoints + epnum;
 
-               /* read from core using indexed model */
-               reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE);
-               if (!reg) {
-                       /* 0's returned when no more endpoints */
+               ret = musb_read_fifosize(musb, hw_ep, epnum);
+               if (ret < 0)
                        break;
-               }
-               musb->nr_endpoints++;
-               musb->epmask |= (1 << epnum);
-
-               hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
-
-               /* shared TX/RX FIFO? */
-               if ((reg & 0xf0) == 0xf0) {
-                       hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
-                       hw_ep->is_shared_fifo = true;
-                       continue;
-               } else {
-                       hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
-                       hw_ep->is_shared_fifo = false;
-               }
 
                /* FIXME set up hw_ep->{rx,tx}_double_buffered */
 
@@ -1326,7 +1611,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
 
        /* log core options (read using indexed model) */
        musb_ep_select(mbase, 0);
-       reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
+       reg = musb_read_configdata(mbase);
 
        strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
        if (reg & MUSB_CONFIGDATA_DYNFIFO)
@@ -1391,7 +1676,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
        }
 
        /* log release info */
-       hwvers = musb_readw(mbase, MUSB_HWVERS);
+       hwvers = musb_read_hwvers(mbase);
        rev_major = (hwvers >> 10) & 0x1f;
        rev_minor = hwvers & 0x3ff;
        snprintf(aRevision, 32, "%d.%d%s", rev_major,
@@ -1400,8 +1685,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
                        musb_driver_name, type, aRevision, aDate);
 
        /* configure ep0 */
-       musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
-       musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
+       musb_configure_ep0(musb);
 
        /* discover endpoint configuration */
        musb->nr_endpoints = 1;
@@ -1445,7 +1729,10 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
 
                hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
-               hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase;
+               /* init list of in and out qhs */
+               INIT_LIST_HEAD(&hw_ep->in_list);
+               INIT_LIST_HEAD(&hw_ep->out_list);
+               hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
                hw_ep->rx_reinit = 1;
                hw_ep->tx_reinit = 1;
 #endif
@@ -1491,8 +1778,8 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
        musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
        musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
 
-       if (musb->int_usb || musb->int_tx || musb->int_rx)
-               retval = musb_interrupt(musb);
+       while (musb->int_usb || musb->int_tx || musb->int_rx)
+               retval |= musb_interrupt(musb);
 
        spin_unlock_irqrestore(&musb->lock, flags);
 
@@ -1519,7 +1806,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
 irqreturn_t musb_interrupt(struct musb *musb)
 {
        irqreturn_t     retval = IRQ_NONE;
-       u8              devctl, power;
+       u8              devctl, power, int_usb;
        int             ep_num;
        u32             reg;
 
@@ -1530,80 +1817,85 @@ irqreturn_t musb_interrupt(struct musb *musb)
                (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
                musb->int_usb, musb->int_tx, musb->int_rx);
 
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       if (is_otg_enabled(musb)|| is_peripheral_enabled(musb))
+               if (!musb->gadget_driver) {
+                       DBG(5, "No gadget driver loaded\n");
+                       musb->int_usb = 0;
+                       musb->int_tx = 0;
+                       musb->int_rx = 0;
+                       return IRQ_HANDLED;
+               }
+#endif
+
        /* the core can interrupt us for multiple reasons; docs have
         * a generic interrupt flowchart to follow
         */
-       if (musb->int_usb & STAGE0_MASK)
-               retval |= musb_stage0_irq(musb, musb->int_usb,
-                               devctl, power);
+       int_usb = musb->int_usb;
+       musb->int_usb = 0;
+       int_usb &= ~MUSB_INTR_SOF;
+       if (int_usb)
+               retval |= musb_stage0_irq(musb, int_usb, devctl, power);
 
        /* "stage 1" is handling endpoint irqs */
 
        /* handle endpoint 0 first */
        if (musb->int_tx & 1) {
+               musb->int_tx &= ~1;
                if (devctl & MUSB_DEVCTL_HM)
                        retval |= musb_h_ep0_irq(musb);
                else
                        retval |= musb_g_ep0_irq(musb);
        }
 
-       /* RX on endpoints 1-15 */
-       reg = musb->int_rx >> 1;
+       /* TX on endpoints 1-15 */
+       reg = musb->int_tx >> 1;
+       musb->int_tx = 0;
        ep_num = 1;
        while (reg) {
                if (reg & 1) {
                        /* musb_ep_select(musb->mregs, ep_num); */
-                       /* REVISIT just retval = ep->rx_irq(...) */
+                       /* REVISIT just retval |= ep->tx_irq(...) */
                        retval = IRQ_HANDLED;
                        if (devctl & MUSB_DEVCTL_HM) {
                                if (is_host_capable())
-                                       musb_host_rx(musb, ep_num);
+                                       musb_host_tx(musb, ep_num);
                        } else {
                                if (is_peripheral_capable())
-                                       musb_g_rx(musb, ep_num);
+                                       musb_g_tx(musb, ep_num);
                        }
                }
-
                reg >>= 1;
                ep_num++;
        }
 
-       /* TX on endpoints 1-15 */
-       reg = musb->int_tx >> 1;
+       /* RX on endpoints 1-15 */
+       reg = musb->int_rx >> 1;
+       musb->int_rx = 0;
        ep_num = 1;
        while (reg) {
                if (reg & 1) {
                        /* musb_ep_select(musb->mregs, ep_num); */
-                       /* REVISIT just retval |= ep->tx_irq(...) */
+                       /* REVISIT just retval = ep->rx_irq(...) */
                        retval = IRQ_HANDLED;
                        if (devctl & MUSB_DEVCTL_HM) {
                                if (is_host_capable())
-                                       musb_host_tx(musb, ep_num);
+                                       musb_host_rx(musb, ep_num);
                        } else {
                                if (is_peripheral_capable())
-                                       musb_g_tx(musb, ep_num);
+                                       musb_g_rx(musb, ep_num, false);
                        }
                }
+
                reg >>= 1;
                ep_num++;
        }
 
-       /* finish handling "global" interrupts after handling fifos */
-       if (musb->int_usb)
-               retval |= musb_stage2_irq(musb,
-                               musb->int_usb, devctl, power);
-
        return retval;
 }
 
 
 #ifndef CONFIG_MUSB_PIO_ONLY
-static int __initdata use_dma = 1;
-
-/* "modprobe ... use_dma=0" etc */
-module_param(use_dma, bool, 0);
-MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
-
 void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
 {
        u8      devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
@@ -1612,7 +1904,7 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
 
        if (!epnum) {
 #ifndef CONFIG_USB_TUSB_OMAP_DMA
-               if (!is_cppi_enabled()) {
+               if (!cppi_ti_dma()) {
                        /* endpoint 0 */
                        if (devctl & MUSB_DEVCTL_HM)
                                musb_h_ep0_irq(musb);
@@ -1637,14 +1929,11 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
                                        musb_host_rx(musb, epnum);
                        } else {
                                if (is_peripheral_capable())
-                                       musb_g_rx(musb, epnum);
+                                       musb_g_rx(musb, epnum, true);
                        }
                }
        }
 }
-
-#else
-#define use_dma                        0
 #endif
 
 /*-------------------------------------------------------------------------*/
@@ -1652,37 +1941,105 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
 #ifdef CONFIG_SYSFS
 
 static ssize_t
+musb_charger_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct musb *musb = dev_to_musb(dev);
+
+       return sprintf(buf, "%d\n", (musb->is_charger ?
+                       musb->is_charger : musb_charger_detect(musb)));
+}
+static DEVICE_ATTR(charger, 0444, musb_charger_show, NULL);
+
+static ssize_t
+musb_amp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct musb *musb = dev_to_musb(dev);
+
+       return sprintf(buf, "%d\n", musb->power_draw);
+}
+static DEVICE_ATTR(mA, 0444, musb_amp_show, NULL);
+
+static ssize_t
 musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct musb *musb = dev_to_musb(dev);
-       unsigned long flags;
        int ret = -EINVAL;
 
-       spin_lock_irqsave(&musb->lock, flags);
+       mutex_lock(&musb->mutex);
        ret = sprintf(buf, "%s\n", otg_state_string(musb));
+       mutex_unlock(&musb->mutex);
+
+       return ret;
+}
+
+static ssize_t
+musb_connect_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct musb     *musb = dev_to_musb(dev);
+       unsigned long   flags;
+       int             ret = -EINVAL;
+
+       spin_lock_irqsave(&musb->lock, flags);
+       ret = sprintf(buf, "%d\n", musb->softconnect);
        spin_unlock_irqrestore(&musb->lock, flags);
 
        return ret;
 }
 
 static ssize_t
-musb_mode_store(struct device *dev, struct device_attribute *attr,
+musb_connect_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        val;
+       int             status;
+       u8              power;
+
+       status = sscanf(buf, "%u", &val);
+       if (status < 1) {
+               printk(KERN_ERR "invalid parameter, %d\n", status);
+               return -EINVAL;
+       }
 
        spin_lock_irqsave(&musb->lock, flags);
-       if (!strncmp(buf, "host", 4))
-               musb_platform_set_mode(musb, MUSB_HOST);
-       if (!strncmp(buf, "peripheral", 10))
-               musb_platform_set_mode(musb, MUSB_PERIPHERAL);
-       if (!strncmp(buf, "otg", 3))
-               musb_platform_set_mode(musb, MUSB_OTG);
+
+       power = musb_readb(musb->mregs, MUSB_POWER);
+
+       if (val)
+               power |= MUSB_POWER_SOFTCONN;
+       else
+               power &= ~MUSB_POWER_SOFTCONN;
+
+       musb->softconnect = !!val;
+       musb_writeb(musb->mregs, MUSB_POWER, power);
+
        spin_unlock_irqrestore(&musb->lock, flags);
 
        return n;
 }
+static DEVICE_ATTR(connect, 0644, musb_connect_show, musb_connect_store);
+
+static ssize_t
+musb_mode_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t n)
+{
+       struct musb     *musb = dev_to_musb(dev);
+       int             status;
+
+       mutex_lock(&musb->mutex);
+       if (sysfs_streq(buf, "host"))
+               status = musb_platform_set_mode(musb, MUSB_HOST);
+       else if (sysfs_streq(buf, "peripheral"))
+               status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
+       else if (sysfs_streq(buf, "otg"))
+               status = musb_platform_set_mode(musb, MUSB_OTG);
+       else
+               status = -EINVAL;
+       mutex_unlock(&musb->mutex);
+
+       return (status == 0) ? n : status;
+}
 static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
 
 static ssize_t
@@ -1700,7 +2057,7 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
 
        spin_lock_irqsave(&musb->lock, flags);
        musb->a_wait_bcon = val;
-       if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
+       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);
@@ -1728,6 +2085,15 @@ static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
 
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
 
+static ssize_t
+musb_suspend_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct musb *musb = dev_to_musb(dev);
+
+       return sprintf(buf, "%d\n", musb->is_suspended);
+}
+static DEVICE_ATTR(suspend, 0444, musb_suspend_show, NULL);
+
 /* Gadget drivers can't know that a host is connected so they might want
  * to start SRP, but users can.  This allows userspace to trigger SRP.
  */
@@ -1759,12 +2125,22 @@ static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
 static void musb_irq_work(struct work_struct *data)
 {
        struct musb *musb = container_of(data, struct musb, irq_work);
-       static int old_state;
+       static int old_state, old_ma, old_suspend;
 
-       if (musb->xceiv.state != old_state) {
-               old_state = musb->xceiv.state;
+       if (musb->xceiv->state != old_state) {
+               old_state = musb->xceiv->state;
                sysfs_notify(&musb->controller->kobj, NULL, "mode");
        }
+       if (musb->power_draw != old_ma) {
+               old_ma = musb->power_draw;
+               sysfs_notify(&musb->controller->kobj, NULL, "mA");
+       }
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       if (old_suspend != musb->is_suspended) {
+               old_suspend = musb->is_suspended;
+               sysfs_notify(&musb->controller->kobj, NULL, "suspend");
+       }
+#endif
 }
 
 /* --------------------------------------------------------------------------
@@ -1781,15 +2157,12 @@ allocate_instance(struct device *dev,
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
        struct usb_hcd  *hcd;
 
-       hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id);
+       hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
        if (!hcd)
                return NULL;
        /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
 
        musb = hcd_to_musb(hcd);
-       INIT_LIST_HEAD(&musb->control);
-       INIT_LIST_HEAD(&musb->in_bulk);
-       INIT_LIST_HEAD(&musb->out_bulk);
 
        hcd->uses_new_polling = 1;
 
@@ -1810,7 +2183,6 @@ allocate_instance(struct device *dev,
        for (epnum = 0, ep = musb->endpoints;
                        epnum < musb->config->num_eps;
                        epnum++, ep++) {
-
                ep->musb = musb;
                ep->epnum = epnum;
        }
@@ -1827,9 +2199,13 @@ static void musb_free(struct musb *musb)
         */
 
 #ifdef CONFIG_SYSFS
+       device_remove_file(musb->controller, &dev_attr_mA);
+       device_remove_file(musb->controller, &dev_attr_connect);
+       device_remove_file(musb->controller, &dev_attr_charger);
        device_remove_file(musb->controller, &dev_attr_mode);
        device_remove_file(musb->controller, &dev_attr_vbus);
-#ifdef CONFIG_USB_MUSB_OTG
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       device_remove_file(musb->controller, &dev_attr_suspend);
        device_remove_file(musb->controller, &dev_attr_srp);
 #endif
 #endif
@@ -1839,7 +2215,8 @@ static void musb_free(struct musb *musb)
 #endif
 
        if (musb->nIrq >= 0) {
-               disable_irq_wake(musb->nIrq);
+               if (musb->irq_wake)
+                       disable_irq_wake(musb->nIrq);
                free_irq(musb->nIrq, musb);
        }
        if (is_dma_capable() && musb->dma_controller) {
@@ -1859,7 +2236,7 @@ static void musb_free(struct musb *musb)
        }
 
 #ifdef CONFIG_USB_MUSB_OTG
-       put_device(musb->xceiv.dev);
+       put_device(musb->xceiv->dev);
 #endif
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -1920,11 +2297,16 @@ bad_config:
        if (!musb)
                return -ENOMEM;
 
+       the_musb = musb;
+
        spin_lock_init(&musb->lock);
+       mutex_init(&musb->mutex);
+       musb->board = plat->board;
        musb->board_mode = plat->mode;
        musb->board_set_power = plat->set_power;
        musb->set_clock = plat->set_clock;
        musb->min_power = plat->min_power;
+       musb->use_dma = use_dma;
 
        /* Clock usage is chip-specific ... functional clock (DaVinci,
         * OMAP2430), or PHY ref (some TUSB6010 boards).  All this core
@@ -1966,7 +2348,7 @@ bad_config:
        }
 #endif
        /* ideally this would be abstracted in platform setup */
-       if (!is_dma_capable() || !musb->dma_controller)
+       if (!musb->use_dma || !musb->dma_controller)
                dev->dma_mask = NULL;
 
        /* be sure interrupts are disabled before connecting ISR */
@@ -1984,15 +2366,19 @@ bad_config:
        INIT_WORK(&musb->irq_work, musb_irq_work);
 
        /* attach to the IRQ */
-       if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) {
+       if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
                dev_err(dev, "request_irq %d failed!\n", nIrq);
                status = -ENODEV;
                goto fail2;
        }
        musb->nIrq = nIrq;
 /* FIXME this handles wakeup irqs wrong */
-       if (enable_irq_wake(nIrq) == 0)
+       if (enable_irq_wake(nIrq) == 0) {
+               musb->irq_wake = 1;
                device_init_wakeup(dev, 1);
+       } else {
+               musb->irq_wake = 0;
+       }
 
        pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n",
                        musb_driver_name,
@@ -2014,7 +2400,7 @@ bad_config:
 
                if (musb->board_mode == MUSB_OTG)
                        hcd->self.otg_port = 1;
-               musb->xceiv.host = &hcd->self;
+               musb->xceiv->host = &hcd->self;
                hcd->power_budget = 2 * (plat->power ? : 250);
        }
 #endif                         /* CONFIG_USB_MUSB_HDRC_HCD */
@@ -2025,8 +2411,8 @@ bad_config:
         */
        if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
                MUSB_HST_MODE(musb);
-               musb->xceiv.default_a = 1;
-               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->xceiv->default_a = 1;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
 
                status = usb_add_hcd(musb_to_hcd(musb), -1, 0);
                if (status)
@@ -2041,8 +2427,8 @@ bad_config:
 
        } else /* peripheral is enabled */ {
                MUSB_DEV_MODE(musb);
-               musb->xceiv.default_a = 0;
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               musb->xceiv->default_a = 0;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
 
                status = musb_gadget_setup(musb);
                if (status)
@@ -2055,10 +2441,17 @@ bad_config:
 
        }
 
+       if (!(musb_debug_create("driver/musb_hdrc", musb)))
+               DBG(1, "could not create procfs entry\n");
+
 #ifdef CONFIG_SYSFS
+       status = device_create_file(dev, &dev_attr_mA);
+       status = device_create_file(dev, &dev_attr_connect);
+       status = device_create_file(dev, &dev_attr_charger);
        status = device_create_file(dev, &dev_attr_mode);
        status = device_create_file(dev, &dev_attr_vbus);
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       status = device_create_file(dev, &dev_attr_suspend);
        status = device_create_file(dev, &dev_attr_srp);
 #endif /* CONFIG_USB_GADGET_MUSB_HDRC */
        status = 0;
@@ -2066,13 +2459,23 @@ bad_config:
        if (status)
                goto fail2;
 
+       /* Resets the controller. Has to be done. Without this, most likely
+        * the state machine inside the transceiver doesn't get fixed properly
+        */
+       musb_save_ctx_and_suspend(&musb->g, 0);
+       musb_restore_ctx_and_resume(&musb->g);
+
        return 0;
 
 fail2:
 #ifdef CONFIG_SYSFS
+       device_remove_file(dev, &dev_attr_mA);
+       device_remove_file(dev, &dev_attr_connect);
+       device_remove_file(dev, &dev_attr_charger);
        device_remove_file(musb->controller, &dev_attr_mode);
        device_remove_file(musb->controller, &dev_attr_vbus);
-#ifdef CONFIG_USB_MUSB_OTG
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       device_remove_file(musb->controller, &dev_attr_suspend);
        device_remove_file(musb->controller, &dev_attr_srp);
 #endif
 #endif
@@ -2121,6 +2524,9 @@ static int __init musb_probe(struct platform_device *pdev)
        /* clobbered by use_dma=n */
        orig_dma_mask = dev->dma_mask;
 #endif
+       /* Store initial mask for USB interrupts */
+       ctx.intrusbe = 0xf7;
+
        return musb_init_controller(dev, irq, base);
 }
 
@@ -2135,6 +2541,7 @@ static int __devexit musb_remove(struct platform_device *pdev)
         *  - OTG mode: both roles are deactivated (or never-activated)
         */
        musb_shutdown(pdev);
+       musb_debug_delete("driver/musb_hdrc", musb);
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
        if (musb->board_mode == MUSB_HOST)
                usb_remove_hcd(musb_to_hcd(musb));
@@ -2150,6 +2557,37 @@ static int __devexit musb_remove(struct platform_device *pdev)
 
 #ifdef CONFIG_PM
 
+void musb_save_ctx(struct musb *musb)
+{
+       ctx.power = musb_readb(musb->mregs, MUSB_POWER);
+       ctx.intrtxe = musb_readw(musb->mregs, MUSB_INTRTXE);
+       ctx.intrrxe = musb_readw(musb->mregs, MUSB_INTRRXE);
+       ctx.intrusbe = musb_readb(musb->mregs, MUSB_INTRUSBE);
+       ctx.devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+}
+
+void musb_restore_ctx(struct musb *musb)
+{
+       int i;
+       musb_writeb(musb->mregs, MUSB_POWER, ctx.power);
+       musb_writew(musb->mregs, MUSB_INTRTX, 0x00);
+       musb_writew(musb->mregs, MUSB_INTRTXE, ctx.intrtxe);
+       musb_writew(musb->mregs, MUSB_INTRRX, 0x00);
+       musb_writew(musb->mregs, MUSB_INTRRXE, ctx.intrrxe);
+       musb_writeb(musb->mregs, MUSB_INTRUSB, 0x00);
+       musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe);
+       musb_writeb(musb->mregs, MUSB_DEVCTL, ctx.devctl);
+
+       /* iterate over every endpoint, select it and restore its context */
+       for (i = 0; i < musb->config->num_eps; i++) {
+               musb_writeb(musb->mregs, MUSB_INDEX, i);
+               musb_writeb(musb->mregs, MUSB_RXFIFOSZ, ctx.rxfifosz[i]);
+               musb_writeb(musb->mregs, MUSB_TXFIFOSZ, ctx.txfifosz[i]);
+               musb_writew(musb->mregs, MUSB_TXFIFOADD, ctx.txfifoadd[i]);
+               musb_writew(musb->mregs, MUSB_RXFIFOADD, ctx.rxfifoadd[i]);
+       };
+}
+
 static int musb_suspend(struct platform_device *pdev, pm_message_t message)
 {
        unsigned long   flags;
@@ -2170,10 +2608,14 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message)
                 */
        }
 
+       /* save context */
+       musb_save_ctx(musb);
+
        if (musb->set_clock)
                musb->set_clock(musb->clock, 0);
        else
                clk_disable(musb->clock);
+
        spin_unlock_irqrestore(&musb->lock, flags);
        return 0;
 }
@@ -2193,6 +2635,9 @@ static int musb_resume(struct platform_device *pdev)
        else
                clk_enable(musb->clock);
 
+       /* restore context */
+       musb_restore_ctx(musb);
+
        /* for static cmos like DaVinci, register values were preserved
         * unless for some reason the whole soc powered down and we're
         * not treating that as a whole-system restart (e.g. swsusp)