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