aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/linux/linux-omap-2.6.29/musb
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@linux.intel.com>2010-08-27 15:14:24 +0100
committerRichard Purdie <rpurdie@linux.intel.com>2010-08-27 15:29:45 +0100
commit29d6678fd546377459ef75cf54abeef5b969b5cf (patch)
tree8edd65790e37a00d01c3f203f773fe4b5012db18 /meta/recipes-kernel/linux/linux-omap-2.6.29/musb
parentda49de6885ee1bc424e70bc02f21f6ab920efb55 (diff)
downloadopenembedded-core-contrib-29d6678fd546377459ef75cf54abeef5b969b5cf.tar.gz
Major layout change to the packages directory
Having one monolithic packages directory makes it hard to find things and is generally overwhelming. This commit splits it into several logical sections roughly based on function, recipes.txt gives more information about the classifications used. The opportunity is also used to switch from "packages" to "recipes" as used in OpenEmbedded as the term "packages" can be confusing to people and has many different meanings. Not all recipes have been classified yet, this is just a first pass at separating things out. Some packages are moved to meta-extras as they're no longer actively used or maintained. Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap-2.6.29/musb')
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch43
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch76
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch218
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch106
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch181
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch84
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch47
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch60
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch93
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch361
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch417
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch56
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch91
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch32
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch202
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch146
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch132
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch167
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch158
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch58
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch70
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch38
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch187
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch259
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch90
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch1109
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch198
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch133
-rw-r--r--meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0001-USB-musb-only-turn-off-vbus-in-OTG-hosts.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0002-USB-composite-avoid-inconsistent-lock-state.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0003-USB-musb-NAK-timeout-scheme-on-bulk-RX-endpoint.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0004-USB-musb-rewrite-host-periodic-endpoint-allocation.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0005-USB-TWL-disable-VUSB-regulators-when-cable-unplugg.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0006-USB-gadget-composite-device-level-suspend-resume-h.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0007-usb-gadget-fix-ethernet-link-reports-to-ethtool.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0008-usb-musb_host-minor-enqueue-locking-fix-v2.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0009-usb-musb_host-fix-ep0-fifo-flushing.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0012-musb-fix-possible-panic-while-resuming.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0013-musb_host-refactor-musb_save_toggle-take-2.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0014-musb_gadget-suppress-parasitic-TX-interrupts-with.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0015-musb_gadget-fix-unhandled-endpoint-0-IRQs.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0016-musb_host-factor-out-musb_ep_-get-set-_qh.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0017-musb_host-refactor-URB-giveback.patch
new file mode 100644
index 0000000000..4a520dff87
--- /dev/null
+++ b/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0018-musb-split-out-CPPI-interrupt-handler.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0019-musb_host-simplify-check-for-active-URB.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0020-musb_host-streamline-musb_cleanup_urb-calls.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0021-twl4030-usb-fix-minor-reporting-goofage.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0022-musb-use-dma-mode-1-for-TX-if-transfer-size-equals.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0023-musb-add-high-bandwidth-ISO-support.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0024-USB-otg-adding-nop-usb-transceiver.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0025-nop-usb-xceiv-behave-when-linked-as-a-module.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0026-musb-proper-hookup-to-transceiver-drivers.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0027-musb-otg-timer-cleanup.patch
new file mode 100644
index 0000000000..f41b766cfe
--- /dev/null
+++ b/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0028-musb-make-initial-HNP-roleswitch-work-v2.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch b/meta/recipes-kernel/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/meta/recipes-kernel/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
+