omap3-pandora-kernel2: update
[openpandora.oe.git] / recipes / linux / omap3-pandora-kernel / musb-rxtx.patch
1 MUSB: Workaround for simultaneous TX and RX usage
2
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.
6
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.
10
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.
13
14 This issue was first reported by Jon Hunter on [1]
15
16 [1] http://marc.info/?l=linux-omap&m=119634480534453&w=2
17
18 Signed-off-by: Anand Gadiyar <gadiyar@ti.com>
19 ---
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
22 list after that.
23
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.
26
27 Patch updated with a few ifdefs removed and one compilation break resolved.
28
29  drivers/usb/musb/Kconfig     |    8 ++
30  drivers/usb/musb/musbhsdma.c |  163 +++++++++++++++++++++++++++++++++++--------
31  2 files changed, 142 insertions(+), 29 deletions(-)
32
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
37 @@ -34,6 +34,7 @@
38  #include <linux/interrupt.h>
39  #include <linux/platform_device.h>
40  #include "musb_core.h"
41 +#include <mach/dma.h>
42  
43  #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
44  #include "omap2430.h"
45 @@ -64,6 +65,9 @@
46  
47  #define MUSB_HSDMA_CHANNELS            8
48  
49 +#define MUSB_FIFO_ADDRESS(epnum)       \
50 +       ((unsigned long) (OMAP_HSOTG_BASE + MUSB_FIFO_OFFSET(epnum)))
51 +
52  struct musb_dma_controller;
53  
54  struct musb_dma_channel {
55 @@ -75,6 +79,8 @@ struct musb_dma_channel {
56         u8                              bIndex;
57         u8                              epnum;
58         u8                              transmit;
59 +
60 +       int                             sysdma_channel;
61  };
62  
63  struct musb_dma_controller {
64 @@ -93,6 +99,42 @@ static int dma_controller_start(struct d
65         return 0;
66  }
67  
68 +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
69 +static void musb_sysdma_completion(int lch, u16 ch_status, void *data)
70 +{
71 +       u32 dwAddress;
72 +       unsigned long flags;
73 +
74 +       struct dma_channel *pChannel;
75 +
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;
81 +
82 +       DBG(2, "lch = 0x%d, ch_status = 0x%x\n", lch, ch_status);
83 +       spin_lock_irqsave(&musb->lock, flags);
84 +
85 +       dwAddress = (u32) omap_get_dma_dst_pos(pImplChannel->sysdma_channel);
86 +       pChannel->actual_len = dwAddress - pImplChannel->dwStartAddress;
87 +
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");
93 +
94 +       pChannel->status = MUSB_DMA_STATUS_FREE;
95 +       musb_dma_completion(musb, pImplChannel->epnum, pImplChannel->transmit);
96 +
97 +       spin_unlock_irqrestore(&musb->lock, flags);
98 +       return;
99 +}
100 +#else
101 +#define musb_sysdma_completion NULL
102 +#endif
103 +
104  static void dma_channel_release(struct dma_channel *pChannel);
105  
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;
112 +
113 +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
114 +                       if (!transmit) {
115 +                               int ret;
116 +                               ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE,
117 +                                       "MUSB SysDMA", musb_sysdma_completion,
118 +                                       (void *) pImplChannel,
119 +                                       &(pImplChannel->sysdma_channel));
120 +
121 +                               if (ret) {
122 +                                       printk(KERN_ERR "request_dma failed:"
123 +                                                       " %d\n", ret);
124 +                                       controller->bmUsedChannels &=
125 +                                                               ~(1 << bBit);
126 +                                       pChannel->status =
127 +                                                       MUSB_DMA_STATUS_UNKNOWN;
128 +                                       pImplChannel->sysdma_channel = -1;
129 +                                       pChannel = NULL;
130 +                               }
131 +                       }
132 +#endif
133 +
134                         break;
135                 }
136         }
137 @@ -163,6 +228,12 @@ static void dma_channel_release(struct d
138                 ~(1 << pImplChannel->bIndex);
139  
140         pChannel->status = MUSB_DMA_STATUS_UNKNOWN;
141 +
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;
146 +       }
147  }
148  
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);
153  
154 -       if (mode) {
155 -               csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
156 -               BUG_ON(len < packet_sz);
157 +       if (pImplChannel->sysdma_channel != -1) {
158 +       /* System DMA */
159 +       /* RX: set src = FIFO */
160 +
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,
166 +                                       0); /* Src Sync */
167 +
168 +               omap_set_dma_src_params(pImplChannel->sysdma_channel, 0,
169 +                                       OMAP_DMA_AMODE_CONSTANT,
170 +                                       MUSB_FIFO_ADDRESS(pImplChannel->epnum),
171 +                                       0, 0);
172 +
173 +               omap_set_dma_dest_params(pImplChannel->sysdma_channel, 0,
174 +                                       OMAP_DMA_AMODE_POST_INC, dma_addr,
175 +                                       0, 0);
176 +
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);
180 +
181 +               omap_start_dma(pImplChannel->sysdma_channel);
182 +
183 +       } else { /* Mentor DMA */
184 +               if (mode) {
185 +                       csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
186 +                       BUG_ON(len < packet_sz);
187  
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;
203 +                       }
204                 }
205 -       }
206  
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)
212 -                               : 0);
213 -
214 -       /* address/count */
215 -       musb_writel(mbase,
216 -               MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
217 -               dma_addr);
218 -       musb_writel(mbase,
219 -               MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
220 -               len);
221 -
222 -       /* control (this should start things) */
223 -       musb_writew(mbase,
224 -               MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
225 -               csr);
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)
231 +                                       : 0);
232 +
233 +               /* address/count */
234 +               musb_writel(mbase,
235 +                       MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
236 +                       dma_addr);
237 +               musb_writel(mbase,
238 +                       MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
239 +                       len);
240 +
241 +               /* control (this should start things) */
242 +               musb_writew(mbase,
243 +                       MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
244 +                       csr);
245 +       } /* Mentor DMA */
246  }
247  
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),
251                                 csr);
252                 } else {
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;
257 +                       }
258 +
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
267         help
268           Enable DMA transfers using Mentor's engine.
269  
270 +config MUSB_USE_SYSTEM_DMA_RX
271 +       bool 'Use System DMA for RX endpoints'
272 +       depends on USB_MUSB_HDRC && USB_INVENTRA_DMA
273 +       help
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.
277 +
278  config USB_TI_CPPI_DMA
279         bool
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