summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-omap-2.6.29/musb
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2009-04-06 11:37:02 +0200
committerKoen Kooi <koen@openembedded.org>2009-04-06 11:37:02 +0200
commit87ece9c2e0532d110109c94f81b4b5082e76d3a7 (patch)
treea1147ad4469308a07b9e11c034e895e7431ccff3 /recipes/linux/linux-omap-2.6.29/musb
parentfba2f2496d1855a307b8279ff947c770ad507e0b (diff)
linux-omap 2.6.28: introduce MACHINE_KERNEL_PR
linux-omap 2.6.29: introduce MACHINE_KERNEL_PR, seperate out dss2 patches, add isp and musb patches
Diffstat (limited to 'recipes/linux/linux-omap-2.6.29/musb')
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch43
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch76
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch218
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch106
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch181
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch84
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch47
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch60
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch93
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch361
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch417
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch56
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch91
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch32
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch202
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch146
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch132
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch167
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch158
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch58
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch70
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch38
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch187
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch259
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch90
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch1109
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch198
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch133
-rw-r--r--recipes/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch145
29 files changed, 4957 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch b/recipes/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch
new file mode 100644
index 0000000000..a7898d1440
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch
@@ -0,0 +1,43 @@
+From a9199e8ab6d6fb105aa251d6bf2192e7eafac8ee Mon Sep 17 00:00:00 2001
+From: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:22:53 -0700
+Subject: [PATCH] USB: musb: only turn off vbus in OTG hosts
+
+Except on DaVinci, VBUS is now switched off as part of idling the
+USB link (after a_wait_bcon) whenever a device is disconnected
+from host. This is correct for OTG hosts, where either SRP or
+an ID interrupt could turn VBUS on again.
+
+However, for non-OTG hosts there's no way to turn VBUS on again,
+so the host becomes unusable. And the procfs entry which once
+allowed a manual workaround for this is now gone.
+
+This patch adds an is_otg_enabled() check before scheduling the
+switch-off timer in disconnect path, supporting a "classic host"
+mode where SRP is unavailable.
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: tweak patch description ]
+
+Signed-off-by: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index af77e46..338cd16 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -769,7 +769,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ case OTG_STATE_A_SUSPEND:
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
+ musb_root_disconnect(musb);
+- if (musb->a_wait_bcon != 0)
++ if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch b/recipes/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch
new file mode 100644
index 0000000000..5cb7bcb065
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch
@@ -0,0 +1,76 @@
+From 83eb44b1c84f99d9a5c67612bd94b4ed7c43f64c Mon Sep 17 00:00:00 2001
+From: Felipe Balbi <felipe.balbi-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:22:49 -0700
+Subject: [PATCH] USB: composite: avoid inconsistent lock state
+
+Avoid the following INFO from lock debugging:
+
+[ 369.126112] =================================
+[ 369.132063] [ INFO: inconsistent lock state ]
+[ 369.136457] 2.6.28-maemo1 #1
+[ 369.139387] ---------------------------------
+[ 369.143782] inconsistent {hardirq-on-W} -> {in-hardirq-W} usage.
+[ 369.149855] swapper/0 [HC1[1]:SC0[0]:HE0:SE1] takes:
+[ 369.154890] (&cdev->lock){+-..}, at: [<bf1979f0>] composite_disconnect+0x1c/0]
+[ 369.163404] {hardirq-on-W} state was registered at:
+[ 369.168348] [<c00788a8>] __lock_acquire+0x5d0/0x7d8
+[ 369.173506] [<c0078b14>] lock_acquire+0x64/0x78
+[ 369.178266] [<c0263a34>] _spin_lock+0x4c/0x80
+[ 369.182905] [<bf19597c>] usb_function_deactivate+0x20/0x70 [g_nokia]
+[ 369.189527] [<bf1a0a88>] 0xbf1a0a88
+[ 369.193281] [<bf19f450>] 0xbf19f450
+[ 369.197004] [<bf19fa3c>] 0xbf19fa3c
+[ 369.200758] [<bf1a03a0>] 0xbf1a03a0
+[ 369.204481] [<bf19f254>] 0xbf19f254
+[ 369.208204] [<bf1a0158>] 0xbf1a0158
+[ 369.211927] [<bf1a130c>] 0xbf1a130c
+[ 369.215650] [<c01c21f0>] usb_gadget_register_driver+0x12c/0x28c
+[ 369.221846] [<bf1a06bc>] 0xbf1a06bc
+[ 369.225569] [<bf1a06e8>] 0xbf1a06e8
+[ 369.229322] [<c002c2dc>] __exception_text_end+0x64/0x19c
+[ 369.234877] [<c0081628>] sys_init_module+0x9c/0x194
+[ 369.240004] [<c002c8e0>] ret_fast_syscall+0x0/0x2c
+[ 369.245039] [<ffffffff>] 0xffffffff
+[ 369.248793] irq event stamp: 218356
+[ 369.252302] hardirqs last enabled at (218355): [<c003a77c>] omap3_enter_idle+8
+[ 369.260420] hardirqs last disabled at (218356): [<c0264774>] __irq_svc+0x34/0x0
+[ 369.267927] softirqs last enabled at (218348): [<c00585a4>] __do_softirq+0x134
+[ 369.275892] softirqs last disabled at (218335): [<c005899c>] irq_exit+0x60/0xb0
+[ 369.283308]
+[ 369.283308] other info that might help us debug this:
+[ 369.289930] no locks held by swapper/0.
+
+Cc: David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
+Signed-off-by: Felipe Balbi <felipe.balbi-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/gadget/composite.c | 5 +++--
+ 1 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
+index 5d11c29..40f1da7 100644
+--- a/drivers/usb/gadget/composite.c
++++ b/drivers/usb/gadget/composite.c
+@@ -149,16 +149,17 @@ done:
+ int usb_function_deactivate(struct usb_function *function)
+ {
+ struct usb_composite_dev *cdev = function->config->cdev;
++ unsigned long flags;
+ int status = 0;
+
+- spin_lock(&cdev->lock);
++ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->deactivations == 0)
+ status = usb_gadget_disconnect(cdev->gadget);
+ if (status == 0)
+ cdev->deactivations++;
+
+- spin_unlock(&cdev->lock);
++ spin_unlock_irqrestore(&cdev->lock, flags);
+ return status;
+ }
+
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch b/recipes/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch
new file mode 100644
index 0000000000..fadad9e44a
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch
@@ -0,0 +1,218 @@
+From ba7b26e69f4bb41f10be444c5fded853330f82b5 Mon Sep 17 00:00:00 2001
+From: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:22:51 -0700
+Subject: [PATCH] USB: musb: NAK timeout scheme on bulk RX endpoint
+
+Fixes endpoint starvation issue when more than one bulk QH is
+multiplexed on the reserved bulk RX endpoint, which is normal
+for cases like serial and ethernet adapters.
+
+This patch sets the NAK timeout interval for such QHs, and when
+a timeout triggers the next QH will be scheduled. (This resembles
+the bulk scheduling done in hardware by EHCI, OHCI, and UHCI.)
+
+This scheme doesn't work for devices which are connected to a
+high to full speed tree (transaction translator) as there is
+no NAK timeout interrupt from the musb controller from such
+devices.
+
+Tested with PIO, Inventra DMA, CPPI DMA.
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: fold in start_urb() update;
+ clarify only for bulk RX; don't accidentally clear WZC bits ]
+
+Signed-off-by: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Cc: Felipe Balbi <felipe.balbi-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 112 ++++++++++++++++++++++++++++++++----------
+ 1 files changed, 85 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index 6dbbd07..bd1d5ae 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -64,11 +64,8 @@
+ *
+ * - DMA (Mentor/OMAP) ...has at least toggle update problems
+ *
+- * - Still no traffic scheduling code to make NAKing for bulk or control
+- * transfers unable to starve other requests; or to make efficient use
+- * of hardware with periodic transfers. (Note that network drivers
+- * commonly post bulk reads that stay pending for a long time; these
+- * would make very visible trouble.)
++ * - [23-feb-2009] minimal traffic scheduling to avoid bulk RX packet
++ * starvation ... nothing yet for TX, interrupt, or bulk.
+ *
+ * - Not tested with HNP, but some SRP paths seem to behave.
+ *
+@@ -88,11 +85,8 @@
+ *
+ * CONTROL transfers all go through ep0. BULK ones go through dedicated IN
+ * and OUT endpoints ... hardware is dedicated for those "async" queue(s).
+- *
+ * (Yes, bulk _could_ use more of the endpoints than that, and would even
+- * benefit from it ... one remote device may easily be NAKing while others
+- * need to perform transfers in that same direction. The same thing could
+- * be done in software though, assuming dma cooperates.)
++ * benefit from it.)
+ *
+ * INTERUPPT and ISOCHRONOUS transfers are scheduled to the other endpoints.
+ * So far that scheduling is both dumb and optimistic: the endpoint will be
+@@ -201,8 +195,9 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ len = urb->iso_frame_desc[0].length;
+ break;
+ default: /* bulk, interrupt */
+- buf = urb->transfer_buffer;
+- len = urb->transfer_buffer_length;
++ /* actual_length may be nonzero on retry paths */
++ buf = urb->transfer_buffer + urb->actual_length;
++ len = urb->transfer_buffer_length - urb->actual_length;
+ }
+
+ DBG(4, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n",
+@@ -1045,7 +1040,8 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
+
+ /* NOTE: this code path would be a good place to PAUSE a
+ * control transfer, if another one is queued, so that
+- * ep0 is more likely to stay busy.
++ * ep0 is more likely to stay busy. That's already done
++ * for bulk RX transfers.
+ *
+ * if (qh->ring.next != &musb->control), then
+ * we have a candidate... NAKing is *NOT* an error
+@@ -1197,6 +1193,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ /* NOTE: this code path would be a good place to PAUSE a
+ * transfer, if there's some other (nonperiodic) tx urb
+ * that could use this fifo. (dma complicates it...)
++ * That's already done for bulk RX transfers.
+ *
+ * if (bulk && qh->ring.next != &musb->out_bulk), then
+ * we have a candidate... NAKing is *NOT* an error
+@@ -1358,6 +1355,50 @@ finish:
+
+ #endif
+
++/* Schedule next QH from musb->in_bulk and move the current qh to
++ * the end; avoids starvation for other endpoints.
++ */
++static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep)
++{
++ struct dma_channel *dma;
++ struct urb *urb;
++ void __iomem *mbase = musb->mregs;
++ void __iomem *epio = ep->regs;
++ struct musb_qh *cur_qh, *next_qh;
++ u16 rx_csr;
++
++ musb_ep_select(mbase, ep->epnum);
++ dma = is_dma_capable() ? ep->rx_channel : NULL;
++
++ /* clear nak timeout bit */
++ rx_csr = musb_readw(epio, MUSB_RXCSR);
++ rx_csr |= MUSB_RXCSR_H_WZC_BITS;
++ rx_csr &= ~MUSB_RXCSR_DATAERROR;
++ musb_writew(epio, MUSB_RXCSR, rx_csr);
++
++ cur_qh = first_qh(&musb->in_bulk);
++ if (cur_qh) {
++ urb = next_urb(cur_qh);
++ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
++ dma->status = MUSB_DMA_STATUS_CORE_ABORT;
++ musb->dma_controller->channel_abort(dma);
++ urb->actual_length += dma->actual_len;
++ dma->actual_len = 0L;
++ }
++ musb_save_toggle(ep, 1, urb);
++
++ /* move cur_qh to end of queue */
++ list_move_tail(&cur_qh->ring, &musb->in_bulk);
++
++ /* get the next qh from musb->in_bulk */
++ next_qh = first_qh(&musb->in_bulk);
++
++ /* set rx_reinit and schedule the next qh */
++ ep->rx_reinit = 1;
++ musb_start_urb(musb, 1, next_qh);
++ }
++}
++
+ /*
+ * Service an RX interrupt for the given IN endpoint; docs cover bulk, iso,
+ * and high-bandwidth IN transfer cases.
+@@ -1421,18 +1462,26 @@ void musb_host_rx(struct musb *musb, u8 epnum)
+ } else if (rx_csr & MUSB_RXCSR_DATAERROR) {
+
+ if (USB_ENDPOINT_XFER_ISOC != qh->type) {
+- /* NOTE this code path would be a good place to PAUSE a
+- * transfer, if there's some other (nonperiodic) rx urb
+- * that could use this fifo. (dma complicates it...)
++ DBG(6, "RX end %d NAK timeout\n", epnum);
++
++ /* NOTE: NAKing is *NOT* an error, so we want to
++ * continue. Except ... if there's a request for
++ * another QH, use that instead of starving it.
+ *
+- * if (bulk && qh->ring.next != &musb->in_bulk), then
+- * we have a candidate... NAKing is *NOT* an error
++ * Devices like Ethernet and serial adapters keep
++ * reads posted at all times, which will starve
++ * other devices without this logic.
+ */
+- DBG(6, "RX end %d NAK timeout\n", epnum);
++ if (usb_pipebulk(urb->pipe)
++ && qh->mux == 1
++ && !list_is_singular(&musb->in_bulk)) {
++ musb_bulk_rx_nak_timeout(musb, hw_ep);
++ return;
++ }
+ musb_ep_select(mbase, epnum);
+- musb_writew(epio, MUSB_RXCSR,
+- MUSB_RXCSR_H_WZC_BITS
+- | MUSB_RXCSR_H_REQPKT);
++ rx_csr |= MUSB_RXCSR_H_WZC_BITS;
++ rx_csr &= ~MUSB_RXCSR_DATAERROR;
++ musb_writew(epio, MUSB_RXCSR, rx_csr);
+
+ goto finish;
+ } else {
+@@ -1756,6 +1805,17 @@ static int musb_schedule(
+ head = &musb->in_bulk;
+ else
+ head = &musb->out_bulk;
++
++ /* Enable bulk RX NAK timeout scheme when bulk requests are
++ * multiplexed. This scheme doen't work in high speed to full
++ * speed scenario as NAK interrupts are not coming from a
++ * full speed device connected to a high speed device.
++ * NAK timeout interval is 8 (128 uframe or 16ms) for HS and
++ * 4 (8 frame or 8ms) for FS device.
++ */
++ if (is_in && qh->dev)
++ qh->intv_reg =
++ (USB_SPEED_HIGH == qh->dev->speed) ? 8 : 4;
+ goto success;
+ } else if (best_end < 0) {
+ return -ENOSPC;
+@@ -1888,13 +1948,11 @@ static int musb_urb_enqueue(
+ *
+ * The downside of disabling this is that transfer scheduling
+ * gets VERY unfair for nonperiodic transfers; a misbehaving
+- * peripheral could make that hurt. Or for reads, one that's
+- * perfectly normal: network and other drivers keep reads
+- * posted at all times, having one pending for a week should
+- * be perfectly safe.
++ * peripheral could make that hurt. That's perfectly normal
++ * for reads from network or serial adapters ... so we have
++ * partial NAKlimit support for bulk RX.
+ *
+- * The upside of disabling it is avoidng transfer scheduling
+- * code to put this aside for while.
++ * The upside of disabling it is simpler transfer scheduling.
+ */
+ interval = 0;
+ }
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch b/recipes/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch
new file mode 100644
index 0000000000..438f11cf7a
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch
@@ -0,0 +1,106 @@
+From 9ebf351bcd28a89a0b1ba8d0496fffbc72421611 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:22:50 -0700
+Subject: [PATCH] USB: musb: rewrite host periodic endpoint allocation
+
+The current MUSB host code doesn't make use of all the available
+FIFOs in for periodic transfers since it wrongly assumes the RX
+and TX sides of any given hw_ep always share one FIFO.
+
+Change: use 'in_qh' and 'out_qh' fields of the 'struct musb_hw_ep'
+to check the endpoint's business; get rid of the now-unused 'periodic'
+array in the 'struct musb'. Also optimize a loop induction variable
+in the endpoint lookup code.
+
+(Based on a previous patch from Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>)
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: clarify description and origin
+ of this fix; whitespace ]
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Cc: Felipe Balbi <felipe.balbi-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.h | 1 -
+ drivers/usb/musb/musb_host.c | 28 +++++++++++-----------------
+ 2 files changed, 11 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
+index 630946a..adf1806 100644
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -331,7 +331,6 @@ struct musb {
+ struct list_head control; /* of musb_qh */
+ struct list_head in_bulk; /* of musb_qh */
+ struct list_head out_bulk; /* of musb_qh */
+- struct musb_qh *periodic[32]; /* tree of interrupt+iso */
+ #endif
+
+ /* called with IRQs blocked; ON/nonzero implies starting a session,
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index bd1d5ae..499c431 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -390,7 +390,6 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+ * de-allocated if it's tracked and allocated;
+ * and where we'd update the schedule tree...
+ */
+- musb->periodic[ep->epnum] = NULL;
+ kfree(qh);
+ qh = NULL;
+ break;
+@@ -1760,31 +1759,27 @@ static int musb_schedule(
+
+ /* else, periodic transfers get muxed to other endpoints */
+
+- /* FIXME this doesn't consider direction, so it can only
+- * work for one half of the endpoint hardware, and assumes
+- * the previous cases handled all non-shared endpoints...
+- */
+-
+- /* we know this qh hasn't been scheduled, so all we need to do
++ /*
++ * We know this qh hasn't been scheduled, so all we need to do
+ * is choose which hardware endpoint to put it on ...
+ *
+ * REVISIT what we really want here is a regular schedule tree
+- * like e.g. OHCI uses, but for now musb->periodic is just an
+- * array of the _single_ logical endpoint associated with a
+- * given physical one (identity mapping logical->physical).
+- *
+- * that simplistic approach makes TT scheduling a lot simpler;
+- * there is none, and thus none of its complexity...
++ * like e.g. OHCI uses.
+ */
+ best_diff = 4096;
+ best_end = -1;
+
+- for (epnum = 1; epnum < musb->nr_endpoints; epnum++) {
++ for (epnum = 1, hw_ep = musb->endpoints + 1;
++ epnum < musb->nr_endpoints;
++ epnum++, hw_ep++) {
+ int diff;
+
+- if (musb->periodic[epnum])
++ if (is_in || hw_ep->is_shared_fifo) {
++ if (hw_ep->in_qh != NULL)
++ continue;
++ } else if (hw_ep->out_qh != NULL)
+ continue;
+- hw_ep = &musb->endpoints[epnum];
++
+ if (hw_ep == musb->bulk_ep)
+ continue;
+
+@@ -1824,7 +1819,6 @@ static int musb_schedule(
+ idle = 1;
+ qh->mux = 0;
+ hw_ep = musb->endpoints + best_end;
+- musb->periodic[best_end] = qh;
+ DBG(4, "qh %p periodic slot %d\n", qh, best_end);
+ success:
+ if (head) {
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch b/recipes/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch
new file mode 100644
index 0000000000..db3481b87b
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch
@@ -0,0 +1,181 @@
+From 60e7ce93befe795357db05001fe4caab522a421d Mon Sep 17 00:00:00 2001
+From: Jouni Hogander <jouni.hogander-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:22:57 -0700
+Subject: [PATCH] USB: TWL: disable VUSB regulators when cable unplugged
+
+This patch disables USB regulators VUSB1V5, VUSB1V8, and VUSB3V1
+when the USB cable is unplugged to reduce power consumption.
+Added a depencency from twl4030 usb driver to TWL_REGULATOR.
+
+Signed-off-by: Jouni Hogander <jouni.hogander-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: Kalle Jokiniemi <kalle.jokiniemi-sMOQStClEysAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/otg/Kconfig | 2 +-
+ drivers/usb/otg/twl4030-usb.c | 73 ++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 65 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
+index ee55b44..5790a5b 100644
+--- a/drivers/usb/otg/Kconfig
++++ b/drivers/usb/otg/Kconfig
+@@ -43,7 +43,7 @@ config ISP1301_OMAP
+
+ config TWL4030_USB
+ tristate "TWL4030 USB Transceiver Driver"
+- depends on TWL4030_CORE
++ depends on TWL4030_CORE && REGULATOR_TWL4030
+ select USB_OTG_UTILS
+ help
+ Enable this to support the USB OTG transceiver on TWL4030
+diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
+index 416e441..d9478d0 100644
+--- a/drivers/usb/otg/twl4030-usb.c
++++ b/drivers/usb/otg/twl4030-usb.c
+@@ -34,6 +34,8 @@
+ #include <linux/delay.h>
+ #include <linux/usb/otg.h>
+ #include <linux/i2c/twl4030.h>
++#include <linux/regulator/consumer.h>
++#include <linux/err.h>
+
+
+ /* Register defines */
+@@ -246,6 +248,11 @@ struct twl4030_usb {
+ struct otg_transceiver otg;
+ struct device *dev;
+
++ /* TWL4030 internal USB regulator supplies */
++ struct regulator *usb1v5;
++ struct regulator *usb1v8;
++ struct regulator *usb3v1;
++
+ /* for vbus reporting with irqs disabled */
+ spinlock_t lock;
+
+@@ -434,6 +441,18 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
+
+ pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
+ if (on) {
++ regulator_enable(twl->usb3v1);
++ regulator_enable(twl->usb1v8);
++ /*
++ * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
++ * in twl4030) resets the VUSB_DEDICATED2 register. This reset
++ * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
++ * SLEEP. We work around this by clearing the bit after usv3v1
++ * is re-activated. This ensures that VUSB3V1 is really active.
++ */
++ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
++ VUSB_DEDICATED2);
++ regulator_enable(twl->usb1v5);
+ pwr &= ~PHY_PWR_PHYPWD;
+ WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
+ twl4030_usb_write(twl, PHY_CLK_CTRL,
+@@ -443,6 +462,9 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
+ } else {
+ pwr |= PHY_PWR_PHYPWD;
+ WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
++ regulator_disable(twl->usb1v5);
++ regulator_disable(twl->usb1v8);
++ regulator_disable(twl->usb3v1);
+ }
+ }
+
+@@ -468,7 +490,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
+ twl->asleep = 0;
+ }
+
+-static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
++static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
+ {
+ /* Enable writing to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
+@@ -480,20 +502,45 @@ static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
+ /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+
+- /* turn on 3.1V regulator */
+- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP);
++ /* Initialize 3.1V regulator */
++ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
++
++ twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
++ if (IS_ERR(twl->usb3v1))
++ return -ENODEV;
++
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+
+- /* turn on 1.5V regulator */
+- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP);
++ /* Initialize 1.5V regulator */
++ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
++
++ twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
++ if (IS_ERR(twl->usb1v5))
++ goto fail1;
++
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+
+- /* turn on 1.8V regulator */
+- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP);
++ /* Initialize 1.8V regulator */
++ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
++
++ twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
++ if (IS_ERR(twl->usb1v8))
++ goto fail2;
++
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+
+ /* disable access to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
++
++ return 0;
++
++fail2:
++ regulator_put(twl->usb1v5);
++ twl->usb1v5 = NULL;
++fail1:
++ regulator_put(twl->usb3v1);
++ twl->usb3v1 = NULL;
++ return -ENODEV;
+ }
+
+ static ssize_t twl4030_usb_vbus_show(struct device *dev,
+@@ -598,7 +645,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
+ {
+ struct twl4030_usb_data *pdata = pdev->dev.platform_data;
+ struct twl4030_usb *twl;
+- int status;
++ int status, err;
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "platform_data not available\n");
+@@ -622,7 +669,12 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
+ /* init spinlock for workqueue */
+ spin_lock_init(&twl->lock);
+
+- twl4030_usb_ldo_init(twl);
++ err = twl4030_usb_ldo_init(twl);
++ if (err) {
++ dev_err(&pdev->dev, "ldo init failed\n");
++ kfree(twl);
++ return err;
++ }
+ otg_set_transceiver(&twl->otg);
+
+ platform_set_drvdata(pdev, twl);
+@@ -688,6 +740,9 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
+ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+
+ twl4030_phy_power(twl, 0);
++ regulator_put(twl->usb1v5);
++ regulator_put(twl->usb1v8);
++ regulator_put(twl->usb3v1);
+
+ kfree(twl);
+
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch b/recipes/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch
new file mode 100644
index 0000000000..3f49a4d636
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch
@@ -0,0 +1,84 @@
+From 7eef82d231578140c6000d04846a48bdaf341a65 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 24 Mar 2009 17:23:19 -0700
+Subject: [PATCH] USB: gadget: composite device-level suspend/resume hooks
+
+Address one open question in the composite gadget framework:
+Yes, we should have device-level suspend/resume callbacks
+in addition to the function-level ones. We have at least one
+scenario (with gadget zero in OTG test mode) that's awkward
+to handle without it.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Cc: Felipe Balbi <felipe.balbi-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
+---
+ drivers/usb/gadget/composite.c | 8 ++++++--
+ include/linux/usb/composite.h | 8 ++++++++
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
+index 40f1da7..59e8523 100644
+--- a/drivers/usb/gadget/composite.c
++++ b/drivers/usb/gadget/composite.c
+@@ -1014,7 +1014,7 @@ composite_suspend(struct usb_gadget *gadget)
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+- /* REVISIT: should we have config and device level
++ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "suspend\n");
+@@ -1024,6 +1024,8 @@ composite_suspend(struct usb_gadget *gadget)
+ f->suspend(f);
+ }
+ }
++ if (composite->suspend)
++ composite->suspend(cdev);
+ }
+
+ static void
+@@ -1032,10 +1034,12 @@ composite_resume(struct usb_gadget *gadget)
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+- /* REVISIT: should we have config and device level
++ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "resume\n");
++ if (composite->resume)
++ composite->resume(cdev);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
+index 935c380..acd7b0f 100644
+--- a/include/linux/usb/composite.h
++++ b/include/linux/usb/composite.h
+@@ -244,6 +244,10 @@ int usb_add_config(struct usb_composite_dev *,
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind(); called as a side effect of unregistering
+ * this driver.
++ * @suspend: Notifies when the host stops sending USB traffic,
++ * after function notifications
++ * @resume: Notifies configuration when the host restarts USB traffic,
++ * before function notifications
+ *
+ * Devices default to reporting self powered operation. Devices which rely
+ * on bus powered operation should report this in their @bind() method.
+@@ -268,6 +272,10 @@ struct usb_composite_driver {
+
+ int (*bind)(struct usb_composite_dev *);
+ int (*unbind)(struct usb_composite_dev *);
++
++ /* global suspend hooks */
++ void (*suspend)(struct usb_composite_dev *);
++ void (*resume)(struct usb_composite_dev *);
+ };
+
+ extern int usb_composite_register(struct usb_composite_driver *);
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch b/recipes/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch
new file mode 100644
index 0000000000..a89bc2ff5c
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch
@@ -0,0 +1,47 @@
+From 00c4bd07a64061ec9ab9c35f5bf01ec6187138f4 Mon Sep 17 00:00:00 2001
+From: Jonathan McDowell <noodles-4QvXXjU8Dv4@public.gmane.org>
+Date: Thu, 26 Mar 2009 00:45:27 -0700
+Subject: [PATCH] usb gadget: fix ethernet link reports to ethtool
+
+The g_ether USB gadget driver currently decides whether or not there's a
+link to report back for eth_get_link based on if the USB link speed is
+set. The USB gadget speed is however often set even before the device is
+enumerated. It seems more sensible to only report a "link" if we're
+actually connected to a host that wants to talk to us. The patch below
+does this for me - tested with the PXA27x UDC driver.
+
+Signed-Off-By: Jonathan McDowell <noodles-4QvXXjU8Dv4@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/gadget/u_ether.c | 8 +-------
+ 1 files changed, 1 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
+index 96d65ca..4007770 100644
+--- a/drivers/usb/gadget/u_ether.c
++++ b/drivers/usb/gadget/u_ether.c
+@@ -175,12 +175,6 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+ strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
+ }
+
+-static u32 eth_get_link(struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+- return dev->gadget->speed != USB_SPEED_UNKNOWN;
+-}
+-
+ /* REVISIT can also support:
+ * - WOL (by tracking suspends and issuing remote wakeup)
+ * - msglevel (implies updated messaging)
+@@ -189,7 +183,7 @@ static u32 eth_get_link(struct net_device *net)
+
+ static struct ethtool_ops ops = {
+ .get_drvinfo = eth_get_drvinfo,
+- .get_link = eth_get_link
++ .get_link = ethtool_op_get_link,
+ };
+
+ static void defer_kevent(struct eth_dev *dev, int flag)
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch b/recipes/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch
new file mode 100644
index 0000000000..8627825b5a
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch
@@ -0,0 +1,60 @@
+From c3b527a21104b6bb61558fba6c65aa80f63e0772 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Thu, 26 Mar 2009 17:36:57 -0700
+Subject: [PATCH] usb: musb_host, minor enqueue locking fix (v2)
+
+Someone noted that the enqueue path used an unlocked access
+for usb_host_endpoint->hcpriv ... fix that, by being safe
+and always accessing it under spinlock protection.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 17 ++++++++---------
+ 1 files changed, 8 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index 499c431..ff09595 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -1841,7 +1841,7 @@ static int musb_urb_enqueue(
+ unsigned long flags;
+ struct musb *musb = hcd_to_musb(hcd);
+ struct usb_host_endpoint *hep = urb->ep;
+- struct musb_qh *qh = hep->hcpriv;
++ struct musb_qh *qh;
+ struct usb_endpoint_descriptor *epd = &hep->desc;
+ int ret;
+ unsigned type_reg;
+@@ -1853,22 +1853,21 @@ static int musb_urb_enqueue(
+
+ spin_lock_irqsave(&musb->lock, flags);
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
++ qh = ret ? NULL : hep->hcpriv;
++ if (qh)
++ urb->hcpriv = qh;
+ spin_unlock_irqrestore(&musb->lock, flags);
+- if (ret)
+- return ret;
+
+ /* DMA mapping was already done, if needed, and this urb is on
+- * hep->urb_list ... so there's little to do unless hep wasn't
+- * yet scheduled onto a live qh.
++ * hep->urb_list now ... so we're done, unless hep wasn't yet
++ * scheduled onto a live qh.
+ *
+ * REVISIT best to keep hep->hcpriv valid until the endpoint gets
+ * disabled, testing for empty qh->ring and avoiding qh setup costs
+ * except for the first urb queued after a config change.
+ */
+- if (qh) {
+- urb->hcpriv = qh;
+- return 0;
+- }
++ if (qh || ret)
++ return ret;
+
+ /* Allocate and initialize qh, minimizing the work done each time
+ * hw_ep gets reprogrammed, or with irqs blocked. Then schedule it.
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch b/recipes/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch
new file mode 100644
index 0000000000..09fc0a17d0
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch
@@ -0,0 +1,93 @@
+From 48ce47b15bfd420982ee275c595a9139eb6fabf7 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Thu, 26 Mar 2009 17:38:30 -0700
+Subject: [PATCH] usb: musb_host, fix ep0 fifo flushing
+
+The MUSB host side can't share generic TX FIFO flush logic
+with EP0; the EP0 TX status register bits are different
+from those for other entpoints.
+
+Resolve this issue by providing a new EP0-specific routine
+to flush and reset the FIFO, which pays careful attention to
+restrictions listed in the latest programmer's guide. This
+gets rid of an open issue whereby the usbtest control write
+test (#14) failed.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 38 +++++++++++++++++++++++++-------------
+ 1 files changed, 25 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index ff09595..a5d75aa 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -125,6 +125,29 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
+ }
+ }
+
++static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
++{
++ void __iomem *epio = ep->regs;
++ u16 csr;
++ int retries = 5;
++
++ /* scrub any data left in the fifo */
++ do {
++ csr = musb_readw(epio, MUSB_TXCSR);
++ if (!(csr & (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_RXPKTRDY)))
++ break;
++ musb_writew(epio, MUSB_TXCSR, MUSB_CSR0_FLUSHFIFO);
++ csr = musb_readw(epio, MUSB_TXCSR);
++ udelay(10);
++ } while (--retries);
++
++ WARN(!retries, "Could not flush host TX%d fifo: csr: %04x\n",
++ ep->epnum, csr);
++
++ /* and reset for the next transfer */
++ musb_writew(epio, MUSB_TXCSR, 0);
++}
++
+ /*
+ * Start transmit. Caller is responsible for locking shared resources.
+ * musb must be locked.
+@@ -693,11 +716,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ musb_writew(epio, MUSB_TXCSR, csr);
+ csr = musb_readw(epio, MUSB_TXCSR);
+ } else {
+- /* endpoint 0: just flush */
+- musb_writew(epio, MUSB_CSR0,
+- csr | MUSB_CSR0_FLUSHFIFO);
+- musb_writew(epio, MUSB_CSR0,
+- csr | MUSB_CSR0_FLUSHFIFO);
++ musb_h_ep0_flush_fifo(hw_ep);
+ }
+
+ /* target addr and (for multipoint) hub addr/port */
+@@ -1063,11 +1082,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
+ csr &= ~MUSB_CSR0_H_NAKTIMEOUT;
+ musb_writew(epio, MUSB_CSR0, csr);
+ } else {
+- csr |= MUSB_CSR0_FLUSHFIFO;
+- musb_writew(epio, MUSB_CSR0, csr);
+- musb_writew(epio, MUSB_CSR0, csr);
+- csr &= ~MUSB_CSR0_H_NAKTIMEOUT;
+- musb_writew(epio, MUSB_CSR0, csr);
++ musb_h_ep0_flush_fifo(hw_ep);
+ }
+
+ musb_writeb(epio, MUSB_NAKLIMIT0, 0);
+@@ -1081,9 +1096,6 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
+ * SHOULD NEVER HAPPEN! */
+ ERR("no URB for end 0\n");
+
+- musb_writew(epio, MUSB_CSR0, MUSB_CSR0_FLUSHFIFO);
+- musb_writew(epio, MUSB_CSR0, MUSB_CSR0_FLUSHFIFO);
+- musb_writew(epio, MUSB_CSR0, 0);
+
+ goto done;
+ }
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch b/recipes/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch
new file mode 100644
index 0000000000..bcbe3bbe39
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch
@@ -0,0 +1,361 @@
+From c99f4a68268801a2e2ffbef9766c3ac89e4fb22c Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Thu, 26 Mar 2009 18:27:47 -0700
+Subject: [PATCH] musb: sanitize clearing TXCSR DMA bits (take 2)
+
+The MUSB code clears TXCSR_DMAMODE incorrectly in several
+places, either asserting that TXCSR_DMAENAB is clear (when
+sometimes it isn't) or clearing both bits together. Recent
+versions of the programmer's guide require DMAENAB to be
+cleared first, although some older ones didn't.
+
+Fix this and while at it:
+
+ - In musb_gadget::txstate(), stop clearing the AUTOSET
+ and DMAMODE bits for the CPPI case since they never
+ get set anyway (the former bit is reserved on DaVinci);
+ but do clear the DMAENAB bit on the DMA error path.
+
+ - In musb_host::musb_ep_program(), remove the duplicate
+ DMA controller specific code code clearing the TXCSR
+ previous state, add the code to clear TXCSR DMA bits
+ on the Inventra DMA error path, to replace such code
+ (executed late) on the PIO path.
+
+ - In musbhsdma::dma_channel_abort()/dma_controller_irq(),
+ add/use the 'offset' variable to avoid MUSB_EP_OFFSET()
+ invocations on every RXCSR/TXCSR access.
+
+[dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: don't introduce CamelCase,
+shrink diff]
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_gadget.c | 33 +++++++++++------
+ drivers/usb/musb/musb_host.c | 79 ++++++++++++++++------------------------
+ drivers/usb/musb/musbhsdma.c | 59 ++++++++++++++++++------------
+ 3 files changed, 90 insertions(+), 81 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index c7ebd08..f79440c 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -165,9 +165,15 @@ static void nuke(struct musb_ep *ep, const int status)
+ if (is_dma_capable() && ep->dma) {
+ struct dma_controller *c = ep->musb->dma_controller;
+ int value;
++
+ if (ep->is_in) {
++ /*
++ * The programming guide says that we must not clear
++ * the DMAMODE bit before DMAENAB, so we only
++ * clear it in the second write...
++ */
+ musb_writew(epio, MUSB_TXCSR,
+- 0 | MUSB_TXCSR_FLUSHFIFO);
++ MUSB_TXCSR_DMAMODE | MUSB_TXCSR_FLUSHFIFO);
+ musb_writew(epio, MUSB_TXCSR,
+ 0 | MUSB_TXCSR_FLUSHFIFO);
+ } else {
+@@ -230,7 +236,7 @@ static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep)
+ | IN token(s) are recd from Host.
+ | -> DMA interrupt on completion
+ | calls TxAvail.
+- | -> stop DMA, ~DmaEenab,
++ | -> stop DMA, ~DMAENAB,
+ | -> set TxPktRdy for last short pkt or zlp
+ | -> Complete Request
+ | -> Continue next request (call txstate)
+@@ -315,9 +321,17 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ request->dma, request_size);
+ if (use_dma) {
+ if (musb_ep->dma->desired_mode == 0) {
+- /* ASSERT: DMAENAB is clear */
+- csr &= ~(MUSB_TXCSR_AUTOSET |
+- MUSB_TXCSR_DMAMODE);
++ /*
++ * We must not clear the DMAMODE bit
++ * before the DMAENAB bit -- and the
++ * latter doesn't always get cleared
++ * before we get here...
++ */
++ csr &= ~(MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAENAB);
++ musb_writew(epio, MUSB_TXCSR, csr
++ | MUSB_TXCSR_P_WZC_BITS);
++ csr &= ~MUSB_TXCSR_DMAMODE;
+ csr |= (MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_MODE);
+ /* against programming guide */
+@@ -334,10 +348,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
+
+ #elif defined(CONFIG_USB_TI_CPPI_DMA)
+ /* program endpoint CSR first, then setup DMA */
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_P_UNDERRUN
+- | MUSB_TXCSR_TXPKTRDY);
++ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
+ csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB;
+ musb_writew(epio, MUSB_TXCSR,
+ (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
+@@ -364,8 +375,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ if (!use_dma) {
+ c->channel_release(musb_ep->dma);
+ musb_ep->dma = NULL;
+- /* ASSERT: DMAENAB clear */
+- csr &= ~(MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE);
++ csr &= ~MUSB_TXCSR_DMAENAB;
++ musb_writew(epio, MUSB_TXCSR, csr);
+ /* invariant: prequest->buf is non-null */
+ }
+ #elif defined(CONFIG_USB_TUSB_OMAP_DMA)
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index a5d75aa..6591282 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -590,10 +590,17 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
+ csr = musb_readw(ep->regs, MUSB_TXCSR);
+ if (csr & MUSB_TXCSR_MODE) {
+ musb_h_tx_flush_fifo(ep);
++ csr = musb_readw(ep->regs, MUSB_TXCSR);
+ musb_writew(ep->regs, MUSB_TXCSR,
+- MUSB_TXCSR_FRCDATATOG);
++ csr | MUSB_TXCSR_FRCDATATOG);
+ }
+- /* clear mode (and everything else) to enable Rx */
++
++ /*
++ * Clear the MODE bit (and everything else) to enable Rx.
++ * NOTE: we mustn't clear the DMAMODE bit before DMAENAB.
++ */
++ if (csr & MUSB_TXCSR_DMAMODE)
++ musb_writew(ep->regs, MUSB_TXCSR, MUSB_TXCSR_DMAMODE);
+ musb_writew(ep->regs, MUSB_TXCSR, 0);
+
+ /* scrub all previous state, clearing toggle */
+@@ -690,12 +697,17 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+
+ /* general endpoint setup */
+ if (epnum) {
+- /* ASSERT: TXCSR_DMAENAB was already cleared */
+-
+ /* flush all old state, set default */
+ musb_h_tx_flush_fifo(hw_ep);
++
++ /*
++ * We must not clear the DMAMODE bit before or in
++ * the same cycle with the DMAENAB bit, so we clear
++ * the latter first...
++ */
+ csr &= ~(MUSB_TXCSR_H_NAKTIMEOUT
+- | MUSB_TXCSR_DMAMODE
++ | MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_FRCDATATOG
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_ERROR
+@@ -703,16 +715,15 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ );
+ csr |= MUSB_TXCSR_MODE;
+
+- if (usb_gettoggle(urb->dev,
+- qh->epnum, 1))
++ if (usb_gettoggle(urb->dev, qh->epnum, 1))
+ csr |= MUSB_TXCSR_H_WR_DATATOGGLE
+ | MUSB_TXCSR_H_DATATOGGLE;
+ else
+ csr |= MUSB_TXCSR_CLRDATATOG;
+
+- /* twice in case of double packet buffering */
+ musb_writew(epio, MUSB_TXCSR, csr);
+ /* REVISIT may need to clear FLUSHFIFO ... */
++ csr &= ~MUSB_TXCSR_DMAMODE;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ csr = musb_readw(epio, MUSB_TXCSR);
+ } else {
+@@ -755,34 +766,19 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+
+ #ifdef CONFIG_USB_INVENTRA_DMA
+ if (dma_channel) {
+-
+- /* clear previous state */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_DMAENAB);
+- csr |= MUSB_TXCSR_MODE;
+- musb_writew(epio, MUSB_TXCSR,
+- csr | MUSB_TXCSR_MODE);
+-
+ qh->segsize = min(len, dma_channel->max_len);
+-
+ if (qh->segsize <= packet_sz)
+ dma_channel->desired_mode = 0;
+ else
+ dma_channel->desired_mode = 1;
+
+-
+ if (dma_channel->desired_mode == 0) {
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE);
++ /* Against the programming guide */
+ csr |= (MUSB_TXCSR_DMAENAB);
+- /* against programming guide */
+ } else
+ csr |= (MUSB_TXCSR_AUTOSET
+ | MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE);
+-
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ dma_ok = dma_controller->channel_program(
+@@ -799,6 +795,17 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ else
+ hw_ep->rx_channel = NULL;
+ dma_channel = NULL;
++
++ /*
++ * The programming guide says that we must
++ * clear the DMAENAB bit before DMAMODE...
++ */
++ csr = musb_readw(epio, MUSB_TXCSR);
++ csr &= ~(MUSB_TXCSR_DMAENAB
++ | MUSB_TXCSR_AUTOSET);
++ musb_writew(epio, MUSB_TXCSR, csr);
++ csr &= ~MUSB_TXCSR_DMAMODE;
++ musb_writew(epio, MUSB_TXCSR, csr);
+ }
+ }
+ #endif
+@@ -806,18 +813,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ /* candidate for DMA */
+ if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
+
+- /* program endpoint CSRs first, then setup DMA.
+- * assume CPPI setup succeeds.
+- * defer enabling dma.
+- */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_DMAENAB);
+- csr |= MUSB_TXCSR_MODE;
+- musb_writew(epio, MUSB_TXCSR,
+- csr | MUSB_TXCSR_MODE);
+-
++ /* Defer enabling DMA */
+ dma_channel->actual_len = 0L;
+ qh->segsize = len;
+
+@@ -846,20 +842,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ }
+
+ if (load_count) {
+- /* ASSERT: TXCSR_DMAENAB was already cleared */
+-
+ /* PIO to load FIFO */
+ qh->segsize = load_count;
+ musb_write_fifo(hw_ep, load_count, buf);
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_AUTOSET);
+- /* write CSR */
+- csr |= MUSB_TXCSR_MODE;
+-
+- if (epnum)
+- musb_writew(epio, MUSB_TXCSR, csr);
+ }
+
+ /* re-enable interrupt */
+diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
+index 8662e9e..40709c3 100644
+--- a/drivers/usb/musb/musbhsdma.c
++++ b/drivers/usb/musb/musbhsdma.c
+@@ -195,30 +195,32 @@ static int dma_channel_abort(struct dma_channel *channel)
+ void __iomem *mbase = musb_channel->controller->base;
+
+ u8 bchannel = musb_channel->idx;
++ int offset;
+ u16 csr;
+
+ if (channel->status == MUSB_DMA_STATUS_BUSY) {
+ if (musb_channel->transmit) {
+-
+- csr = musb_readw(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum,
+- MUSB_TXCSR));
+- csr &= ~(MUSB_TXCSR_AUTOSET |
+- MUSB_TXCSR_DMAENAB |
+- MUSB_TXCSR_DMAMODE);
+- musb_writew(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),
+- csr);
++ offset = MUSB_EP_OFFSET(musb_channel->epnum,
++ MUSB_TXCSR);
++
++ /*
++ * The programming guide says that we must clear
++ * the DMAENAB bit before the DMAMODE bit...
++ */
++ csr = musb_readw(mbase, offset);
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB);
++ musb_writew(mbase, offset, csr);
++ csr &= ~MUSB_TXCSR_DMAMODE;
++ musb_writew(mbase, offset, csr);
+ } else {
+- csr = musb_readw(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum,
+- MUSB_RXCSR));
++ offset = MUSB_EP_OFFSET(musb_channel->epnum,
++ MUSB_RXCSR);
++
++ csr = musb_readw(mbase, offset);
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR |
+ MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE);
+- musb_writew(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),
+- csr);
++ musb_writew(mbase, offset, csr);
+ }
+
+ musb_writew(mbase,
+@@ -296,14 +298,25 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
+ && ((channel->desired_mode == 0)
+ || (channel->actual_len &
+ (musb_channel->max_packet_sz - 1)))
+- ) {
++ ) {
++ u8 epnum = musb_channel->epnum;
++ int offset = MUSB_EP_OFFSET(epnum,
++ MUSB_TXCSR);
++ u16 txcsr;
++
++ /*
++ * The programming guide says that we
++ * must clear DMAENAB before DMAMODE.
++ */
++ musb_ep_select(mbase, epnum);
++ txcsr = musb_readw(mbase, offset);
++ txcsr &= ~(MUSB_TXCSR_DMAENAB
++ | MUSB_TXCSR_AUTOSET);
++ musb_writew(mbase, offset, txcsr);
+ /* Send out the packet */
+- musb_ep_select(mbase,
+- musb_channel->epnum);
+- musb_writew(mbase, MUSB_EP_OFFSET(
+- musb_channel->epnum,
+- MUSB_TXCSR),
+- MUSB_TXCSR_TXPKTRDY);
++ txcsr &= ~MUSB_TXCSR_DMAMODE;
++ txcsr |= MUSB_TXCSR_TXPKTRDY;
++ musb_writew(mbase, offset, txcsr);
+ } else {
+ musb_dma_completion(
+ musb,
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch b/recipes/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch
new file mode 100644
index 0000000000..7d546e10b0
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch
@@ -0,0 +1,417 @@
+From 035cd4a26e9b1638b4b0419b98409026176563ca Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Thu, 26 Mar 2009 18:29:19 -0700
+Subject: [PATCH] musb: fix isochronous TXDMA (take 2)
+
+Multi-frame isochronous TX URBs transfers in DMA mode never
+complete with CPPI DMA because musb_host_tx() doesn't restart
+DMA on the second frame, only emitting a debug message.
+With Inventra DMA they complete, but in PIO mode. To fix:
+
+ - Factor out programming of the DMA transfer from
+ musb_ep_program() into musb_tx_dma_program();
+
+ - Reorder the code at the end of musb_host_tx() to
+ facilitate the fallback to PIO iff DMA fails;
+
+ - Handle the buffer offset consistently for both
+ PIO and DMA modes;
+
+ - Add an argument to musb_ep_program() for the same
+ reason (it only worked correctly with non-zero
+ offset of the first frame in PIO mode);
+
+ - Set the completed isochronous frame descriptor's
+ 'actual_length' and 'status' fields correctly in
+ DMA mode.
+
+Also, since CPPI reportedly doesn't like sending isochronous
+packets in the RNDIS mode, change the criterion for this
+mode to be used only for multi-packet transfers. (There's
+no need for that mode in the single-packet case anyway.)
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: split comment paragraph
+into bullet list, shrink patch delta, style tweaks ]
+
+Signed-off-by: Pavel Kiryukhin <pkiryukhin-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/cppi_dma.c | 1 +
+ drivers/usb/musb/musb_host.c | 227 +++++++++++++++++++-----------------------
+ 2 files changed, 105 insertions(+), 123 deletions(-)
+
+diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
+index 569ef0f..ac7227c 100644
+--- a/drivers/usb/musb/cppi_dma.c
++++ b/drivers/usb/musb/cppi_dma.c
+@@ -579,6 +579,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx)
+ * trigger the "send a ZLP?" confusion.
+ */
+ rndis = (maxpacket & 0x3f) == 0
++ && length > maxpacket
+ && length < 0xffff
+ && (length % maxpacket) != 0;
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index 6591282..f6e84a0 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -96,8 +96,8 @@
+
+
+ static void musb_ep_program(struct musb *musb, u8 epnum,
+- struct urb *urb, unsigned int nOut,
+- u8 *buf, u32 len);
++ struct urb *urb, int is_out,
++ u8 *buf, u32 offset, u32 len);
+
+ /*
+ * Clear TX fifo. Needed to avoid BABBLE errors.
+@@ -189,9 +189,10 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ {
+ u16 frame;
+ u32 len;
+- void *buf;
+ void __iomem *mbase = musb->mregs;
+ struct urb *urb = next_urb(qh);
++ void *buf = urb->transfer_buffer;
++ u32 offset = 0;
+ struct musb_hw_ep *hw_ep = qh->hw_ep;
+ unsigned pipe = urb->pipe;
+ u8 address = usb_pipedevice(pipe);
+@@ -214,7 +215,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC:
+ qh->iso_idx = 0;
+ qh->frame = 0;
+- buf = urb->transfer_buffer + urb->iso_frame_desc[0].offset;
++ offset = urb->iso_frame_desc[0].offset;
+ len = urb->iso_frame_desc[0].length;
+ break;
+ default: /* bulk, interrupt */
+@@ -232,14 +233,14 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break;
+ default: s = "-intr"; break;
+ }; s; }),
+- epnum, buf, len);
++ epnum, buf + offset, len);
+
+ /* Configure endpoint */
+ if (is_in || hw_ep->is_shared_fifo)
+ hw_ep->in_qh = qh;
+ else
+ hw_ep->out_qh = qh;
+- musb_ep_program(musb, epnum, urb, !is_in, buf, len);
++ musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len);
+
+ /* transmit may have more work: start it when it is time */
+ if (is_in)
+@@ -250,7 +251,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC:
+ case USB_ENDPOINT_XFER_INT:
+ DBG(3, "check whether there's still time for periodic Tx\n");
+- qh->iso_idx = 0;
+ frame = musb_readw(mbase, MUSB_FRAME);
+ /* FIXME this doesn't implement that scheduling policy ...
+ * or handle framecounter wrapping
+@@ -631,14 +631,68 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
+ ep->rx_reinit = 0;
+ }
+
++static bool musb_tx_dma_program(struct dma_controller *dma,
++ struct musb_hw_ep *hw_ep, struct musb_qh *qh,
++ struct urb *urb, u32 offset, u32 length)
++{
++ struct dma_channel *channel = hw_ep->tx_channel;
++ void __iomem *epio = hw_ep->regs;
++ u16 pkt_size = qh->maxpacket;
++ u16 csr;
++ u8 mode;
++
++#ifdef CONFIG_USB_INVENTRA_DMA
++ if (length > channel->max_len)
++ length = channel->max_len;
++
++ csr = musb_readw(epio, MUSB_TXCSR);
++ if (length > pkt_size) {
++ mode = 1;
++ csr |= MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAMODE
++ | MUSB_TXCSR_DMAENAB;
++ } else {
++ mode = 0;
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
++ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
++ }
++ channel->desired_mode = mode;
++ musb_writew(epio, MUSB_TXCSR, csr);
++#else
++ if (!is_cppi_enabled() && !tusb_dma_omap())
++ return false;
++
++ channel->actual_len = 0;
++
++ /*
++ * TX uses "RNDIS" mode automatically but needs help
++ * to identify the zero-length-final-packet case.
++ */
++ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
++#endif
++
++ qh->segsize = length;
++
++ if (!dma->channel_program(channel, pkt_size, mode,
++ urb->transfer_dma + offset, length)) {
++ dma->channel_release(channel);
++ hw_ep->tx_channel = NULL;
++
++ csr = musb_readw(epio, MUSB_TXCSR);
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB);
++ musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_H_WZC_BITS);
++ return false;
++ }
++ return true;
++}
+
+ /*
+ * Program an HDRC endpoint as per the given URB
+ * Context: irqs blocked, controller lock held
+ */
+ static void musb_ep_program(struct musb *musb, u8 epnum,
+- struct urb *urb, unsigned int is_out,
+- u8 *buf, u32 len)
++ struct urb *urb, int is_out,
++ u8 *buf, u32 offset, u32 len)
+ {
+ struct dma_controller *dma_controller;
+ struct dma_channel *dma_channel;
+@@ -764,82 +818,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ else
+ load_count = min((u32) packet_sz, len);
+
+-#ifdef CONFIG_USB_INVENTRA_DMA
+- if (dma_channel) {
+- qh->segsize = min(len, dma_channel->max_len);
+- if (qh->segsize <= packet_sz)
+- dma_channel->desired_mode = 0;
+- else
+- dma_channel->desired_mode = 1;
+-
+- if (dma_channel->desired_mode == 0) {
+- /* Against the programming guide */
+- csr |= (MUSB_TXCSR_DMAENAB);
+- } else
+- csr |= (MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_DMAMODE);
+- musb_writew(epio, MUSB_TXCSR, csr);
+-
+- dma_ok = dma_controller->channel_program(
+- dma_channel, packet_sz,
+- dma_channel->desired_mode,
+- urb->transfer_dma,
+- qh->segsize);
+- if (dma_ok) {
+- load_count = 0;
+- } else {
+- dma_controller->channel_release(dma_channel);
+- if (is_out)
+- hw_ep->tx_channel = NULL;
+- else
+- hw_ep->rx_channel = NULL;
+- dma_channel = NULL;
+-
+- /*
+- * The programming guide says that we must
+- * clear the DMAENAB bit before DMAMODE...
+- */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_AUTOSET);
+- musb_writew(epio, MUSB_TXCSR, csr);
+- csr &= ~MUSB_TXCSR_DMAMODE;
+- musb_writew(epio, MUSB_TXCSR, csr);
+- }
+- }
+-#endif
+-
+- /* candidate for DMA */
+- if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
+-
+- /* Defer enabling DMA */
+- dma_channel->actual_len = 0L;
+- qh->segsize = len;
+-
+- /* TX uses "rndis" mode automatically, but needs help
+- * to identify the zero-length-final-packet case.
+- */
+- dma_ok = dma_controller->channel_program(
+- dma_channel, packet_sz,
+- (urb->transfer_flags
+- & URB_ZERO_PACKET)
+- == URB_ZERO_PACKET,
+- urb->transfer_dma,
+- qh->segsize);
+- if (dma_ok) {
+- load_count = 0;
+- } else {
+- dma_controller->channel_release(dma_channel);
+- hw_ep->tx_channel = NULL;
+- dma_channel = NULL;
+-
+- /* REVISIT there's an error path here that
+- * needs handling: can't do dma, but
+- * there's no pio buffer address...
+- */
+- }
+- }
++ if (dma_channel && musb_tx_dma_program(dma_controller,
++ hw_ep, qh, urb, offset, len))
++ load_count = 0;
+
+ if (load_count) {
+ /* PIO to load FIFO */
+@@ -899,7 +880,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ dma_channel, packet_sz,
+ !(urb->transfer_flags
+ & URB_SHORT_NOT_OK),
+- urb->transfer_dma,
++ urb->transfer_dma + offset,
+ qh->segsize);
+ if (!dma_ok) {
+ dma_controller->channel_release(
+@@ -1142,8 +1123,8 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ int pipe;
+ bool done = false;
+ u16 tx_csr;
+- size_t wLength = 0;
+- u8 *buf = NULL;
++ size_t length = 0;
++ size_t offset = 0;
+ struct urb *urb;
+ struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
+ void __iomem *epio = hw_ep->regs;
+@@ -1161,7 +1142,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ /* with CPPI, DMA sometimes triggers "extra" irqs */
+ if (!urb) {
+ DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
+- goto finish;
++ return;
+ }
+
+ pipe = urb->pipe;
+@@ -1198,7 +1179,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_H_WZC_BITS
+ | MUSB_TXCSR_TXPKTRDY);
+- goto finish;
++ return;
+ }
+
+ if (status) {
+@@ -1230,29 +1211,28 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ /* second cppi case */
+ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
+ DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
+- goto finish;
+-
++ return;
+ }
+
+- /* REVISIT this looks wrong... */
+ if (!status || dma || usb_pipeisoc(pipe)) {
+ if (dma)
+- wLength = dma->actual_len;
++ length = dma->actual_len;
+ else
+- wLength = qh->segsize;
+- qh->offset += wLength;
++ length = qh->segsize;
++ qh->offset += length;
+
+ if (usb_pipeisoc(pipe)) {
+ struct usb_iso_packet_descriptor *d;
+
+ d = urb->iso_frame_desc + qh->iso_idx;
+- d->actual_length = qh->segsize;
++ d->actual_length = length;
++ d->status = status;
+ if (++qh->iso_idx >= urb->number_of_packets) {
+ done = true;
+ } else {
+ d++;
+- buf = urb->transfer_buffer + d->offset;
+- wLength = d->length;
++ offset = d->offset;
++ length = d->length;
+ }
+ } else if (dma) {
+ done = true;
+@@ -1265,10 +1245,8 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ & URB_ZERO_PACKET))
+ done = true;
+ if (!done) {
+- buf = urb->transfer_buffer
+- + qh->offset;
+- wLength = urb->transfer_buffer_length
+- - qh->offset;
++ offset = qh->offset;
++ length = urb->transfer_buffer_length - offset;
+ }
+ }
+ }
+@@ -1287,28 +1265,31 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ urb->status = status;
+ urb->actual_length = qh->offset;
+ musb_advance_schedule(musb, urb, hw_ep, USB_DIR_OUT);
++ return;
++ } else if (usb_pipeisoc(pipe) && dma) {
++ if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb,
++ offset, length))
++ return;
++ } else if (tx_csr & MUSB_TXCSR_DMAENAB) {
++ DBG(1, "not complete, but DMA enabled?\n");
++ return;
++ }
+
+- } else if (!(tx_csr & MUSB_TXCSR_DMAENAB)) {
+- /* WARN_ON(!buf); */
+-
+- /* REVISIT: some docs say that when hw_ep->tx_double_buffered,
+- * (and presumably, fifo is not half-full) we should write TWO
+- * packets before updating TXCSR ... other docs disagree ...
+- */
+- /* PIO: start next packet in this URB */
+- if (wLength > qh->maxpacket)
+- wLength = qh->maxpacket;
+- musb_write_fifo(hw_ep, wLength, buf);
+- qh->segsize = wLength;
+-
+- musb_ep_select(mbase, epnum);
+- musb_writew(epio, MUSB_TXCSR,
+- MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
+- } else
+- DBG(1, "not complete, but dma enabled?\n");
++ /*
++ * PIO: start next packet in this URB.
++ *
++ * REVISIT: some docs say that when hw_ep->tx_double_buffered,
++ * (and presumably, FIFO is not half-full) we should write *two*
++ * packets before updating TXCSR; other docs disagree...
++ */
++ if (length > qh->maxpacket)
++ length = qh->maxpacket;
++ musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset);
++ qh->segsize = length;
+
+-finish:
+- return;
++ musb_ep_select(mbase, epnum);
++ musb_writew(epio, MUSB_TXCSR,
++ MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
+ }
+
+
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch b/recipes/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch
new file mode 100644
index 0000000000..2bbde84c16
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch
@@ -0,0 +1,56 @@
+From b9a61b80ea89d9d6d78a23d96a28df94fd612298 Mon Sep 17 00:00:00 2001
+From: Kim Kyuwon <q1.kim-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+Date: Thu, 26 Mar 2009 18:56:51 -0700
+Subject: [PATCH] musb: fix possible panic while resuming
+
+During driver resume processing, musb could cause a kernel panic.
+Fix by enabling the clock earlier, with the resume_early method.
+
+Signed-off-by: Kim Kyuwon <q1.kim-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 8 ++------
+ 1 files changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index 338cd16..3019725 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -2170,16 +2170,13 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message)
+ return 0;
+ }
+
+-static int musb_resume(struct platform_device *pdev)
++static int musb_resume_early(struct platform_device *pdev)
+ {
+- unsigned long flags;
+ struct musb *musb = dev_to_musb(&pdev->dev);
+
+ if (!musb->clock)
+ return 0;
+
+- spin_lock_irqsave(&musb->lock, flags);
+-
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ else
+@@ -2189,7 +2186,6 @@ static int musb_resume(struct platform_device *pdev)
+ * unless for some reason the whole soc powered down and we're
+ * not treating that as a whole-system restart (e.g. swsusp)
+ */
+- spin_unlock_irqrestore(&musb->lock, flags);
+ return 0;
+ }
+
+@@ -2207,7 +2203,7 @@ static struct platform_driver musb_driver = {
+ .remove = __devexit_p(musb_remove),
+ .shutdown = musb_shutdown,
+ .suspend = musb_suspend,
+- .resume = musb_resume,
++ .resume_early = musb_resume_early,
+ };
+
+ /*-------------------------------------------------------------------------*/
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch b/recipes/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch
new file mode 100644
index 0000000000..0202871d41
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch
@@ -0,0 +1,91 @@
+From 2658f7c9029967501cd4d749364f2e02d02eebd5 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:54:21 -0700
+Subject: [PATCH] musb_host: refactor musb_save_toggle() (take 2)
+
+Refactor musb_save_toggle() as follows:
+
+ - replace 'struct musb_hw_ep *ep' parameter by 'struct
+ musb_qh *qh' to avoid re-calculating this value
+
+ - move usb_settogle() call out of the *if* operator.
+
+This is a net minor shrink of source and object code.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 35 ++++++++++++-----------------------
+ 1 files changed, 12 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index f6e84a0..dc32ce4 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -318,35 +318,24 @@ __acquires(musb->lock)
+ spin_lock(&musb->lock);
+ }
+
+-/* for bulk/interrupt endpoints only */
+-static inline void
+-musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb)
++/* For bulk/interrupt endpoints only */
++static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
++ struct urb *urb)
+ {
+- struct usb_device *udev = urb->dev;
++ void __iomem *epio = qh->hw_ep->regs;
+ u16 csr;
+- void __iomem *epio = ep->regs;
+- struct musb_qh *qh;
+
+- /* FIXME: the current Mentor DMA code seems to have
++ /*
++ * FIXME: the current Mentor DMA code seems to have
+ * problems getting toggle correct.
+ */
+
+- if (is_in || ep->is_shared_fifo)
+- qh = ep->in_qh;
++ if (is_in)
++ csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
+ else
+- qh = ep->out_qh;
++ csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
+
+- if (!is_in) {
+- csr = musb_readw(epio, MUSB_TXCSR);
+- usb_settoggle(udev, qh->epnum, 1,
+- (csr & MUSB_TXCSR_H_DATATOGGLE)
+- ? 1 : 0);
+- } else {
+- csr = musb_readw(epio, MUSB_RXCSR);
+- usb_settoggle(udev, qh->epnum, 0,
+- (csr & MUSB_RXCSR_H_DATATOGGLE)
+- ? 1 : 0);
+- }
++ usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
+ }
+
+ /* caller owns controller lock, irqs are blocked */
+@@ -362,7 +351,7 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+- musb_save_toggle(ep, is_in, urb);
++ musb_save_toggle(qh, is_in, urb);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (status == 0 && urb->error_count)
+@@ -1362,7 +1351,7 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep)
+ urb->actual_length += dma->actual_len;
+ dma->actual_len = 0L;
+ }
+- musb_save_toggle(ep, 1, urb);
++ musb_save_toggle(cur_qh, 1, urb);
+
+ /* move cur_qh to end of queue */
+ list_move_tail(&cur_qh->ring, &musb->in_bulk);
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch b/recipes/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch
new file mode 100644
index 0000000000..08e08a8538
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch
@@ -0,0 +1,32 @@
+From 7766f2ea909b73f56d21746485069e02839b75f1 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:53:32 -0700
+Subject: [PATCH] musb_gadget: suppress "parasitic" TX interrupts with CPPI
+
+Suppress "parasitic" endpoint interrupts in the DMA mode
+when using CPPI DMA driver; they're caused by the MUSB gadget
+driver using the DMA request mode 0 instead of the mode 1.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_gadget.c | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index f79440c..bc197b2 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -349,7 +349,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ #elif defined(CONFIG_USB_TI_CPPI_DMA)
+ /* program endpoint CSR first, then setup DMA */
+ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
+- csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB;
++ csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
++ MUSB_TXCSR_MODE;
+ musb_writew(epio, MUSB_TXCSR,
+ (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
+ | csr);
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch b/recipes/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch
new file mode 100644
index 0000000000..7115b152d9
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch
@@ -0,0 +1,202 @@
+From 5424305125492a2417bde7c6d23ee4b84e25f6be Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:52:43 -0700
+Subject: [PATCH] musb_gadget: fix unhandled endpoint 0 IRQs
+
+The gadget EP0 code routinely ignores an interrupt at end of
+the data phase because of musb_g_ep0_giveback() resetting the
+state machine to "idle, waiting for SETUP" phase prematurely.
+
+The driver also prematurely leaves the status phase on
+receiving the SetupEnd interrupt.
+
+As there were still unhandled endpoint 0 interrupts happening
+from time to time after fixing these issues, there turned to
+be yet another culprit: two distinct gadget states collapsed
+into one.
+
+The (missing) state that comes after STATUS IN/OUT states was
+typically indiscernible from them since the corresponding
+interrupts tend to happen within too little period of time
+(due to only a zero-length status packet in between) and so
+they got coalesced; yet this state is not the same as the next
+one which is associated with the reception of a SETUP packet.
+
+Adding this extra state seems to have fixed the rest of the
+unhandled interrupts that generic_interrupt() and
+davinci_interrupt() hid by faking their result and only
+emitting a debug message -- so, stop doing that.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/davinci.c | 7 +----
+ drivers/usb/musb/musb_core.c | 8 +-----
+ drivers/usb/musb/musb_core.h | 3 +-
+ drivers/usb/musb/musb_gadget_ep0.c | 45 +++++++++++++++++++++++++++++++----
+ 4 files changed, 43 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
+index 2dc7606..399c435 100644
+--- a/drivers/usb/musb/davinci.c
++++ b/drivers/usb/musb/davinci.c
+@@ -357,12 +357,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+- /* REVISIT we sometimes get unhandled IRQs
+- * (e.g. ep0). not clear why...
+- */
+- if (retval != IRQ_HANDLED)
+- DBG(5, "unhandled? %08x\n", tmp);
+- return IRQ_HANDLED;
++ return retval;
+ }
+
+ int musb_platform_set_mode(struct musb *musb, u8 mode)
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index 3019725..a1de43b 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -1481,13 +1481,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+- /* REVISIT we sometimes get spurious IRQs on g_ep0
+- * not clear why...
+- */
+- if (retval != IRQ_HANDLED)
+- DBG(5, "spurious?\n");
+-
+- return IRQ_HANDLED;
++ return retval;
+ }
+
+ #else
+diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
+index adf1806..f56a56c 100644
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -171,7 +171,8 @@ enum musb_h_ep0_state {
+
+ /* peripheral side ep0 states */
+ enum musb_g_ep0_state {
+- MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */
++ MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */
++ MUSB_EP0_STAGE_SETUP, /* received SETUP */
+ MUSB_EP0_STAGE_TX, /* IN data */
+ MUSB_EP0_STAGE_RX, /* OUT data */
+ MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
+diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
+index 3f5e30d..ec0e899 100644
+--- a/drivers/usb/musb/musb_gadget_ep0.c
++++ b/drivers/usb/musb/musb_gadget_ep0.c
+@@ -4,6 +4,7 @@
+ * Copyright 2005 Mentor Graphics Corporation
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (C) 2006-2007 Nokia Corporation
++ * Copyright (C) 2008-2009 MontaVista Software, Inc. <source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -58,7 +59,8 @@
+ static char *decode_ep0stage(u8 stage)
+ {
+ switch (stage) {
+- case MUSB_EP0_STAGE_SETUP: return "idle";
++ case MUSB_EP0_STAGE_IDLE: return "idle";
++ case MUSB_EP0_STAGE_SETUP: return "setup";
+ case MUSB_EP0_STAGE_TX: return "in";
+ case MUSB_EP0_STAGE_RX: return "out";
+ case MUSB_EP0_STAGE_ACKWAIT: return "wait";
+@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
+ musb_writew(regs, MUSB_CSR0,
+ csr & ~MUSB_CSR0_P_SENTSTALL);
+ retval = IRQ_HANDLED;
+- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
++ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
+ csr = musb_readw(regs, MUSB_CSR0);
+ }
+
+@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
+ if (csr & MUSB_CSR0_P_SETUPEND) {
+ musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
+ retval = IRQ_HANDLED;
+- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
++ /* Transition into the early status phase */
++ switch (musb->ep0_state) {
++ case MUSB_EP0_STAGE_TX:
++ musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
++ break;
++ case MUSB_EP0_STAGE_RX:
++ musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
++ break;
++ default:
++ ERR("SetupEnd came in a wrong ep0stage %s",
++ decode_ep0stage(musb->ep0_state));
++ }
+ csr = musb_readw(regs, MUSB_CSR0);
+ /* NOTE: request may need completion */
+ }
+@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
+ if (req)
+ musb_g_ep0_giveback(musb, req);
+ }
++
++ /*
++ * In case when several interrupts can get coalesced,
++ * check to see if we've already received a SETUP packet...
++ */
++ if (csr & MUSB_CSR0_RXPKTRDY)
++ goto setup;
++
++ retval = IRQ_HANDLED;
++ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
++ break;
++
++ case MUSB_EP0_STAGE_IDLE:
++ /*
++ * This state is typically (but not always) indiscernible
++ * from the status states since the corresponding interrupts
++ * tend to happen within too little period of time (with only
++ * a zero-length packet in between) and so get coalesced...
++ */
+ retval = IRQ_HANDLED;
+ musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+ /* FALLTHROUGH */
+
+ case MUSB_EP0_STAGE_SETUP:
++setup:
+ if (csr & MUSB_CSR0_RXPKTRDY) {
+ struct usb_ctrlrequest setup;
+ int handled = 0;
+@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
+ stall:
+ DBG(3, "stall (%d)\n", handled);
+ musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
+- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
++ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
+ finish:
+ musb_writew(regs, MUSB_CSR0,
+ musb->ackpend);
+@@ -803,7 +836,7 @@ finish:
+ /* "can't happen" */
+ WARN_ON(1);
+ musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
+- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
++ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
+ break;
+ }
+
+@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
+
+ csr |= MUSB_CSR0_P_SENDSTALL;
+ musb_writew(regs, MUSB_CSR0, csr);
+- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
++ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
+ musb->ackpend = 0;
+ break;
+ default:
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch b/recipes/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch
new file mode 100644
index 0000000000..a2f54ff47b
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch
@@ -0,0 +1,146 @@
+From f9ca8154cf395ec00129f12016697ef610a826ff Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:55:16 -0700
+Subject: [PATCH] musb_host: factor out musb_ep_{get|set}_qh()
+
+Factor out the often used code to get/set the active 'qh'
+pointer for the hardware endpoint. Change the way the case
+of a shared FIFO is handled by setting *both* 'in_qh' and
+'out_qh' fields of 'struct musb_hw_ep'. That seems more
+consistent and makes getting to the current 'qh' easy when
+the code knows the direction beforehand.
+
+While at it, turn some assignments into intializers and
+fix declaration style in the vicinity.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 56 ++++++++++++++++-------------------------
+ 1 files changed, 22 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index dc32ce4..bc89079 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -178,6 +178,19 @@ static inline void cppi_host_txdma_start(struct musb_hw_ep *ep)
+ musb_writew(ep->regs, MUSB_TXCSR, txcsr);
+ }
+
++static void musb_ep_set_qh(struct musb_hw_ep *ep, int is_in, struct musb_qh *qh)
++{
++ if (is_in != 0 || ep->is_shared_fifo)
++ ep->in_qh = qh;
++ if (is_in == 0 || ep->is_shared_fifo)
++ ep->out_qh = qh;
++}
++
++static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in)
++{
++ return is_in ? ep->in_qh : ep->out_qh;
++}
++
+ /*
+ * Start the URB at the front of an endpoint's queue
+ * end must be claimed from the caller.
+@@ -207,7 +220,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* control transfers always start with SETUP */
+ is_in = 0;
+- hw_ep->out_qh = qh;
+ musb->ep0_stage = MUSB_EP0_START;
+ buf = urb->setup_packet;
+ len = 8;
+@@ -236,10 +248,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ epnum, buf + offset, len);
+
+ /* Configure endpoint */
+- if (is_in || hw_ep->is_shared_fifo)
+- hw_ep->in_qh = qh;
+- else
+- hw_ep->out_qh = qh;
++ musb_ep_set_qh(hw_ep, is_in, qh);
+ musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len);
+
+ /* transmit may have more work: start it when it is time */
+@@ -374,11 +383,8 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+ else
+ ep->tx_reinit = 1;
+
+- /* clobber old pointers to this qh */
+- if (is_in || ep->is_shared_fifo)
+- ep->in_qh = NULL;
+- else
+- ep->out_qh = NULL;
++ /* Clobber old pointers to this qh */
++ musb_ep_set_qh(ep, is_in, NULL);
+ qh->hep->hcpriv = NULL;
+
+ switch (qh->type) {
+@@ -421,12 +427,7 @@ static void
+ musb_advance_schedule(struct musb *musb, struct urb *urb,
+ struct musb_hw_ep *hw_ep, int is_in)
+ {
+- struct musb_qh *qh;
+-
+- if (is_in || hw_ep->is_shared_fifo)
+- qh = hw_ep->in_qh;
+- else
+- qh = hw_ep->out_qh;
++ struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in);
+
+ if (urb->status == -EINPROGRESS)
+ qh = musb_giveback(qh, urb, 0);
+@@ -689,15 +690,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ void __iomem *mbase = musb->mregs;
+ struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
+ void __iomem *epio = hw_ep->regs;
+- struct musb_qh *qh;
+- u16 packet_sz;
+-
+- if (!is_out || hw_ep->is_shared_fifo)
+- qh = hw_ep->in_qh;
+- else
+- qh = hw_ep->out_qh;
+-
+- packet_sz = qh->maxpacket;
++ struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out);
++ u16 packet_sz = qh->maxpacket;
+
+ DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s "
+ "h_addr%02x h_port%02x bytes %d\n",
+@@ -1114,17 +1108,14 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ u16 tx_csr;
+ size_t length = 0;
+ size_t offset = 0;
+- struct urb *urb;
+ struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
+ void __iomem *epio = hw_ep->regs;
+- struct musb_qh *qh = hw_ep->is_shared_fifo ? hw_ep->in_qh
+- : hw_ep->out_qh;
++ struct musb_qh *qh = hw_ep->out_qh;
++ struct urb *urb = next_urb(qh);
+ u32 status = 0;
+ void __iomem *mbase = musb->mregs;
+ struct dma_channel *dma;
+
+- urb = next_urb(qh);
+-
+ musb_ep_select(mbase, epnum);
+ tx_csr = musb_readw(epio, MUSB_TXCSR);
+
+@@ -1741,10 +1732,7 @@ static int musb_schedule(
+ epnum++, hw_ep++) {
+ int diff;
+
+- if (is_in || hw_ep->is_shared_fifo) {
+- if (hw_ep->in_qh != NULL)
+- continue;
+- } else if (hw_ep->out_qh != NULL)
++ if (musb_ep_get_qh(hw_ep, is_in) != NULL)
+ continue;
+
+ if (hw_ep == musb->bulk_ep)
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch b/recipes/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch
new file mode 100644
index 0000000000..4a520dff87
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch
@@ -0,0 +1,132 @@
+From 013056a09afd324e729d64b9a0e66a004604e1d6 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:58:31 -0700
+Subject: [PATCH] musb_host: refactor URB giveback
+
+As musb_advance_schedule() is now the only remaning
+caller of musb_giveback() (and the only valid context
+of such call), just fold the latter into the former
+and then rename __musb_giveback() into musb_giveback().
+
+This is a net minor shrink.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 54 +++++++++++++++--------------------------
+ 1 files changed, 20 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index bc89079..e833959 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -292,9 +292,8 @@ start:
+ }
+ }
+
+-/* caller owns controller lock, irqs are blocked */
+-static void
+-__musb_giveback(struct musb *musb, struct urb *urb, int status)
++/* Context: caller owns controller lock, IRQs are blocked */
++static void musb_giveback(struct musb *musb, struct urb *urb, int status)
+ __releases(musb->lock)
+ __acquires(musb->lock)
+ {
+@@ -347,14 +346,22 @@ static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
+ usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
+ }
+
+-/* caller owns controller lock, irqs are blocked */
+-static struct musb_qh *
+-musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
++/*
++ * Advance this hardware endpoint's queue, completing the specified URB and
++ * advancing to either the next URB queued to that qh, or else invalidating
++ * that qh and advancing to the next qh scheduled after the current one.
++ *
++ * Context: caller owns controller lock, IRQs are blocked
++ */
++static void musb_advance_schedule(struct musb *musb, struct urb *urb,
++ struct musb_hw_ep *hw_ep, int is_in)
+ {
++ struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in);
+ struct musb_hw_ep *ep = qh->hw_ep;
+- struct musb *musb = ep->musb;
+- int is_in = usb_pipein(urb->pipe);
+ int ready = qh->is_ready;
++ int status;
++
++ status = (urb->status == -EINPROGRESS) ? 0 : urb->status;
+
+ /* save toggle eagerly, for paranoia */
+ switch (qh->type) {
+@@ -363,13 +370,13 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+ musb_save_toggle(qh, is_in, urb);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+- if (status == 0 && urb->error_count)
++ if (urb->error_count)
+ status = -EXDEV;
+ break;
+ }
+
+ qh->is_ready = 0;
+- __musb_giveback(musb, urb, status);
++ musb_giveback(musb, urb, status);
+ qh->is_ready = ready;
+
+ /* reclaim resources (and bandwidth) ASAP; deschedule it, and
+@@ -413,31 +420,10 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+ break;
+ }
+ }
+- return qh;
+-}
+-
+-/*
+- * Advance this hardware endpoint's queue, completing the specified urb and
+- * advancing to either the next urb queued to that qh, or else invalidating
+- * that qh and advancing to the next qh scheduled after the current one.
+- *
+- * Context: caller owns controller lock, irqs are blocked
+- */
+-static void
+-musb_advance_schedule(struct musb *musb, struct urb *urb,
+- struct musb_hw_ep *hw_ep, int is_in)
+-{
+- struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in);
+-
+- if (urb->status == -EINPROGRESS)
+- qh = musb_giveback(qh, urb, 0);
+- else
+- qh = musb_giveback(qh, urb, urb->status);
+
+ if (qh != NULL && qh->is_ready) {
+ DBG(4, "... next ep%d %cX urb %p\n",
+- hw_ep->epnum, is_in ? 'R' : 'T',
+- next_urb(qh));
++ hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
+ musb_start_urb(musb, is_in, qh);
+ }
+ }
+@@ -2080,7 +2066,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+
+ ret = 0;
+ qh->is_ready = 0;
+- __musb_giveback(musb, urb, 0);
++ musb_giveback(musb, urb, 0);
+ qh->is_ready = ready;
+
+ /* If nothing else (usually musb_giveback) is using it
+@@ -2164,7 +2150,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+ * will activate any of these as it advances.
+ */
+ while (!list_empty(&hep->urb_list))
+- __musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
++ musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
+
+ hep->hcpriv = NULL;
+ list_del(&qh->ring);
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch b/recipes/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch
new file mode 100644
index 0000000000..bf3d6e7021
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch
@@ -0,0 +1,167 @@
+From b91b067c531c9322f3719951b07303e790b13475 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:59:46 -0700
+Subject: [PATCH] musb: split out CPPI interrupt handler
+
+As DaVinci DM646x has a dedicated CPPI DMA interrupt, replace
+cppi_completion() (which has always been kind of layering
+violation) by a complete CPPI interrupt handler.
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: only cppi_dma.c needs platform
+device header, not cppi_dma.h ]
+
+Signed-off-by: Dmitry Krivoschekov <dkrivoschekov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/cppi_dma.c | 34 +++++++++++++++++++++++++++++++---
+ drivers/usb/musb/cppi_dma.h | 6 ++++--
+ drivers/usb/musb/davinci.c | 14 ++++----------
+ 3 files changed, 39 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
+index ac7227c..6ff3c67 100644
+--- a/drivers/usb/musb/cppi_dma.c
++++ b/drivers/usb/musb/cppi_dma.c
+@@ -6,6 +6,7 @@
+ * The TUSB6020, using VLYNQ, has CPPI that looks much like DaVinci.
+ */
+
++#include <linux/platform_device.h>
+ #include <linux/usb.h>
+
+ #include "musb_core.h"
+@@ -1145,17 +1146,27 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
+ return completed;
+ }
+
+-void cppi_completion(struct musb *musb, u32 rx, u32 tx)
++irqreturn_t cppi_interrupt(int irq, void *dev_id)
+ {
+- void __iomem *tibase;
+- int i, index;
++ struct musb *musb = dev_id;
+ struct cppi *cppi;
++ void __iomem *tibase;
+ struct musb_hw_ep *hw_ep = NULL;
++ u32 rx, tx;
++ int i, index;
+
+ cppi = container_of(musb->dma_controller, struct cppi, controller);
+
+ tibase = musb->ctrl_base;
+
++ tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
++ rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
++
++ if (!tx && !rx)
++ return IRQ_NONE;
++
++ DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
++
+ /* process TX channels */
+ for (index = 0; tx; tx = tx >> 1, index++) {
+ struct cppi_channel *tx_ch;
+@@ -1293,6 +1304,8 @@ void cppi_completion(struct musb *musb, u32 rx, u32 tx)
+
+ /* write to CPPI EOI register to re-enable interrupts */
+ musb_writel(tibase, DAVINCI_CPPI_EOI_REG, 0);
++
++ return IRQ_HANDLED;
+ }
+
+ /* Instantiate a software object representing a DMA controller. */
+@@ -1300,6 +1313,9 @@ struct dma_controller *__init
+ dma_controller_create(struct musb *musb, void __iomem *mregs)
+ {
+ struct cppi *controller;
++ struct device *dev = musb->controller;
++ struct platform_device *pdev = to_platform_device(dev);
++ int irq = platform_get_irq(pdev, 1);
+
+ controller = kzalloc(sizeof *controller, GFP_KERNEL);
+ if (!controller)
+@@ -1330,6 +1346,15 @@ dma_controller_create(struct musb *musb, void __iomem *mregs)
+ return NULL;
+ }
+
++ if (irq > 0) {
++ if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) {
++ dev_err(dev, "request_irq %d failed!\n", irq);
++ dma_controller_destroy(&controller->controller);
++ return NULL;
++ }
++ controller->irq = irq;
++ }
++
+ return &controller->controller;
+ }
+
+@@ -1342,6 +1367,9 @@ void dma_controller_destroy(struct dma_controller *c)
+
+ cppi = container_of(c, struct cppi, controller);
+
++ if (cppi->irq)
++ free_irq(cppi->irq, cppi->musb);
++
+ /* assert: caller stopped the controller first */
+ dma_pool_destroy(cppi->pool);
+
+diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h
+index 729b407..8a39de3 100644
+--- a/drivers/usb/musb/cppi_dma.h
++++ b/drivers/usb/musb/cppi_dma.h
+@@ -119,6 +119,8 @@ struct cppi {
+ void __iomem *mregs; /* Mentor regs */
+ void __iomem *tibase; /* TI/CPPI regs */
+
++ int irq;
++
+ struct cppi_channel tx[4];
+ struct cppi_channel rx[4];
+
+@@ -127,7 +129,7 @@ struct cppi {
+ struct list_head tx_complete;
+ };
+
+-/* irq handling hook */
+-extern void cppi_completion(struct musb *, u32 rx, u32 tx);
++/* CPPI IRQ handler */
++extern irqreturn_t cppi_interrupt(int, void *);
+
+ #endif /* end of ifndef _CPPI_DMA_H_ */
+diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
+index 399c435..9fd74bf 100644
+--- a/drivers/usb/musb/davinci.c
++++ b/drivers/usb/musb/davinci.c
+@@ -250,6 +250,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = __hci;
+ void __iomem *tibase = musb->ctrl_base;
++ struct cppi *cppi;
+ u32 tmp;
+
+ spin_lock_irqsave(&musb->lock, flags);
+@@ -266,16 +267,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
+ /* CPPI interrupts share the same IRQ line, but have their own
+ * mask, state, "vector", and EOI registers.
+ */
+- if (is_cppi_enabled()) {
+- u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
+- u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
+-
+- if (cppi_tx || cppi_rx) {
+- DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
+- cppi_completion(musb, cppi_rx, cppi_tx);
+- retval = IRQ_HANDLED;
+- }
+- }
++ cppi = container_of(musb->dma_controller, struct cppi, controller);
++ if (is_cppi_enabled() && musb->dma_controller && !cppi->irq)
++ retval = cppi_interrupt(irq, __hci);
+
+ /* ack and handle non-CPPI interrupts */
+ tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch b/recipes/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch
new file mode 100644
index 0000000000..c0e57155c8
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch
@@ -0,0 +1,158 @@
+From 69242ddd26151d45f46011cf7abc581b14699fb2 Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:56:26 -0700
+Subject: [PATCH] musb_host: simplify check for active URB
+
+The existance of the scheduling list shouldn't matter in
+determining whether there's currectly an URB executing on a
+hardware endpoint. What should actually matter is the 'in_qh'
+or 'out_qh' fields of the 'struct musb_hw_ep' -- those are
+set in musb_start_urb() and cleared in musb_giveback() when
+the endpoint's URB list drains. Hence we should be able to
+replace the big *switch* statements in musb_urb_dequeue()
+and musb_h_disable() with mere musb_ep_get_qh() calls...
+
+While at it, do some more changes:
+
+ - add 'is_in' variable to musb_urb_dequeue();
+
+ - remove the unnecessary 'epnum' variable from musb_h_disable();
+
+ - fix the comment style in the vicinity.
+
+This is a minor shrink of source and object code.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 72 ++++++++---------------------------------
+ 1 files changed, 14 insertions(+), 58 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index e833959..e121e0e 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -2008,14 +2008,14 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+ {
+ struct musb *musb = hcd_to_musb(hcd);
+ struct musb_qh *qh;
+- struct list_head *sched;
+ unsigned long flags;
++ int is_in = usb_pipein(urb->pipe);
+ int ret;
+
+ DBG(4, "urb=%p, dev%d ep%d%s\n", urb,
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+- usb_pipein(urb->pipe) ? "in" : "out");
++ is_in ? "in" : "out");
+
+ spin_lock_irqsave(&musb->lock, flags);
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+@@ -2026,45 +2026,23 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+ if (!qh)
+ goto done;
+
+- /* Any URB not actively programmed into endpoint hardware can be
++ /*
++ * Any URB not actively programmed into endpoint hardware can be
+ * immediately given back; that's any URB not at the head of an
+ * endpoint queue, unless someday we get real DMA queues. And even
+ * if it's at the head, it might not be known to the hardware...
+ *
+- * Otherwise abort current transfer, pending dma, etc.; urb->status
++ * Otherwise abort current transfer, pending DMA, etc.; urb->status
+ * has already been updated. This is a synchronous abort; it'd be
+ * OK to hold off until after some IRQ, though.
++ *
++ * NOTE: qh is invalid unless !list_empty(&hep->urb_list)
+ */
+- if (!qh->is_ready || urb->urb_list.prev != &qh->hep->urb_list)
+- ret = -EINPROGRESS;
+- else {
+- switch (qh->type) {
+- case USB_ENDPOINT_XFER_CONTROL:
+- sched = &musb->control;
+- break;
+- case USB_ENDPOINT_XFER_BULK:
+- if (qh->mux == 1) {
+- if (usb_pipein(urb->pipe))
+- sched = &musb->in_bulk;
+- else
+- sched = &musb->out_bulk;
+- break;
+- }
+- default:
+- /* REVISIT when we get a schedule tree, periodic
+- * transfers won't always be at the head of a
+- * singleton queue...
+- */
+- sched = NULL;
+- break;
+- }
+- }
+-
+- /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
+- if (ret < 0 || (sched && qh != first_qh(sched))) {
++ if (!qh->is_ready
++ || urb->urb_list.prev != &qh->hep->urb_list
++ || musb_ep_get_qh(qh->hw_ep, is_in) != qh) {
+ int ready = qh->is_ready;
+
+- ret = 0;
+ qh->is_ready = 0;
+ musb_giveback(musb, urb, 0);
+ qh->is_ready = ready;
+@@ -2088,13 +2066,11 @@ done:
+ static void
+ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+ {
+- u8 epnum = hep->desc.bEndpointAddress;
++ u8 is_in = hep->desc.bEndpointAddress & USB_DIR_IN;
+ unsigned long flags;
+ struct musb *musb = hcd_to_musb(hcd);
+- u8 is_in = epnum & USB_DIR_IN;
+ struct musb_qh *qh;
+ struct urb *urb;
+- struct list_head *sched;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+@@ -2102,31 +2078,11 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+ if (qh == NULL)
+ goto exit;
+
+- switch (qh->type) {
+- case USB_ENDPOINT_XFER_CONTROL:
+- sched = &musb->control;
+- break;
+- case USB_ENDPOINT_XFER_BULK:
+- if (qh->mux == 1) {
+- if (is_in)
+- sched = &musb->in_bulk;
+- else
+- sched = &musb->out_bulk;
+- break;
+- }
+- default:
+- /* REVISIT when we get a schedule tree, periodic transfers
+- * won't always be at the head of a singleton queue...
+- */
+- sched = NULL;
+- break;
+- }
+-
+- /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
++ /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
+
+- /* kick first urb off the hardware, if needed */
++ /* Kick the first URB off the hardware, if needed */
+ qh->is_ready = 0;
+- if (!sched || qh == first_qh(sched)) {
++ if (musb_ep_get_qh(qh->hw_ep, is_in) == qh) {
+ urb = next_urb(qh);
+
+ /* make software (then hardware) stop ASAP */
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch b/recipes/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch
new file mode 100644
index 0000000000..d89eaadd61
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch
@@ -0,0 +1,58 @@
+From d408894fa4263440ed8a9e68566bacea7e6f6bed Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Fri, 27 Mar 2009 12:57:50 -0700
+Subject: [PATCH] musb_host: streamline musb_cleanup_urb() calls
+
+The argument for the 'is_in' parameter of musb_cleanup_urb()
+is always extracted from an URB that's passed to the function.
+So that parameter is superfluous; remove it.
+
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_host.c | 9 +++++----
+ 1 files changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index e121e0e..71e835e 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -1950,14 +1950,15 @@ done:
+ * called with controller locked, irqs blocked
+ * that hardware queue advances to the next transfer, unless prevented
+ */
+-static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in)
++static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
+ {
+ struct musb_hw_ep *ep = qh->hw_ep;
+ void __iomem *epio = ep->regs;
+ unsigned hw_end = ep->epnum;
+ void __iomem *regs = ep->musb->mregs;
+- u16 csr;
++ int is_in = usb_pipein(urb->pipe);
+ int status = 0;
++ u16 csr;
+
+ musb_ep_select(regs, hw_end);
+
+@@ -2056,7 +2057,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+ kfree(qh);
+ }
+ } else
+- ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
++ ret = musb_cleanup_urb(urb, qh);
+ done:
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return ret;
+@@ -2090,7 +2091,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+ urb->status = -ESHUTDOWN;
+
+ /* cleanup */
+- musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
++ musb_cleanup_urb(urb, qh);
+
+ /* Then nuke all the others ... and advance the
+ * queue on hw_ep (e.g. bulk ring) when we're done.
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch b/recipes/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch
new file mode 100644
index 0000000000..d9733f92b0
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch
@@ -0,0 +1,70 @@
+From c4804e5a447275553c55bbb0ab1748954cb8fbfc Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 31 Mar 2009 12:26:10 -0700
+Subject: [PATCH] twl4030-usb: fix minor reporting goofage
+
+Fix a reporting glitch in the twl4030 USB transceiver code.
+It wasn't properly distinguishing the two types of active
+USB link: ID grounded, vs not. In the current code that
+distinction doesn't much matter; in the future this bugfix
+should help support better USB controller communications.
+
+Provide a comment sorting out some of the cryptic bits of
+the manual: different sections use different names for
+key signals, and the register definitions don't help much
+without the explanations and diagrams.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/otg/twl4030-usb.c | 26 +++++++++++++++++++-------
+ 1 files changed, 19 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
+index d9478d0..f740390 100644
+--- a/drivers/usb/otg/twl4030-usb.c
++++ b/drivers/usb/otg/twl4030-usb.c
+@@ -217,6 +217,7 @@
+
+ /* In module TWL4030_MODULE_PM_MASTER */
+ #define PROTECT_KEY 0x0E
++#define STS_HW_CONDITIONS 0x0F
+
+ /* In module TWL4030_MODULE_PM_RECEIVER */
+ #define VUSB_DEDICATED1 0x7D
+@@ -351,15 +352,26 @@ static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl)
+ int status;
+ int linkstat = USB_LINK_UNKNOWN;
+
+- /* STS_HW_CONDITIONS */
+- status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, 0x0f);
++ /*
++ * For ID/VBUS sensing, see manual section 15.4.8 ...
++ * except when using only battery backup power, two
++ * comparators produce VBUS_PRES and ID_PRES signals,
++ * which don't match docs elsewhere. But ... BIT(7)
++ * and BIT(2) of STS_HW_CONDITIONS, respectively, do
++ * seem to match up. If either is true the USB_PRES
++ * signal is active, the OTG module is activated, and
++ * its interrupt may be raised (may wake the system).
++ */
++ status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER,
++ STS_HW_CONDITIONS);
+ if (status < 0)
+ dev_err(twl->dev, "USB link status err %d\n", status);
+- else if (status & BIT(7))
+- linkstat = USB_LINK_VBUS;
+- else if (status & BIT(2))
+- linkstat = USB_LINK_ID;
+- else
++ else if (status & (BIT(7) | BIT(2))) {
++ if (status & BIT(2))
++ linkstat = USB_LINK_ID;
++ else
++ linkstat = USB_LINK_VBUS;
++ } else
+ linkstat = USB_LINK_NONE;
+
+ dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch b/recipes/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch
new file mode 100644
index 0000000000..16b5b9908b
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch
@@ -0,0 +1,38 @@
+From ba59a0812ba0e223bd0af8f4dea6c971b6289696 Mon Sep 17 00:00:00 2001
+From: Anand Gadiyar <gadiyar-l0cyMroinI0@public.gmane.org>
+Date: Thu, 2 Apr 2009 12:07:08 -0700
+Subject: [PATCH] musb: use dma mode 1 for TX if transfer size equals maxpacket (v2)
+
+Currently, with Inventra DMA, we use Mode 0 if transfer size is less
+than or equal to the endpoint's maxpacket size. This requires that
+we explicitly set TXPKTRDY for that transfer.
+
+However the musb_g_tx code will not set TXPKTRDY twice if the last
+transfer is exactly equal to maxpacket, even if request->zero is set.
+Using Mode 1 will solve this; a better fix might be in musb_g_tx().
+
+Without this change, musb will not correctly send out a ZLP if the
+last transfer is the maxpacket size and request->zero is set.
+
+Signed-off-by: Anand Gadiyar <gadiyar-l0cyMroinI0@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_gadget.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index bc197b2..e8f920c 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -310,7 +310,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ /* setup DMA, then program endpoint CSR */
+ request_size = min(request->length,
+ musb_ep->dma->max_len);
+- if (request_size <= musb_ep->packet_sz)
++ if (request_size < musb_ep->packet_sz)
+ musb_ep->dma->desired_mode = 0;
+ else
+ musb_ep->dma->desired_mode = 1;
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch b/recipes/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch
new file mode 100644
index 0000000000..3038dd171d
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch
@@ -0,0 +1,187 @@
+From 2e049a88b729ae2fdc0ecdabad1857810bd62737 Mon Sep 17 00:00:00 2001
+From: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Date: Fri, 3 Apr 2009 16:16:17 -0700
+Subject: [PATCH] musb: add high bandwidth ISO support
+
+Tested on OMAP3 host side with Creative (Live! Cam Optia) USB camera
+which uses high bandwidth isochronous IN endpoints. FIFO mode 4 is
+updated to provide the needed 4K endpoint buffer without breaking
+the g_nokia composite gadget configuration. (This is the only
+gadget driver known to use enough endpoints to notice the change.)
+
+Signed-off-by: Ajay Kumar Gupta <ajay.gupta-l0cyMroinI0@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 19 ++++++++---------
+ drivers/usb/musb/musb_core.h | 3 ++
+ drivers/usb/musb/musb_host.c | 47 +++++++++++++++++++++++++++++++----------
+ drivers/usb/musb/musb_host.h | 1 +
+ 4 files changed, 48 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index a1de43b..d953305 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -1068,14 +1068,13 @@ static struct fifo_cfg __initdata mode_4_cfg[] = {
+ { .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
+-{ .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_TX, .maxpacket = 256, },
++{ .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 = 64, },
++{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, },
++{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, },
++{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, },
+ { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
+ { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
+ };
+@@ -1335,11 +1334,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
+ }
+ if (reg & MUSB_CONFIGDATA_HBRXE) {
+ strcat(aInfo, ", HB-ISO Rx");
+- strcat(aInfo, " (X)"); /* no driver support */
++ musb->hb_iso_rx = true;
+ }
+ if (reg & MUSB_CONFIGDATA_HBTXE) {
+ strcat(aInfo, ", HB-ISO Tx");
+- strcat(aInfo, " (X)"); /* no driver support */
++ musb->hb_iso_tx = true;
+ }
+ if (reg & MUSB_CONFIGDATA_SOFTCONE)
+ strcat(aInfo, ", SoftConn");
+diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
+index f56a56c..0ac4faf 100644
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -387,6 +387,9 @@ struct musb {
+ unsigned is_multipoint:1;
+ unsigned ignore_disconnect:1; /* during bus resets */
+
++ unsigned hb_iso_rx:1; /* high bandwidth iso rx? */
++ unsigned hb_iso_tx:1; /* high bandwidth iso tx? */
++
+ #ifdef C_MP_TX
+ unsigned bulk_split:1;
+ #define can_bulk_split(musb,type) \
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index 71e835e..ece5122 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -602,7 +602,8 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
+ musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg);
+ musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg);
+ /* NOTE: bulk combining rewrites high bits of maxpacket */
+- musb_writew(ep->regs, MUSB_RXMAXP, qh->maxpacket);
++ musb_writew(ep->regs, MUSB_RXMAXP,
++ qh->maxpacket | ((qh->hb_mult - 1) << 11));
+
+ ep->rx_reinit = 0;
+ }
+@@ -624,9 +625,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (length > pkt_size) {
+ mode = 1;
+- csr |= MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_DMAENAB;
++ csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
++ /* autoset shouldn't be set in high bandwidth */
++ if (qh->hb_mult == 1)
++ csr |= MUSB_TXCSR_AUTOSET;
+ } else {
+ mode = 0;
+ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
+@@ -1432,6 +1434,10 @@ void musb_host_rx(struct musb *musb, u8 epnum)
+ /* packet error reported later */
+ iso_err = true;
+ }
++ } else if (rx_csr & MUSB_RXCSR_INCOMPRX) {
++ DBG(3, "end %d high bandwidth incomplete ISO packet RX\n",
++ epnum);
++ status = -EPROTO;
+ }
+
+ /* faults abort the transfer */
+@@ -1639,7 +1645,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
+ val &= ~MUSB_RXCSR_H_AUTOREQ;
+ else
+ val |= MUSB_RXCSR_H_AUTOREQ;
+- val |= MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB;
++ val |= MUSB_RXCSR_DMAENAB;
++
++ /* autoclear shouldn't be set in high bandwidth */
++ if (qh->hb_mult == 1)
++ val |= MUSB_RXCSR_AUTOCLEAR;
+
+ musb_writew(epio, MUSB_RXCSR,
+ MUSB_RXCSR_H_WZC_BITS | val);
+@@ -1725,9 +1735,10 @@ static int musb_schedule(
+ continue;
+
+ if (is_in)
+- diff = hw_ep->max_packet_sz_rx - qh->maxpacket;
++ diff = hw_ep->max_packet_sz_rx;
+ else
+- diff = hw_ep->max_packet_sz_tx - qh->maxpacket;
++ diff = hw_ep->max_packet_sz_tx;
++ diff -= (qh->maxpacket * qh->hb_mult);
+
+ if (diff >= 0 && best_diff > diff) {
+ best_diff = diff;
+@@ -1830,15 +1841,27 @@ static int musb_urb_enqueue(
+ qh->is_ready = 1;
+
+ qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize);
++ qh->type = usb_endpoint_type(epd);
+
+- /* no high bandwidth support yet */
+- if (qh->maxpacket & ~0x7ff) {
+- ret = -EMSGSIZE;
+- goto done;
++ /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier.
++ * Some musb cores don't support high bandwidth ISO transfers; and
++ * we don't (yet!) support high bandwidth interrupt transfers.
++ */
++ qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03);
++ if (qh->hb_mult > 1) {
++ int ok = (qh->type == USB_ENDPOINT_XFER_ISOC);
++
++ if (ok)
++ ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx)
++ || (usb_pipeout(urb->pipe) && musb->hb_iso_tx);
++ if (!ok) {
++ ret = -EMSGSIZE;
++ goto done;
++ }
++ qh->maxpacket &= 0x7ff;
+ }
+
+ qh->epnum = usb_endpoint_num(epd);
+- qh->type = usb_endpoint_type(epd);
+
+ /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */
+ qh->addr_reg = (u8) usb_pipedevice(urb->pipe);
+diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
+index 0b7fbcd..14b0077 100644
+--- a/drivers/usb/musb/musb_host.h
++++ b/drivers/usb/musb/musb_host.h
+@@ -67,6 +67,7 @@ struct musb_qh {
+ u8 is_ready; /* safe to modify hw_ep */
+ u8 type; /* XFERTYPE_* */
+ u8 epnum;
++ u8 hb_mult; /* high bandwidth pkts per uf */
+ u16 maxpacket;
+ u16 frame; /* for periodic schedule */
+ unsigned iso_idx; /* in urb->iso_frame_desc[] */
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch b/recipes/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch
new file mode 100644
index 0000000000..67004d7ec6
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch
@@ -0,0 +1,259 @@
+From de835357b3597af5304742cbd89771d70533292a Mon Sep 17 00:00:00 2001
+From: Ajay Kumar Gupta <ajay.gupta@ti.com>
+Date: Fri, 6 Feb 2009 17:32:35 +0530
+Subject: [PATCH] USB: otg: adding nop usb transceiver
+
+NOP transceiver is used by all the usb transceiver which are mostly
+autonomous and doesn't require any programming or which are built
+into the usb ip itself.NOP transceiver only allocates the memory
+for struct xceiv and calls otg_set_transceiver() so function call
+to otg_get_transceiver() will return a valid transceiver.
+
+NOP transceiver device should be registered by calling
+usb_nop_xceiv_register() from platform files.
+
+Signed-off-by: Ajay Kumar Gupta <ajay.gupta@ti.com>
+Cc: Felipe Balbi <felipe.balbi@nokia.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/otg/Kconfig | 8 ++
+ drivers/usb/otg/Makefile | 1 +
+ drivers/usb/otg/nop-usb-xceiv.c | 180 +++++++++++++++++++++++++++++++++++++++
+ include/linux/usb/otg.h | 4 +
+ 4 files changed, 193 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/usb/otg/nop-usb-xceiv.c
+
+diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
+index 5790a5b..aa884d0 100644
+--- a/drivers/usb/otg/Kconfig
++++ b/drivers/usb/otg/Kconfig
+@@ -51,4 +51,12 @@ config TWL4030_USB
+ This transceiver supports high and full speed devices plus,
+ in host mode, low speed.
+
++config NOP_USB_XCEIV
++ tristate "NOP USB Transceiver Driver"
++ select USB_OTG_UTILS
++ help
++ this driver is to be used by all the usb transceiver which are either
++ built-in with usb ip or which are autonomous and doesn't require any
++ phy programming such as ISP1x04 etc.
++
+ endif # USB || OTG
+diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
+index d73c7cf..2081678 100644
+--- a/drivers/usb/otg/Makefile
++++ b/drivers/usb/otg/Makefile
+@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
+ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+ obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
++obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
+
+ ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
+ ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG
+diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
+new file mode 100644
+index 0000000..4b933f6
+--- /dev/null
++++ b/drivers/usb/otg/nop-usb-xceiv.c
+@@ -0,0 +1,180 @@
++/*
++ * drivers/usb/otg/nop-usb-xceiv.c
++ *
++ * NOP USB transceiver for all USB transceiver which are either built-in
++ * into USB IP or which are mostly autonomous.
++ *
++ * Copyright (C) 2009 Texas Instruments Inc
++ * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ * Current status:
++ * this is to add "nop" transceiver for all those phy which is
++ * autonomous such as isp1504 etc.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/usb/otg.h>
++
++struct nop_usb_xceiv {
++ struct otg_transceiver otg;
++ struct device *dev;
++};
++
++static u64 nop_xceiv_dmamask = DMA_32BIT_MASK;
++
++static struct platform_device nop_xceiv_device = {
++ .name = "nop_usb_xceiv",
++ .id = -1,
++ .dev = {
++ .dma_mask = &nop_xceiv_dmamask,
++ .coherent_dma_mask = DMA_32BIT_MASK,
++ .platform_data = NULL,
++ },
++};
++
++void usb_nop_xceiv_register(void)
++{
++ if (platform_device_register(&nop_xceiv_device) < 0) {
++ printk(KERN_ERR "Unable to register usb nop transceiver\n");
++ return;
++ }
++}
++
++void usb_nop_xceiv_unregister(void)
++{
++ platform_device_unregister(&nop_xceiv_device);
++}
++
++static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x)
++{
++ return container_of(x, struct nop_usb_xceiv, otg);
++}
++
++static int nop_set_suspend(struct otg_transceiver *x, int suspend)
++{
++ return 0;
++}
++
++static int nop_set_peripheral(struct otg_transceiver *x,
++ struct usb_gadget *gadget)
++{
++ struct nop_usb_xceiv *nop;
++
++ if (!x)
++ return -ENODEV;
++
++ nop = xceiv_to_nop(x);
++
++ if (!gadget) {
++ nop->otg.gadget = NULL;
++ return -ENODEV;
++ }
++
++ nop->otg.gadget = gadget;
++ nop->otg.state = OTG_STATE_B_IDLE;
++ return 0;
++}
++
++static int nop_set_host(struct otg_transceiver *x, struct usb_bus *host)
++{
++ struct nop_usb_xceiv *nop;
++
++ if (!x)
++ return -ENODEV;
++
++ nop = xceiv_to_nop(x);
++
++ if (!host) {
++ nop->otg.host = NULL;
++ return -ENODEV;
++ }
++
++ nop->otg.host = host;
++ return 0;
++}
++
++static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
++{
++ struct nop_usb_xceiv *nop;
++ int err;
++
++ nop = kzalloc(sizeof *nop, GFP_KERNEL);
++ if (!nop)
++ return -ENOMEM;
++
++ nop->dev = &pdev->dev;
++ nop->otg.dev = nop->dev;
++ nop->otg.label = "nop-xceiv";
++ nop->otg.state = OTG_STATE_UNDEFINED;
++ nop->otg.set_host = nop_set_host;
++ nop->otg.set_peripheral = nop_set_peripheral;
++ nop->otg.set_suspend = nop_set_suspend;
++
++ err = otg_set_transceiver(&nop->otg);
++ if (err) {
++ dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
++ err);
++ goto exit;
++ }
++
++ platform_set_drvdata(pdev, nop);
++
++ return 0;
++exit:
++ kfree(nop);
++ return err;
++}
++
++static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev)
++{
++ struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
++
++ otg_set_transceiver(NULL);
++
++ platform_set_drvdata(pdev, NULL);
++ kfree(nop);
++
++ return 0;
++}
++
++static struct platform_driver nop_usb_xceiv_driver = {
++ .probe = nop_usb_xceiv_probe,
++ .remove = __devexit_p(nop_usb_xceiv_remove),
++ .driver = {
++ .name = "nop_usb_xceiv",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init nop_usb_xceiv_init(void)
++{
++ return platform_driver_register(&nop_usb_xceiv_driver);
++}
++subsys_initcall(nop_usb_xceiv_init);
++
++static void __exit nop_usb_xceiv_exit(void)
++{
++ platform_driver_unregister(&nop_usb_xceiv_driver);
++}
++module_exit(nop_usb_xceiv_exit);
++
++MODULE_ALIAS("platform:nop_usb_xceiv");
++MODULE_AUTHOR("Texas Instruments Inc");
++MODULE_DESCRIPTION("NOP USB Transceiver driver");
++MODULE_LICENSE("GPL");
+diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
+index 94df4fe..54f2424 100644
+--- a/include/linux/usb/otg.h
++++ b/include/linux/usb/otg.h
+@@ -80,6 +80,10 @@ struct otg_transceiver {
+
+ /* for board-specific init logic */
+ extern int otg_set_transceiver(struct otg_transceiver *);
++#ifdef CONFIG_NOP_USB_XCEIV
++extern void usb_nop_xceiv_register(void);
++extern void usb_nop_xceiv_unregister(void);
++#endif
+
+
+ /* for usb host and peripheral controller drivers */
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch b/recipes/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch
new file mode 100644
index 0000000000..21fe7bea17
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch
@@ -0,0 +1,90 @@
+From ae4f027580168814f734cf3c41a662a7f10c744c Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 31 Mar 2009 12:28:31 -0700
+Subject: [PATCH] nop-usb-xceiv: behave when linked as a module
+
+The NOP OTG transceiver driver needs to be usable from modules.
+Make sure its symbols are always accessible at both compile and
+link time, and make sure the device instance is allocated from
+the heap so that device lifetime rules are obeyed.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/otg/nop-usb-xceiv.c | 25 ++++++++++---------------
+ include/linux/usb/otg.h | 4 ++--
+ 2 files changed, 12 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
+index 4b933f6..9ed5ea5 100644
+--- a/drivers/usb/otg/nop-usb-xceiv.c
++++ b/drivers/usb/otg/nop-usb-xceiv.c
+@@ -22,8 +22,8 @@
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+- * this is to add "nop" transceiver for all those phy which is
+- * autonomous such as isp1504 etc.
++ * This provides a "nop" transceiver for PHYs which are
++ * autonomous such as isp1504, isp1707, etc.
+ */
+
+ #include <linux/module.h>
+@@ -36,30 +36,25 @@ struct nop_usb_xceiv {
+ struct device *dev;
+ };
+
+-static u64 nop_xceiv_dmamask = DMA_32BIT_MASK;
+-
+-static struct platform_device nop_xceiv_device = {
+- .name = "nop_usb_xceiv",
+- .id = -1,
+- .dev = {
+- .dma_mask = &nop_xceiv_dmamask,
+- .coherent_dma_mask = DMA_32BIT_MASK,
+- .platform_data = NULL,
+- },
+-};
++static struct platform_device *pd;
+
+ void usb_nop_xceiv_register(void)
+ {
+- if (platform_device_register(&nop_xceiv_device) < 0) {
++ if (pd)
++ return;
++ pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0);
++ if (!pd) {
+ printk(KERN_ERR "Unable to register usb nop transceiver\n");
+ return;
+ }
+ }
++EXPORT_SYMBOL(usb_nop_xceiv_register);
+
+ void usb_nop_xceiv_unregister(void)
+ {
+- platform_device_unregister(&nop_xceiv_device);
++ platform_device_unregister(pd);
+ }
++EXPORT_SYMBOL(usb_nop_xceiv_unregister);
+
+ static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x)
+ {
+diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
+index 54f2424..7df8bae 100644
+--- a/include/linux/usb/otg.h
++++ b/include/linux/usb/otg.h
+@@ -80,10 +80,10 @@ struct otg_transceiver {
+
+ /* for board-specific init logic */
+ extern int otg_set_transceiver(struct otg_transceiver *);
+-#ifdef CONFIG_NOP_USB_XCEIV
++
++/* sometimes transceivers are accessed only through e.g. ULPI */
+ extern void usb_nop_xceiv_register(void);
+ extern void usb_nop_xceiv_unregister(void);
+-#endif
+
+
+ /* for usb host and peripheral controller drivers */
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch b/recipes/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch
new file mode 100644
index 0000000000..035a6c7676
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch
@@ -0,0 +1,1109 @@
+From 43ee46723ffa9dd43d611362064d235440aa04e7 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 31 Mar 2009 12:30:04 -0700
+Subject: [PATCH] musb: proper hookup to transceiver drivers
+
+Let the otg_transceiver in MUSB be managed by an external driver;
+don't assume it's integrated. OMAP3 chips need it to be external,
+and there may be ways to interact with the transceiver which add
+functionality to the system.
+
+Platform init code is responsible for setting up the transeciver,
+probably using the NOP transceiver for integrated transceivers.
+External ones will use whatever the board init code provided,
+such as twl4030 or something more hands-off.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/Kconfig | 2 +
+ drivers/usb/musb/blackfin.c | 11 +++-
+ drivers/usb/musb/davinci.c | 33 +++++++++-----
+ drivers/usb/musb/musb_core.c | 96 +++++++++++++++++++++------------------
+ drivers/usb/musb/musb_core.h | 2 +-
+ drivers/usb/musb/musb_gadget.c | 38 +++++++--------
+ drivers/usb/musb/musb_host.c | 2 +-
+ drivers/usb/musb/musb_virthub.c | 20 ++++----
+ drivers/usb/musb/omap2430.c | 62 +++++++++----------------
+ drivers/usb/musb/tusb6010.c | 70 ++++++++++++++++++-----------
+ 10 files changed, 181 insertions(+), 155 deletions(-)
+
+diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
+index 9985db0..9eea991 100644
+--- a/drivers/usb/musb/Kconfig
++++ b/drivers/usb/musb/Kconfig
+@@ -10,6 +10,7 @@ comment "Enable Host or Gadget support to see Inventra options"
+ config USB_MUSB_HDRC
+ depends on (USB || USB_GADGET) && HAVE_CLK
+ depends on !SUPERH
++ select NOP_USB_XCEIV if ARCH_DAVINCI
+ select TWL4030_USB if MACH_OMAP_3430SDP
+ select USB_OTG_UTILS
+ tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+@@ -55,6 +56,7 @@ comment "Blackfin high speed USB Support"
+ config USB_TUSB6010
+ boolean "TUSB 6010 support"
+ depends on USB_MUSB_HDRC && !USB_MUSB_SOC
++ select NOP_USB_XCEIV
+ default y
+ help
+ The TUSB 6010 chip, from Texas Instruments, connects a discrete
+diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
+index 7861348..f2f66eb 100644
+--- a/drivers/usb/musb/blackfin.c
++++ b/drivers/usb/musb/blackfin.c
+@@ -143,7 +143,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
+ u16 val;
+
+ spin_lock_irqsave(&musb->lock, flags);
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_BCON:
+ /* Start a new session */
+@@ -154,7 +154,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
+ val = musb_readw(musb->mregs, MUSB_DEVCTL);
+ if (!(val & MUSB_DEVCTL_BDEVICE)) {
+ gpio_set_value(musb->config->gpio_vrsel, 1);
+- musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ } else {
+ gpio_set_value(musb->config->gpio_vrsel, 0);
+
+@@ -247,6 +247,11 @@ int __init musb_platform_init(struct musb *musb)
+ }
+ gpio_direction_output(musb->config->gpio_vrsel, 0);
+
++ usb_nop_xceiv_register();
++ musb->xceiv = otg_get_transceiver();
++ if (!musb->xceiv)
++ return -ENODEV;
++
+ if (ANOMALY_05000346) {
+ bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
+ SSYNC();
+@@ -291,7 +296,7 @@ int __init musb_platform_init(struct musb *musb)
+ musb_conn_timer_handler, (unsigned long) musb);
+ }
+ if (is_peripheral_enabled(musb))
+- musb->xceiv.set_power = bfin_set_power;
++ musb->xceiv->set_power = bfin_set_power;
+
+ musb->isr = blackfin_interrupt;
+
+diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
+index 9fd74bf..81de742 100644
+--- a/drivers/usb/musb/davinci.c
++++ b/drivers/usb/musb/davinci.c
+@@ -200,7 +200,7 @@ static void otg_timer(unsigned long _musb)
+ DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
+
+ spin_lock_irqsave(&musb->lock, flags);
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_VFALL:
+ /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
+ * seems to mis-handle session "start" otherwise (or in our
+@@ -211,7 +211,7 @@ static void otg_timer(unsigned long _musb)
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ break;
+ }
+- musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+ MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
+ break;
+@@ -236,7 +236,7 @@ static void otg_timer(unsigned long _musb)
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ else
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+@@ -310,21 +310,21 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
+ * to stop registering in devctl.
+ */
+ musb->int_usb &= ~MUSB_INTR_VBUSERROR;
+- musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ WARNING("VBUS error workaround (delay coming)\n");
+ } else if (is_host_enabled(musb) && drvvbus) {
+ musb->is_active = 1;
+ MUSB_HST_MODE(musb);
+- musb->xceiv.default_a = 1;
+- musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
++ musb->xceiv->default_a = 1;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ portstate(musb->port1_status |= USB_PORT_STAT_POWER);
+ del_timer(&otg_workaround);
+ } else {
+ musb->is_active = 0;
+ MUSB_DEV_MODE(musb);
+- musb->xceiv.default_a = 0;
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->default_a = 0;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
+ }
+
+@@ -346,7 +346,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
+
+ /* poll for ID change */
+ if (is_otg_enabled(musb)
+- && musb->xceiv.state == OTG_STATE_B_IDLE)
++ && musb->xceiv->state == OTG_STATE_B_IDLE)
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+@@ -365,6 +365,11 @@ int __init musb_platform_init(struct musb *musb)
+ void __iomem *tibase = musb->ctrl_base;
+ u32 revision;
+
++ usb_nop_xceiv_register();
++ musb->xceiv = otg_get_transceiver();
++ if (!musb->xceiv)
++ return -ENODEV;
++
+ musb->mregs += DAVINCI_BASE_OFFSET;
+
+ clk_enable(musb->clock);
+@@ -372,7 +377,7 @@ int __init musb_platform_init(struct musb *musb)
+ /* returns zero if e.g. not clocked */
+ revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
+ if (revision == 0)
+- return -ENODEV;
++ goto fail;
+
+ if (is_host_enabled(musb))
+ setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
+@@ -396,6 +401,10 @@ int __init musb_platform_init(struct musb *musb)
+
+ musb->isr = davinci_interrupt;
+ return 0;
++
++fail:
++ usb_nop_xceiv_unregister();
++ return -ENODEV;
+ }
+
+ int musb_platform_exit(struct musb *musb)
+@@ -406,7 +415,7 @@ int musb_platform_exit(struct musb *musb)
+ davinci_source_power(musb, 0 /*off*/, 1);
+
+ /* delay, to avoid problems with module reload */
+- if (is_host_enabled(musb) && musb->xceiv.default_a) {
++ if (is_host_enabled(musb) && musb->xceiv->default_a) {
+ int maxdelay = 30;
+ u8 devctl, warn = 0;
+
+@@ -435,5 +444,7 @@ int musb_platform_exit(struct musb *musb)
+
+ clk_disable(musb->clock);
+
++ usb_nop_xceiv_unregister();
++
+ return 0;
+ }
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index d953305..ac150af 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -267,7 +267,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";
+@@ -302,11 +302,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:
+@@ -331,20 +331,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;
+@@ -402,7 +402,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
+@@ -425,12 +425,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;
+@@ -441,11 +441,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
+@@ -490,7 +490,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ */
+ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+ musb->ep0_stage = MUSB_EP0_START;
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ musb_set_vbus(musb, 1);
+
+@@ -516,7 +516,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
+@@ -602,11 +602,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
+@@ -614,13 +614,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;
+@@ -650,7 +650,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ }
+ } 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
+@@ -673,12 +673,12 @@ 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;
++ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ /* FALLTHROUGH */
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_reset(musb);
+@@ -763,7 +763,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ MUSB_MODE(musb), devctl);
+ handled = IRQ_HANDLED;
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ #ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
+@@ -805,7 +805,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ otg_state_string(musb), devctl, power);
+ handled = IRQ_HANDLED;
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ #ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_A_PERIPHERAL:
+ /*
+@@ -817,10 +817,10 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_suspend(musb);
+ musb->is_active = is_otg_enabled(musb)
+- && musb->xceiv.gadget->b_hnp_enable;
++ && musb->xceiv->gadget->b_hnp_enable;
+ if (musb->is_active) {
+ #ifdef CONFIG_USB_MUSB_OTG
+- musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
++ 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
+@@ -834,9 +834,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+ case OTG_STATE_A_HOST:
+- musb->xceiv.state = OTG_STATE_A_SUSPEND;
++ musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->is_active = is_otg_enabled(musb)
+- && musb->xceiv.host->b_hnp_enable;
++ && musb->xceiv->host->b_hnp_enable;
+ break;
+ case OTG_STATE_B_HOST:
+ /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
+@@ -1681,7 +1681,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);
+@@ -1742,8 +1742,8 @@ static void musb_irq_work(struct work_struct *data)
+ struct musb *musb = container_of(data, struct musb, irq_work);
+ static int old_state;
+
+- 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");
+ }
+ }
+@@ -1840,7 +1840,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
+@@ -1921,10 +1921,18 @@ bad_config:
+ }
+ }
+
+- /* assume vbus is off */
+-
+- /* platform adjusts musb->mregs and musb->isr if needed,
+- * and activates clocks
++ /* The musb_platform_init() call:
++ * - adjusts musb->mregs and musb->isr if needed,
++ * - may initialize an integrated tranceiver
++ * - initializes musb->xceiv, usually by otg_get_transceiver()
++ * - activates clocks.
++ * - stops powering VBUS
++ * - assigns musb->board_set_vbus if host mode is enabled
++ *
++ * There are various transciever configurations. Blackfin,
++ * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses
++ * external/discrete ones in various flavors (twl4030 family,
++ * isp1504, non-OTG, etc) mostly hooking up through ULPI.
+ */
+ musb->isr = generic_interrupt;
+ status = musb_platform_init(musb);
+@@ -1992,17 +2000,17 @@ bad_config:
+ ? "DMA" : "PIO",
+ musb->nIrq);
+
+-#ifdef CONFIG_USB_MUSB_HDRC_HCD
+- /* host side needs more setup, except for no-host modes */
+- if (musb->board_mode != MUSB_PERIPHERAL) {
++ /* host side needs more setup */
++ if (is_host_enabled(musb)) {
+ struct usb_hcd *hcd = musb_to_hcd(musb);
+
+- if (musb->board_mode == MUSB_OTG)
++ otg_set_host(musb->xceiv, &hcd->self);
++
++ if (is_otg_enabled(musb))
+ 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 */
+
+ /* For the host-only role, we can activate right away.
+ * (We expect the ID pin to be forcibly grounded!!)
+@@ -2010,8 +2018,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)
+@@ -2026,8 +2034,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)
+diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
+index 0ac4faf..c3ee348 100644
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -356,7 +356,7 @@ struct musb {
+ u16 int_rx;
+ u16 int_tx;
+
+- struct otg_transceiver xceiv;
++ struct otg_transceiver *xceiv;
+
+ int nIrq;
+ unsigned irq_wake:1;
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index e8f920c..2fbfba5 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -1406,7 +1406,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_B_PERIPHERAL:
+ /* NOTE: OTG state machine doesn't include B_SUSPENDED;
+ * that's part of the standard usb 1.1 state machine, and
+@@ -1508,9 +1508,9 @@ static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+ {
+ struct musb *musb = gadget_to_musb(gadget);
+
+- if (!musb->xceiv.set_power)
++ if (!musb->xceiv->set_power)
+ return -EOPNOTSUPP;
+- return otg_set_power(&musb->xceiv, mA);
++ return otg_set_power(musb->xceiv, mA);
+ }
+
+ static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
+@@ -1733,11 +1733,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+- /* REVISIT always use otg_set_peripheral(), handling
+- * issues including the root hub one below ...
+- */
+- musb->xceiv.gadget = &musb->g;
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ otg_set_peripheral(musb->xceiv, &musb->g);
+ musb->is_active = 1;
+
+ /* FIXME this ignores the softconnect flag. Drivers are
+@@ -1749,6 +1745,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+ if (!is_otg_enabled(musb))
+ musb_start(musb);
+
++ otg_set_peripheral(musb->xceiv, &musb->g);
++
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ if (is_otg_enabled(musb)) {
+@@ -1762,8 +1760,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+ if (retval < 0) {
+ DBG(1, "add_hcd failed, %d\n", retval);
+ spin_lock_irqsave(&musb->lock, flags);
+- musb->xceiv.gadget = NULL;
+- musb->xceiv.state = OTG_STATE_UNDEFINED;
++ otg_set_peripheral(musb->xceiv, NULL);
+ musb->gadget_driver = NULL;
+ musb->g.dev.driver = NULL;
+ spin_unlock_irqrestore(&musb->lock, flags);
+@@ -1846,8 +1843,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+
+ (void) musb_gadget_vbus_draw(&musb->g, 0);
+
+- musb->xceiv.state = OTG_STATE_UNDEFINED;
++ musb->xceiv->state = OTG_STATE_UNDEFINED;
+ stop_activity(musb, driver);
++ otg_set_peripheral(musb->xceiv, NULL);
+
+ DBG(3, "unregistering driver %s\n", driver->function);
+ spin_unlock_irqrestore(&musb->lock, flags);
+@@ -1883,7 +1881,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
+ void musb_g_resume(struct musb *musb)
+ {
+ musb->is_suspended = 0;
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_B_IDLE:
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+@@ -1909,10 +1907,10 @@ void musb_g_suspend(struct musb *musb)
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ DBG(3, "devctl %02x\n", devctl);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_B_IDLE:
+ if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+- musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
++ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ musb->is_suspended = 1;
+@@ -1958,22 +1956,22 @@ void musb_g_disconnect(struct musb *musb)
+ spin_lock(&musb->lock);
+ }
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ default:
+ #ifdef CONFIG_USB_MUSB_OTG
+ DBG(2, "Unhandled disconnect %s, setting a_idle\n",
+ otg_state_string(musb));
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+- musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ #endif
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_IDLE:
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ break;
+ case OTG_STATE_B_SRP_INIT:
+ break;
+@@ -2029,10 +2027,10 @@ __acquires(musb->lock)
+ * or else after HNP, as A-Device
+ */
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+- musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
++ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->g.is_a_peripheral = 0;
+ } else if (is_otg_enabled(musb)) {
+- musb->xceiv.state = OTG_STATE_A_PERIPHERAL;
++ musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
+ musb->g.is_a_peripheral = 1;
+ } else
+ WARN_ON(1);
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index ece5122..795dabe 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -2169,7 +2169,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
+ {
+ struct musb *musb = hcd_to_musb(hcd);
+
+- if (musb->xceiv.state == OTG_STATE_A_SUSPEND)
++ if (musb->xceiv->state == OTG_STATE_A_SUSPEND)
+ return 0;
+
+ if (is_host_active(musb) && musb->is_active) {
+diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
+index e0e9ce5..7e7900f 100644
+--- a/drivers/usb/musb/musb_virthub.c
++++ b/drivers/usb/musb/musb_virthub.c
+@@ -78,18 +78,18 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
+ DBG(3, "Root port suspended, power %02x\n", power);
+
+ musb->port1_status |= USB_PORT_STAT_SUSPEND;
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_HOST:
+- musb->xceiv.state = OTG_STATE_A_SUSPEND;
++ musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->is_active = is_otg_enabled(musb)
+- && musb->xceiv.host->b_hnp_enable;
++ && musb->xceiv->host->b_hnp_enable;
+ musb_platform_try_idle(musb, 0);
+ break;
+ #ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_B_HOST:
+- musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
++ musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
+ musb->is_active = is_otg_enabled(musb)
+- && musb->xceiv.host->b_hnp_enable;
++ && musb->xceiv->host->b_hnp_enable;
+ musb_platform_try_idle(musb, 0);
+ break;
+ #endif
+@@ -116,7 +116,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
+ void __iomem *mbase = musb->mregs;
+
+ #ifdef CONFIG_USB_MUSB_OTG
+- if (musb->xceiv.state == OTG_STATE_B_IDLE) {
++ if (musb->xceiv->state == OTG_STATE_B_IDLE) {
+ DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n");
+ musb->port1_status &= ~USB_PORT_STAT_RESET;
+ return;
+@@ -186,14 +186,14 @@ void musb_root_disconnect(struct musb *musb)
+ usb_hcd_poll_rh_status(musb_to_hcd(musb));
+ musb->is_active = 0;
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
+- musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->is_active = 0;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ break;
+ default:
+ DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
+@@ -332,7 +332,7 @@ int musb_hub_control(
+ musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+ usb_hcd_poll_rh_status(musb_to_hcd(musb));
+ /* NOTE: it might really be A_WAIT_BCON ... */
+- musb->xceiv.state = OTG_STATE_A_HOST;
++ musb->xceiv->state = OTG_STATE_A_HOST;
+ }
+
+ put_unaligned(cpu_to_le32(musb->port1_status
+diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
+index 901dffd..5f67b03 100644
+--- a/drivers/usb/musb/omap2430.c
++++ b/drivers/usb/musb/omap2430.c
+@@ -62,17 +62,17 @@ static void musb_do_idle(unsigned long _musb)
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ break;
+@@ -90,7 +90,7 @@ static void musb_do_idle(unsigned long _musb)
+ musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+ usb_hcd_poll_rh_status(musb_to_hcd(musb));
+ /* NOTE: it might really be A_WAIT_BCON ... */
+- musb->xceiv.state = OTG_STATE_A_HOST;
++ musb->xceiv->state = OTG_STATE_A_HOST;
+ }
+ break;
+ #endif
+@@ -98,9 +98,9 @@ static void musb_do_idle(unsigned long _musb)
+ case OTG_STATE_A_HOST:
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ else
+- musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ #endif
+ default:
+ break;
+@@ -119,7 +119,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+- && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
++ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
+ del_timer(&musb_idle_timer);
+ last_timer = jiffies;
+@@ -164,8 +164,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
+
+ if (is_on) {
+ musb->is_active = 1;
+- musb->xceiv.default_a = 1;
+- musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
++ musb->xceiv->default_a = 1;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ MUSB_HST_MODE(musb);
+@@ -176,8 +176,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
+ * jumping right to B_IDLE...
+ */
+
+- musb->xceiv.default_a = 0;
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->default_a = 0;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ MUSB_DEV_MODE(musb);
+@@ -189,10 +189,6 @@ static void omap_set_vbus(struct musb *musb, int is_on)
+ otg_state_string(musb),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+ }
+-static int omap_set_power(struct otg_transceiver *x, unsigned mA)
+-{
+- return 0;
+-}
+
+ static int musb_platform_resume(struct musb *musb);
+
+@@ -203,24 +199,6 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+- switch (musb_mode) {
+-#ifdef CONFIG_USB_MUSB_HDRC_HCD
+- case MUSB_HOST:
+- otg_set_host(&musb->xceiv, musb->xceiv.host);
+- break;
+-#endif
+-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+- case MUSB_PERIPHERAL:
+- otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
+- break;
+-#endif
+-#ifdef CONFIG_USB_MUSB_OTG
+- case MUSB_OTG:
+- break;
+-#endif
+- default:
+- return -EINVAL;
+- }
+ return 0;
+ }
+
+@@ -232,6 +210,16 @@ int __init musb_platform_init(struct musb *musb)
+ omap_cfg_reg(AE5_2430_USB0HS_STP);
+ #endif
+
++ /* We require some kind of external transceiver, hooked
++ * up through ULPI. TWL4030-family PMICs include one,
++ * which needs a driver, drivers aren't always needed.
++ */
++ musb->xceiv = otg_get_transceiver();
++ if (!musb->xceiv) {
++ pr_err("HS USB OTG: no transceiver configured\n");
++ return -ENODEV;
++ }
++
+ musb_platform_resume(musb);
+
+ l = omap_readl(OTG_SYSCONFIG);
+@@ -258,8 +246,6 @@ int __init musb_platform_init(struct musb *musb)
+
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = omap_set_vbus;
+- if (is_peripheral_enabled(musb))
+- musb->xceiv.set_power = omap_set_power;
+ musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
+
+ setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
+@@ -283,8 +269,7 @@ int musb_platform_suspend(struct musb *musb)
+ l |= ENABLEWAKEUP; /* enable wakeup */
+ omap_writel(l, OTG_SYSCONFIG);
+
+- if (musb->xceiv.set_suspend)
+- musb->xceiv.set_suspend(&musb->xceiv, 1);
++ otg_set_suspend(musb->xceiv, 1);
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 0);
+@@ -301,8 +286,7 @@ static int musb_platform_resume(struct musb *musb)
+ if (!musb->clock)
+ return 0;
+
+- if (musb->xceiv.set_suspend)
+- musb->xceiv.set_suspend(&musb->xceiv, 0);
++ otg_set_suspend(musb->xceiv, 0);
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
+index 9e20fd0..c473dec 100644
+--- a/drivers/usb/musb/tusb6010.c
++++ b/drivers/usb/musb/tusb6010.c
+@@ -260,6 +260,8 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
+ tusb_fifo_read_unaligned(fifo, buf, len);
+ }
+
++static struct musb *the_musb;
++
+ #ifdef CONFIG_USB_GADGET_MUSB_HDRC
+
+ /* This is used by gadget drivers, and OTG transceiver logic, allowing
+@@ -270,7 +272,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
+ */
+ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
+ {
+- struct musb *musb = container_of(x, struct musb, xceiv);
++ struct musb *musb = the_musb;
+ void __iomem *tbase = musb->ctrl_base;
+ u32 reg;
+
+@@ -420,7 +422,7 @@ static void musb_do_idle(unsigned long _musb)
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ if ((musb->a_wait_bcon != 0)
+ && (musb->idle_timeout == 0
+@@ -484,7 +486,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+- && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
++ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
+ del_timer(&musb_idle_timer);
+ last_timer = jiffies;
+@@ -533,8 +535,8 @@ static void tusb_source_power(struct musb *musb, int is_on)
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
+- musb->xceiv.default_a = 1;
+- musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
++ musb->xceiv->default_a = 1;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ conf |= TUSB_DEV_CONF_USB_HOST_MODE;
+@@ -547,24 +549,24 @@ static void tusb_source_power(struct musb *musb, int is_on)
+ /* If ID pin is grounded, we want to be a_idle */
+ otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
+ if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+- musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
++ musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ break;
+ default:
+- musb->xceiv.state = OTG_STATE_A_IDLE;
++ musb->xceiv->state = OTG_STATE_A_IDLE;
+ }
+ musb->is_active = 0;
+- musb->xceiv.default_a = 1;
++ musb->xceiv->default_a = 1;
+ MUSB_HST_MODE(musb);
+ } else {
+ musb->is_active = 0;
+- musb->xceiv.default_a = 0;
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->default_a = 0;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ }
+
+@@ -675,7 +677,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+ else
+ default_a = is_host_enabled(musb);
+ DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
+- musb->xceiv.default_a = default_a;
++ musb->xceiv->default_a = default_a;
+ tusb_source_power(musb, default_a);
+
+ /* Don't allow idling immediately */
+@@ -687,7 +689,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+ if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
+
+ /* B-dev state machine: no vbus ~= disconnect */
+- if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
++ if ((is_otg_enabled(musb) && !musb->xceiv->default_a)
+ || !is_host_enabled(musb)) {
+ #ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* ? musb_root_disconnect(musb); */
+@@ -702,9 +704,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+
+ if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
+ DBG(1, "Forcing disconnect (no interrupt)\n");
+- if (musb->xceiv.state != OTG_STATE_B_IDLE) {
++ if (musb->xceiv->state != OTG_STATE_B_IDLE) {
+ /* INTR_DISCONNECT can hide... */
+- musb->xceiv.state = OTG_STATE_B_IDLE;
++ musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->int_usb |= MUSB_INTR_DISCONNECT;
+ }
+ musb->is_active = 0;
+@@ -718,7 +720,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+ DBG(2, "vbus change, %s, otg %03x\n",
+ otg_state_string(musb), otg_stat);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_IDLE:
+ DBG(2, "Got SRP, turning on VBUS\n");
+ musb_set_vbus(musb, 1);
+@@ -766,7 +768,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+
+ DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
+
+- switch (musb->xceiv.state) {
++ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_VRISE:
+ /* VBUS has probably been valid for a while now,
+ * but may well have bounced out of range a bit
+@@ -778,7 +780,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
+ DBG(2, "devctl %02x\n", devctl);
+ break;
+ }
+- musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->is_active = 0;
+ idle_timeout = jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon);
+@@ -1094,9 +1096,14 @@ int __init musb_platform_init(struct musb *musb)
+ {
+ struct platform_device *pdev;
+ struct resource *mem;
+- void __iomem *sync;
++ void __iomem *sync = NULL;
+ int ret;
+
++ usb_nop_xceiv_register();
++ musb->xceiv = otg_get_transceiver();
++ if (!musb->xceiv)
++ return -ENODEV;
++
+ pdev = to_platform_device(musb->controller);
+
+ /* dma address for async dma */
+@@ -1107,14 +1114,16 @@ int __init musb_platform_init(struct musb *musb)
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem) {
+ pr_debug("no sync dma resource?\n");
+- return -ENODEV;
++ ret = -ENODEV;
++ goto done;
+ }
+ musb->sync = mem->start;
+
+ sync = ioremap(mem->start, mem->end - mem->start + 1);
+ if (!sync) {
+ pr_debug("ioremap for sync failed\n");
+- return -ENOMEM;
++ ret = -ENOMEM;
++ goto done;
+ }
+ musb->sync_va = sync;
+
+@@ -1127,28 +1136,37 @@ int __init musb_platform_init(struct musb *musb)
+ if (ret) {
+ printk(KERN_ERR "Could not start tusb6010 (%d)\n",
+ ret);
+- return -ENODEV;
++ goto done;
+ }
+ musb->isr = tusb_interrupt;
+
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = tusb_source_power;
+- if (is_peripheral_enabled(musb))
+- musb->xceiv.set_power = tusb_draw_power;
++ if (is_peripheral_enabled(musb)) {
++ musb->xceiv->set_power = tusb_draw_power;
++ the_musb = musb;
++ }
+
+ setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
+
++done:
++ if (ret < 0) {
++ if (sync)
++ iounmap(sync);
++ usb_nop_xceiv_unregister();
++ }
+ return ret;
+ }
+
+ int musb_platform_exit(struct musb *musb)
+ {
+ del_timer_sync(&musb_idle_timer);
++ the_musb = NULL;
+
+ if (musb->board_set_power)
+ musb->board_set_power(0);
+
+ iounmap(musb->sync_va);
+-
++ usb_nop_xceiv_unregister();
+ return 0;
+ }
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch b/recipes/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch
new file mode 100644
index 0000000000..f41b766cfe
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch
@@ -0,0 +1,198 @@
+From b4b8c1e7604784b9877f07400ff2a718118ef05c Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 31 Mar 2009 12:32:12 -0700
+Subject: [PATCH] musb: otg timer cleanup
+
+Minor cleanup of OTG timer handling:
+ * unify decls for OTG time constants, in the core header
+ * set up and use that timer in a more normal way
+ * move to the driver struct, so it's usable outside core
+
+And tighten use and setup of T(a_wait_bcon) so that if it's used,
+it's always valid. (If that timer expires, the A-device will
+stop powering VBUS. For non-OTG systems, that will be a surprise.)
+No behavioral changes, other than more consistency when applying
+that core HNP timeout.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 41 ++++++++++++++++++++++-------------------
+ drivers/usb/musb/musb_core.h | 14 +++++++++++---
+ drivers/usb/musb/omap2430.c | 2 --
+ 3 files changed, 33 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index ac150af..05c5dd3 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -112,6 +112,7 @@
+ #include "davinci.h"
+ #endif
+
++#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
+
+
+ unsigned musb_debug;
+@@ -288,12 +289,6 @@ const char *otg_state_string(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)
+@@ -320,10 +315,8 @@ void musb_otg_timer_func(unsigned long data)
+ 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.
++ * Stops the HNP transition. Caller must take care of locking.
+ */
+ void musb_hnp_stop(struct musb *musb)
+ {
+@@ -661,11 +654,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ musb_g_reset(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
+- DBG(1, "HNP: Setting timer as %s\n",
+- otg_state_string(musb));
+- musb_otg_timer.data = (unsigned long)musb;
+- mod_timer(&musb_otg_timer, jiffies
+- + msecs_to_jiffies(100));
++ /* never use invalid T(a_wait_bcon) */
++ DBG(1, "HNP: in %s, %d msec timeout\n",
++ otg_state_string(musb),
++ TA_WAIT_BCON(musb));
++ mod_timer(&musb->otg_timer, jiffies
++ + msecs_to_jiffies(TA_WAIT_BCON(musb)));
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+@@ -822,9 +816,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ #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));
++ mod_timer(&musb->otg_timer, jiffies
++ + msecs_to_jiffies(
++ OTG_TIME_B_ASE0_BRST));
+ #endif
+ }
+ break;
+@@ -1680,7 +1674,8 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
+ }
+
+ spin_lock_irqsave(&musb->lock, flags);
+- musb->a_wait_bcon = val;
++ /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
++ musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ;
+ if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON)
+ musb->is_active = 0;
+ musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
+@@ -1699,10 +1694,13 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
+
+ spin_lock_irqsave(&musb->lock, flags);
+ val = musb->a_wait_bcon;
++ /* FIXME get_vbus_status() is normally #defined as false...
++ * and is effectively TUSB-specific.
++ */
+ vbus = musb_platform_get_vbus_status(musb);
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+- return sprintf(buf, "Vbus %s, timeout %lu\n",
++ return sprintf(buf, "Vbus %s, timeout %lu msec\n",
+ vbus ? "on" : "off", val);
+ }
+ static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
+@@ -1775,6 +1773,7 @@ allocate_instance(struct device *dev,
+ hcd->uses_new_polling = 1;
+
+ musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
++ musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
+ #else
+ musb = kzalloc(sizeof *musb, GFP_KERNEL);
+ if (!musb)
+@@ -1969,6 +1968,10 @@ bad_config:
+ if (status < 0)
+ goto fail2;
+
++#ifdef CONFIG_USB_OTG
++ setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
++#endif
++
+ /* Init IRQ workqueue before request_irq */
+ INIT_WORK(&musb->irq_work, musb_irq_work);
+
+diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
+index c3ee348..cf3ccb0 100644
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -40,6 +40,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/smp_lock.h>
+ #include <linux/errno.h>
++#include <linux/timer.h>
+ #include <linux/clk.h>
+ #include <linux/device.h>
+ #include <linux/usb/ch9.h>
+@@ -180,10 +181,15 @@ enum musb_g_ep0_state {
+ MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
+ } __attribute__ ((packed));
+
+-/* OTG protocol constants */
++/*
++ * OTG protocol constants. See USB OTG 1.3 spec,
++ * sections 5.5 "Device Timings" and 6.6.5 "Timers".
++ */
+ #define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
+-#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */
+-#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */
++#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
++#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
++#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
++
+
+ /*************************** REGISTER ACCESS ********************************/
+
+@@ -332,6 +338,8 @@ struct musb {
+ struct list_head control; /* of musb_qh */
+ struct list_head in_bulk; /* of musb_qh */
+ struct list_head out_bulk; /* of musb_qh */
++
++ struct timer_list otg_timer;
+ #endif
+
+ /* called with IRQs blocked; ON/nonzero implies starting a session,
+diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
+index 5f67b03..3fbc807 100644
+--- a/drivers/usb/musb/omap2430.c
++++ b/drivers/usb/musb/omap2430.c
+@@ -45,7 +45,6 @@
+ #define get_cpu_rev() 2
+ #endif
+
+-#define MUSB_TIMEOUT_A_WAIT_BCON 1100
+
+ static struct timer_list musb_idle_timer;
+
+@@ -246,7 +245,6 @@ int __init musb_platform_init(struct musb *musb)
+
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = omap_set_vbus;
+- musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
+
+ setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
+
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch b/recipes/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch
new file mode 100644
index 0000000000..6269016223
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch
@@ -0,0 +1,133 @@
+From a637c5056ef52fbb7c41eb7537a9ec3d150231ad Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Thu, 2 Apr 2009 10:16:11 -0700
+Subject: [PATCH] musb: make initial HNP roleswitch work (v2)
+
+Minor HNP bugfixes, so the initial role switch works:
+
+ - A-Device:
+ * disconnect-during-suspend enters A_PERIPHERAL state
+ * kill OTG timer after reset as A_PERIPHERAL ...
+ * ... and also pass that reset to the gadget
+ * once HNP succeeds, clear the "ignore_disconnect" flag
+ * from A_PERIPHERAL, disconnect transitions to A_WAIT_BCON
+
+ - B-Device:
+ * kill OTG timer on entry to B_HOST state (HNP succeeded)
+ * once HNP succeeds, clear "ignore_disconnect" flag
+ * kick the root hub only _after_ the state is adjusted
+
+Other state transitions are left alone. Notably, exit paths from
+the "roles have switched" state ... A_PERIPHERAL handling of that
+stays seriously broken.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 27 ++++++++++++++++-----------
+ drivers/usb/musb/musb_gadget.c | 2 +-
+ drivers/usb/musb/musb_virthub.c | 11 ++++++++++-
+ 3 files changed, 27 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index 05c5dd3..9dc995a 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -587,28 +587,23 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ if (devctl & MUSB_DEVCTL_LSDEV)
+ musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+
+- if (hcd->status_urb)
+- usb_hcd_poll_rh_status(hcd);
+- else
+- usb_hcd_resume_root_hub(hcd);
+-
+- MUSB_HST_MODE(musb);
+-
+ /* indicate new connection to OTG machine */
+ 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;
+- hcd->self.is_b_host = 1;
+ int_usb &= ~MUSB_INTR_SUSPEND;
++ goto b_host;
+ } else
+ DBG(1, "CONNECT as b_peripheral???\n");
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+- DBG(1, "HNP: Waiting to switch to b_host state\n");
++ DBG(1, "HNP: CONNECT, now b_host\n");
++b_host:
+ musb->xceiv->state = OTG_STATE_B_HOST;
+ hcd->self.is_b_host = 1;
++ musb->ignore_disconnect = 0;
++ del_timer(&musb->otg_timer);
+ break;
+ default:
+ if ((devctl & MUSB_DEVCTL_VBUS)
+@@ -618,6 +613,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ }
+ break;
+ }
++
++ /* poke the root hub */
++ MUSB_HST_MODE(musb);
++ if (hcd->status_urb)
++ usb_hcd_poll_rh_status(hcd);
++ else
++ usb_hcd_resume_root_hub(hcd);
++
+ DBG(1, "CONNECT (%s) devctl %02x\n",
+ otg_state_string(musb), devctl);
+ }
+@@ -662,7 +665,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ + msecs_to_jiffies(TA_WAIT_BCON(musb)));
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+- musb_hnp_stop(musb);
++ musb->ignore_disconnect = 0;
++ del_timer(&musb->otg_timer);
++ musb_g_reset(musb);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ DBG(1, "HNP: RESET (%s), to b_peripheral\n",
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index 2fbfba5..7dd3d59 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -1964,7 +1964,7 @@ void musb_g_disconnect(struct musb *musb)
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
+index 7e7900f..14f7cf3 100644
+--- a/drivers/usb/musb/musb_virthub.c
++++ b/drivers/usb/musb/musb_virthub.c
+@@ -187,8 +187,17 @@ void musb_root_disconnect(struct musb *musb)
+ musb->is_active = 0;
+
+ switch (musb->xceiv->state) {
+- case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
++#ifdef CONFIG_USB_MUSB_OTG
++ if (is_otg_enabled(musb)
++ && musb->xceiv->host->b_hnp_enable) {
++ musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
++ musb->g.is_a_peripheral = 1;
++ break;
++ }
++#endif
++ /* FALLTHROUGH */
++ case OTG_STATE_A_HOST:
+ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->is_active = 0;
+ break;
+--
+1.6.0.4
+
diff --git a/recipes/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch b/recipes/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch
new file mode 100644
index 0000000000..fc34fb983e
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch
@@ -0,0 +1,145 @@
+From 4288b7df4ae6629a4fb14aca2c489da01d4d19c3 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+Date: Tue, 31 Mar 2009 12:35:09 -0700
+Subject: [PATCH] musb: support disconnect after HNP roleswitch
+
+Adjust HNP state machines in MUSB driver so that they handle the
+case where the cable is disconnected. The A-side machine was
+very wrong (unrecoverable); the B-Side was much less so.
+
+ - A_PERIPHERAL ... as usual, the non-observability of the ID
+ pin through Mentor's registers makes trouble. We can't go
+ directly to A_WAIT_VFALL to end the session and start the
+ disconnect processing. We can however sense link suspending,
+ go to A_WAIT_BCON, and from there use OTG timeouts to finally
+ trigger that A_WAIT_VFALL transition. (Hoping that nobody
+ reconnects quickly to that port and notices the wrong state.)
+
+ - B_HOST ... actually clear the Host Request (HR) bit as the
+ messages say, disconnect the peripheral from the root hub,
+ and don't detour through a suspend state. (In some cases
+ this would eventually have cleaned up.)
+
+Also adjust the A_SUSPEND transition to respect the A_AIDL_BDIS
+timeout, so if HNP doesn't trigger quickly enough the A_WAIT_VFALL
+transition happens as it should.
+
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/musb_core.c | 41 +++++++++++++++++++++++++++-----------
+ drivers/usb/musb/musb_gadget.c | 2 +
+ drivers/usb/musb/musb_virthub.c | 4 +++
+ 3 files changed, 35 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
+index 9dc995a..5770ccb 100644
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -304,9 +304,11 @@ void musb_otg_timer_func(unsigned long data)
+ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->is_active = 0;
+ break;
++ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_WAIT_BCON:
+- DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
+- musb_hnp_stop(musb);
++ DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
++ musb_set_vbus(musb, 0);
++ musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ default:
+ DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
+@@ -324,15 +326,12 @@ void musb_hnp_stop(struct musb *musb)
+ void __iomem *mbase = musb->mregs;
+ u8 reg;
+
++ DBG(1, "HNP: stop from %s\n", otg_state_string(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->xceiv->state = OTG_STATE_A_IDLE;
+- MUSB_HST_MODE(musb);
+- musb->is_active = 0;
++ DBG(1, "HNP: back to %s\n", otg_state_string(musb));
+ break;
+ case OTG_STATE_B_HOST:
+ DBG(1, "HNP: Disabling HR\n");
+@@ -775,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ #endif /* HOST */
+ #ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_B_HOST:
+- musb_hnp_stop(musb);
++ /* REVISIT this behaves for "real disconnect"
++ * cases; make sure the other transitions from
++ * from B_HOST act right too. The B_HOST code
++ * in hnp_stop() is currently not used...
++ */
++ musb_root_disconnect(musb);
++ musb_to_hcd(musb)->self.is_b_host = 0;
++ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
++ MUSB_DEV_MODE(musb);
++ musb_g_disconnect(musb);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+@@ -807,10 +815,19 @@ 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:
+- /*
+- * We cannot stop HNP here, devctl BDEVICE might be
+- * still set.
++ /* We also come here if the cable is removed, since
++ * this silicon doesn't report ID-no-longer-grounded.
++ *
++ * We depend on T(a_wait_bcon) to shut us down, and
++ * hope users don't do anything dicey during this
++ * undesired detour through A_WAIT_BCON.
+ */
++ musb_hnp_stop(musb);
++ usb_hcd_resume_root_hub(musb_to_hcd(musb));
++ musb_root_disconnect(musb);
++ musb_platform_try_idle(musb, jiffies
++ + msecs_to_jiffies(musb->a_wait_bcon
++ ? : OTG_TIME_A_WAIT_BCON));
+ break;
+ #endif
+ case OTG_STATE_B_PERIPHERAL:
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index 7dd3d59..8b3c4e2 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -1962,9 +1962,11 @@ void musb_g_disconnect(struct musb *musb)
+ DBG(2, "Unhandled disconnect %s, setting a_idle\n",
+ otg_state_string(musb));
+ musb->xceiv->state = OTG_STATE_A_IDLE;
++ MUSB_HST_MODE(musb);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
++ MUSB_HST_MODE(musb);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
+index 14f7cf3..e8ef925 100644
+--- a/drivers/usb/musb/musb_virthub.c
++++ b/drivers/usb/musb/musb_virthub.c
+@@ -83,6 +83,10 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
+ musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->is_active = is_otg_enabled(musb)
+ && musb->xceiv->host->b_hnp_enable;
++ if (musb->is_active)
++ mod_timer(&musb->otg_timer, jiffies
++ + msecs_to_jiffies(
++ OTG_TIME_A_AIDL_BDIS));
+ musb_platform_try_idle(musb, 0);
+ break;
+ #ifdef CONFIG_USB_MUSB_OTG
+--
+1.6.0.4
+