]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/musb/musb_core.c
ARM: OMAP: musb_vbus_store(): dont exit with spinlock held
[linux-2.6-omap-h63xx.git] / drivers / usb / musb / musb_core.c
index e02d65f0419e783e9560afa2498038c2b7d19c94..9a73de521a868da4a8a464d965d21c94b8bc8ea2 100644 (file)
@@ -135,7 +135,7 @@ const char *otg_state_string(struct musb *musb)
 #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
 #define DRIVER_DESC "Inventra Dual-Role USB Controller Driver"
 
-#define MUSB_VERSION_BASE "2.2a/db-0.5.2"
+#define MUSB_VERSION_BASE "6.0"
 
 #ifndef MUSB_VERSION_SUFFIX
 #define MUSB_VERSION_SUFFIX    ""
@@ -299,7 +299,7 @@ void musb_otg_timer_func(unsigned long data)
 
        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");
+               DBG(1, "HNP: B_WAIT_ACON timeout; back to B_PERIPHERAL\n");
                musb_g_disconnect(musb);
                musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
                musb->is_active = 0;
@@ -321,10 +321,11 @@ void musb_hnp_stop(struct musb *musb)
        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_root_disconnect(musb);
                musb->xceiv.state = OTG_STATE_A_IDLE;
+               MUSB_HST_MODE(musb);
                musb->is_active = 0;
                break;
        case OTG_STATE_B_HOST:
@@ -341,6 +342,14 @@ void musb_hnp_stop(struct musb *musb)
                DBG(1, "HNP: Stopping in unknown state %s\n",
                        otg_state_string(musb));
        }
+
+       /*
+        * When returning to A state after HNP, avoid hub_port_rebounce(),
+        * which cause occasional OPT A "Did not receive reset after connect"
+        * errors.
+        */
+       musb->port1_status &=
+               ~(1 << USB_PORT_FEAT_C_CONNECTION);
 }
 
 #endif
@@ -434,7 +443,8 @@ static irqreturn_t musb_stage0_irq(struct musb * musb, u8 int_usb,
                                 * not get a disconnect irq...
                                 */
                                if ((devctl & MUSB_DEVCTL_VBUS)
-                                               != (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+                                               != (3 << MUSB_DEVCTL_VBUS_SHIFT)
+                                               ) {
                                        musb->int_usb |= MUSB_INTR_DISCONNECT;
                                        musb->int_usb &= ~MUSB_INTR_SUSPEND;
                                        break;
@@ -529,7 +539,7 @@ static irqreturn_t musb_stage0_irq(struct musb * musb, u8 int_usb,
                                        s = "<AValid"; break;
                                case 2 << MUSB_DEVCTL_VBUS_SHIFT:
                                        s = "<VBusValid"; break;
-                               //case 3 << MUSB_DEVCTL_VBUS_SHIFT:
+                               /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
                                default:
                                        s = "VALID"; break;
                                }; s; }),
@@ -554,7 +564,7 @@ static irqreturn_t musb_stage0_irq(struct musb * musb, u8 int_usb,
 #ifdef CONFIG_USB_MUSB_OTG
                /* flush endpoints when transitioning from Device Mode */
                if (is_peripheral_active(musb)) {
-                       // REVISIT HNP; just force disconnect
+                       /* REVISIT HNP; just force disconnect */
                }
                musb_writew(mbase, MUSB_INTRTXE, musb->epmask);
                musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe);
@@ -582,7 +592,7 @@ static irqreturn_t musb_stage0_irq(struct musb * musb, u8 int_usb,
                switch (musb->xceiv.state) {
                case OTG_STATE_B_PERIPHERAL:
                        if (int_usb & MUSB_INTR_SUSPEND) {
-                               DBG(1, "HNP: SUSPEND and CONNECT, now b_host\n");
+                               DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
                                musb->xceiv.state = OTG_STATE_B_HOST;
                                hcd->self.is_b_host = 1;
                                int_usb &= ~MUSB_INTR_SUSPEND;
@@ -615,20 +625,33 @@ static irqreturn_t musb_stage0_irq(struct musb * musb, u8 int_usb,
                        /*
                         * Looks like non-HS BABBLE can be ignored, but
                         * HS BABBLE is an error condition. For HS the solution
-                        * is to avoid babble in the first place and fix whatever
-                        * causes BABBLE. When HS BABBLE happens we can only stop
-                        * the session.
+                        * is to avoid babble in the first place and fix what
+                        * caused BABBLE. When HS BABBLE happens we can only
+                        * stop the session.
                         */
                        if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
                                DBG(1, "BABBLE devctl: %02x\n", devctl);
                        else {
-                               ERR("Stopping host session because of babble\n");
+                               ERR("Stopping host session -- babble\n");
                                musb_writeb(mbase, MUSB_DEVCTL, 0);
                        }
                } else if (is_peripheral_capable()) {
-                       DBG(1, "BUS RESET\n");
-
-                       musb_g_reset(musb);
+                       DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
+                       switch (musb->xceiv.state) {
+                       case OTG_STATE_A_PERIPHERAL:
+                       case OTG_STATE_A_WAIT_BCON:     /* OPT TD.4.7-900ms */
+                               musb_hnp_stop(musb);
+                               break;
+                       case OTG_STATE_B_IDLE:
+                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               /* FALLTHROUGH */
+                       case OTG_STATE_B_PERIPHERAL:
+                               musb_g_reset(musb);
+                               break;
+                       default:
+                               DBG(1, "Unhandled BUS RESET as %s\n",
+                                       otg_state_string(musb));
+                       }
                        schedule_work(&musb->irq_work);
                }
 
@@ -681,8 +704,10 @@ static irqreturn_t musb_stage2_irq(struct musb * musb, u8 int_usb,
                for (epnum = 1; (epnum < musb->nr_endpoints)
                                        && (musb->epmask >= (1 << epnum));
                                epnum++, ep++) {
-                       // FIXME handle framecounter wraps (12 bits)
-                       // eliminate duplicated StartUrb logic
+                       /*
+                        * FIXME handle framecounter wraps (12 bits)
+                        * eliminate duplicated StartUrb logic
+                        */
                        if (ep->dwWaitFrame >= frame) {
                                ep->dwWaitFrame = 0;
                                printk("SOF --> periodic TX%s on %d\n",
@@ -718,6 +743,7 @@ static irqreturn_t musb_stage2_irq(struct musb * musb, u8 int_usb,
                        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:
@@ -746,7 +772,10 @@ static irqreturn_t musb_stage2_irq(struct musb * musb, u8 int_usb,
                switch (musb->xceiv.state) {
 #ifdef CONFIG_USB_MUSB_OTG
                case OTG_STATE_A_PERIPHERAL:
-                       musb_hnp_stop(musb);
+                       /*
+                        * We cannot stop HNP here, devctl BDEVICE might be
+                        * still set.
+                        */
                        break;
 #endif
                case OTG_STATE_B_PERIPHERAL:
@@ -812,7 +841,7 @@ void musb_start(struct musb *musb)
                                                | MUSB_POWER_SOFTCONN
                                                | MUSB_POWER_HSENAB
                                                /* ENSUSPEND wedges tusb */
-                                               // | MUSB_POWER_ENSUSPEND
+                                               /* | MUSB_POWER_ENSUSPEND */
                                                );
 
        musb->is_active = 0;
@@ -1088,7 +1117,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep  *hw_ep,
                hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
                hw_ep->max_packet_sz_tx = maxpacket;
 
-               hw_ep->is_shared_fifo = TRUE;
+               hw_ep->is_shared_fifo = true;
                break;
        }
 
@@ -1142,7 +1171,7 @@ static int __init ep_config_from_table(struct musb *musb)
 
 
        offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
-       // assert(offset > 0)
+       /* assert(offset > 0) */
 
        /* NOTE:  for RTL versions >= 1.400 EPINFO and RAMINFO would
         * be better than static MUSB_C_NUM_EPS and DYN_FIFO_SIZE...
@@ -1214,11 +1243,11 @@ static int __init ep_config_from_hw(struct musb *musb)
                /* 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;
+                       hw_ep->is_shared_fifo = true;
                        continue;
                } else {
                        hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
-                       hw_ep->is_shared_fifo = FALSE;
+                       hw_ep->is_shared_fifo = false;
                }
 
                /* FIXME set up hw_ep->{rx,tx}_double_buffered */
@@ -1277,7 +1306,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
        if (reg & MUSB_CONFIGDATA_MPRXE) {
                strcat(aInfo, ", bulk combine");
 #ifdef C_MP_RX
-               musb->bulk_combine = TRUE;
+               musb->bulk_combine = true;
 #else
                strcat(aInfo, " (X)");          /* no driver support */
 #endif
@@ -1285,7 +1314,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
        if (reg & MUSB_CONFIGDATA_MPTXE) {
                strcat(aInfo, ", bulk split");
 #ifdef C_MP_TX
-               musb->bulk_split = TRUE;
+               musb->bulk_split = true;
 #else
                strcat(aInfo, " (X)");          /* no driver support */
 #endif
@@ -1496,7 +1525,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
        ep_num = 1;
        while (reg) {
                if (reg & 1) {
-                       // musb_ep_select(musb->mregs, ep_num);
+                       /* musb_ep_select(musb->mregs, ep_num); */
                        /* REVISIT just retval = ep->rx_irq(...) */
                        retval = IRQ_HANDLED;
                        if (devctl & MUSB_DEVCTL_HM) {
@@ -1517,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
        ep_num = 1;
        while (reg) {
                if (reg & 1) {
-                       // musb_ep_select(musb->mregs, ep_num);
+                       /* musb_ep_select(musb->mregs, ep_num); */
                        /* REVISIT just retval |= ep->tx_irq(...) */
                        retval = IRQ_HANDLED;
                        if (devctl & MUSB_DEVCTL_HM) {
@@ -1683,11 +1712,12 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
        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;
        }
+
+       spin_lock_irqsave(&musb->lock, flags);
        musb->a_wait_bcon = val;
        if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
                musb->is_active = 0;
@@ -1741,12 +1771,17 @@ static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
 
 #endif /* sysfs */
 
-/* Only used to provide cable state change events */
+/* Only used to provide driver mode change events */
 static void musb_irq_work(struct work_struct *data)
 {
        struct musb *musb = container_of(data, struct musb, irq_work);
+       static int old_state;
 
-       sysfs_notify(&musb->controller->kobj, NULL, "cable");
+       if (musb->xceiv.state != old_state) {
+               old_state = musb->xceiv.state;
+               sysfs_notify(&musb->controller->kobj, NULL, "cable");
+               sysfs_notify(&musb->controller->kobj, NULL, "mode");
+       }
 }
 
 /* --------------------------------------------------------------------------
@@ -1794,6 +1829,9 @@ allocate_instance(struct device *dev, void __iomem *mbase)
                ep->epnum = epnum;
        }
 
+#ifdef CONFIG_USB_MUSB_OTG
+       otg_set_transceiver(&musb->xceiv);
+#endif
        musb->controller = dev;
        return musb;
 }
@@ -1838,6 +1876,10 @@ static void musb_free(struct musb *musb)
                clk_put(musb->clock);
        }
 
+#ifdef CONFIG_USB_MUSB_OTG
+       put_device(musb->xceiv.dev);
+#endif
+
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
        usb_put_hcd(musb_to_hcd(musb));
 #else
@@ -1963,7 +2005,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
                goto fail2;
        }
        musb->nIrq = nIrq;
-// FIXME this handles wakeup irqs wrong
+/* FIXME this handles wakeup irqs wrong */
        if (enable_irq_wake(nIrq) == 0)
                device_init_wakeup(dev, 1);