1 MUSB: Workaround for simultaneous TX and RX usage
3 MUSB RTL V1.4 has a hardware issue which results in a DMA controller
4 hang when TX and RX DMA channels are simultaneously enabled. This
5 affects at least OMAP2430 and OMAP34XX.
7 Since RX transfers are in Mode 0 and anyway result in one DMA interrupt
8 per packet, we can use System DMA to unload the RX fifos. MUSB DMA can
9 be used for all TX channels as before.
11 Tested with full-duplex TX and RX transfers using g_ether. Runs for 24
12 hours without a hang. Without this patch, the hang occurs within minutes.
14 This issue was first reported by Jon Hunter on [1]
16 [1] http://marc.info/?l=linux-omap&m=119634480534453&w=2
18 Signed-off-by: Anand Gadiyar <gadiyar@ti.com>
20 Patch based on the linux-omap tree. I believe the tabs-to-spaces issue
21 has been resolved now, but this is the first patch I'm sending out to the
24 Suggestions welcome for removing the 2 #ifdefs below. One suggestion was to
25 use a module parameter to decide whether to use system dma or mentor dma.
27 Patch updated with a few ifdefs removed and one compilation break resolved.
29 drivers/usb/musb/Kconfig | 8 ++
30 drivers/usb/musb/musbhsdma.c | 163 +++++++++++++++++++++++++++++++++++--------
31 2 files changed, 142 insertions(+), 29 deletions(-)
33 Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.c
34 ===================================================================
35 --- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.c 2008-07-28 10:54:37.000000000 +0530
36 +++ linux-omap-2.6/drivers/usb/musb/musbhsdma.c 2008-08-13 11:58:26.272902373 +0530
38 #include <linux/interrupt.h>
39 #include <linux/platform_device.h>
40 #include "musb_core.h"
41 +#include <mach/dma.h>
43 #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
47 #define MUSB_HSDMA_CHANNELS 8
49 +#define MUSB_FIFO_ADDRESS(epnum) \
50 + ((unsigned long) (OMAP_HSOTG_BASE + MUSB_FIFO_OFFSET(epnum)))
52 struct musb_dma_controller;
54 struct musb_dma_channel {
55 @@ -75,6 +79,8 @@ struct musb_dma_channel {
63 struct musb_dma_controller {
64 @@ -93,6 +99,42 @@ static int dma_controller_start(struct d
68 +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
69 +static void musb_sysdma_completion(int lch, u16 ch_status, void *data)
72 + unsigned long flags;
74 + struct dma_channel *pChannel;
76 + struct musb_dma_channel *pImplChannel =
77 + (struct musb_dma_channel *) data;
78 + struct musb_dma_controller *controller = pImplChannel->controller;
79 + struct musb *musb = controller->pDmaPrivate;
80 + pChannel = &pImplChannel->Channel;
82 + DBG(2, "lch = 0x%d, ch_status = 0x%x\n", lch, ch_status);
83 + spin_lock_irqsave(&musb->lock, flags);
85 + dwAddress = (u32) omap_get_dma_dst_pos(pImplChannel->sysdma_channel);
86 + pChannel->actual_len = dwAddress - pImplChannel->dwStartAddress;
88 + DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n",
89 + pChannel, pImplChannel->dwStartAddress, dwAddress,
90 + pChannel->actual_len, pImplChannel->len,
91 + (pChannel->actual_len < pImplChannel->len) ?
92 + "=> reconfig 0": "=> complete");
94 + pChannel->status = MUSB_DMA_STATUS_FREE;
95 + musb_dma_completion(musb, pImplChannel->epnum, pImplChannel->transmit);
97 + spin_unlock_irqrestore(&musb->lock, flags);
101 +#define musb_sysdma_completion NULL
104 static void dma_channel_release(struct dma_channel *pChannel);
106 static int dma_controller_stop(struct dma_controller *c)
107 @@ -144,6 +186,29 @@ static struct dma_channel *dma_channel_a
108 /* Tx => mode 1; Rx => mode 0 */
109 pChannel->desired_mode = transmit;
110 pChannel->actual_len = 0;
111 + pImplChannel->sysdma_channel = -1;
113 +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
116 + ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE,
117 + "MUSB SysDMA", musb_sysdma_completion,
118 + (void *) pImplChannel,
119 + &(pImplChannel->sysdma_channel));
122 + printk(KERN_ERR "request_dma failed:"
124 + controller->bmUsedChannels &=
127 + MUSB_DMA_STATUS_UNKNOWN;
128 + pImplChannel->sysdma_channel = -1;
137 @@ -163,6 +228,12 @@ static void dma_channel_release(struct d
138 ~(1 << pImplChannel->bIndex);
140 pChannel->status = MUSB_DMA_STATUS_UNKNOWN;
142 + if (pImplChannel->sysdma_channel != -1) {
143 + omap_stop_dma(pImplChannel->sysdma_channel);
144 + omap_free_dma(pImplChannel->sysdma_channel);
145 + pImplChannel->sysdma_channel = -1;
149 static void configure_channel(struct dma_channel *pChannel,
150 @@ -179,41 +250,69 @@ static void configure_channel(struct dma
151 DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n",
152 pChannel, packet_sz, dma_addr, len, mode);
155 - csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
156 - BUG_ON(len < packet_sz);
157 + if (pImplChannel->sysdma_channel != -1) {
159 + /* RX: set src = FIFO */
161 + omap_set_dma_transfer_params(pImplChannel->sysdma_channel,
162 + OMAP_DMA_DATA_TYPE_S8,
163 + len, 1, /* One frame */
164 + OMAP_DMA_SYNC_ELEMENT,
165 + OMAP24XX_DMA_NO_DEVICE,
168 + omap_set_dma_src_params(pImplChannel->sysdma_channel, 0,
169 + OMAP_DMA_AMODE_CONSTANT,
170 + MUSB_FIFO_ADDRESS(pImplChannel->epnum),
173 + omap_set_dma_dest_params(pImplChannel->sysdma_channel, 0,
174 + OMAP_DMA_AMODE_POST_INC, dma_addr,
177 + omap_set_dma_dest_data_pack(pImplChannel->sysdma_channel, 1);
178 + omap_set_dma_dest_burst_mode(pImplChannel->sysdma_channel,
179 + OMAP_DMA_DATA_BURST_16);
181 + omap_start_dma(pImplChannel->sysdma_channel);
183 + } else { /* Mentor DMA */
185 + csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
186 + BUG_ON(len < packet_sz);
188 - if (packet_sz >= 64) {
189 - csr |= MUSB_HSDMA_BURSTMODE_INCR16
190 + if (packet_sz >= 64) {
191 + csr |= MUSB_HSDMA_BURSTMODE_INCR16
192 << MUSB_HSDMA_BURSTMODE_SHIFT;
193 - } else if (packet_sz >= 32) {
194 - csr |= MUSB_HSDMA_BURSTMODE_INCR8
195 + } else if (packet_sz >= 32) {
196 + csr |= MUSB_HSDMA_BURSTMODE_INCR8
197 << MUSB_HSDMA_BURSTMODE_SHIFT;
198 - } else if (packet_sz >= 16) {
199 - csr |= MUSB_HSDMA_BURSTMODE_INCR4
200 + } else if (packet_sz >= 16) {
201 + csr |= MUSB_HSDMA_BURSTMODE_INCR4
202 << MUSB_HSDMA_BURSTMODE_SHIFT;
207 - csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT)
208 - | (1 << MUSB_HSDMA_ENABLE_SHIFT)
209 - | (1 << MUSB_HSDMA_IRQENABLE_SHIFT)
210 - | (pImplChannel->transmit
211 - ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
214 - /* address/count */
216 - MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
219 - MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
222 - /* control (this should start things) */
224 - MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
226 + csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT)
227 + | (1 << MUSB_HSDMA_ENABLE_SHIFT)
228 + | (1 << MUSB_HSDMA_IRQENABLE_SHIFT)
229 + | (pImplChannel->transmit
230 + ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
233 + /* address/count */
235 + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
238 + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
241 + /* control (this should start things) */
243 + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
248 static int dma_channel_program(struct dma_channel *pChannel,
249 @@ -265,6 +364,12 @@ static int dma_channel_abort(struct dma_
250 MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_TXCSR),
253 + if (pImplChannel->sysdma_channel != -1) {
254 + omap_stop_dma(pImplChannel->sysdma_channel);
255 + omap_free_dma(pImplChannel->sysdma_channel);
256 + pImplChannel->sysdma_channel = -1;
259 csr = musb_readw(mbase,
260 MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_RXCSR));
261 csr &= ~(MUSB_RXCSR_AUTOCLEAR |
262 Index: linux-omap-2.6/drivers/usb/musb/Kconfig
263 ===================================================================
264 --- linux-omap-2.6.orig/drivers/usb/musb/Kconfig 2008-07-28 10:54:37.000000000 +0530
265 +++ linux-omap-2.6/drivers/usb/musb/Kconfig 2008-08-12 13:18:34.000000000 +0530
266 @@ -150,6 +150,14 @@ config USB_INVENTRA_DMA
268 Enable DMA transfers using Mentor's engine.
270 +config MUSB_USE_SYSTEM_DMA_RX
271 + bool 'Use System DMA for RX endpoints'
272 + depends on USB_MUSB_HDRC && USB_INVENTRA_DMA
274 + MUSB RTL version 1.4 has a hardware issue when TX and RX DMA
275 + channels are simultaneously enabled. To work around this issue,
276 + you can choose to use System DMA for RX channels.
278 config USB_TI_CPPI_DMA
280 depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY--
281 To unsubscribe from this list: send the line "unsubscribe linux-omap" in
282 the body of a message to majordomo@vger.kernel.org
283 More majordomo info at http://vger.kernel.org/majordomo-info.html