diff options
Diffstat (limited to 'firmware/target/arm/usb-designware.c')
-rw-r--r-- | firmware/target/arm/usb-designware.c | 1381 |
1 files changed, 1381 insertions, 0 deletions
diff --git a/firmware/target/arm/usb-designware.c b/firmware/target/arm/usb-designware.c new file mode 100644 index 0000000000..24c6055434 --- /dev/null +++ b/firmware/target/arm/usb-designware.c | |||
@@ -0,0 +1,1381 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009-2014 by Michael Sparmann | ||
11 | * Copyright © 2010 Amaury Pouly | ||
12 | * Copyright (C) 2014 by Marcin Bukat | ||
13 | * Copyright (C) 2016 by Cástor Muñoz | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | #include <inttypes.h> | ||
25 | #include <string.h> | ||
26 | |||
27 | #include "config.h" | ||
28 | #include "cpu.h" | ||
29 | #include "system.h" | ||
30 | #include "kernel.h" | ||
31 | #include "panic.h" | ||
32 | #include "power.h" | ||
33 | #include "usb.h" | ||
34 | #include "usb_drv.h" | ||
35 | #include "usb_ch9.h" | ||
36 | #include "usb_core.h" | ||
37 | |||
38 | #include "usb-designware.h" | ||
39 | |||
40 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
41 | /*#define LOGF_ENABLE*/ | ||
42 | #include "logf.h" | ||
43 | |||
44 | |||
45 | /* The ARM940T uses a subset of the ARMv4 functions, not | ||
46 | * supporting clean/invalidate cache entries using MVA. | ||
47 | */ | ||
48 | #if CONFIG_CPU == S5L8701 | ||
49 | #define DISCARD_DCACHE_RANGE(b,s) commit_discard_dcache() | ||
50 | #define COMMIT_DCACHE_RANGE(b,s) commit_dcache() | ||
51 | #else | ||
52 | #define DISCARD_DCACHE_RANGE(b,s) discard_dcache_range(b,s) | ||
53 | #define COMMIT_DCACHE_RANGE(b,s) commit_dcache_range(b,s) | ||
54 | #endif | ||
55 | |||
56 | #ifndef USB_DW_TOUTCAL | ||
57 | #define USB_DW_TOUTCAL 0 | ||
58 | #endif | ||
59 | |||
60 | #define GET_DTXFNUM(ep) ((DWC_DIEPCTL(ep)>>22) & 0xf) | ||
61 | |||
62 | #define USB_DW_NUM_DIRS 2 | ||
63 | #define USB_DW_DIR_OFF(dir) (((dir) == USB_DW_EPDIR_IN) ? 0 : 16) | ||
64 | |||
65 | enum usb_dw_epdir | ||
66 | { | ||
67 | USB_DW_EPDIR_IN = 0, | ||
68 | USB_DW_EPDIR_OUT = 1, | ||
69 | }; | ||
70 | |||
71 | union usb_ep0_buffer | ||
72 | { | ||
73 | struct usb_ctrlrequest setup; | ||
74 | uint8_t raw[64]; | ||
75 | }; | ||
76 | |||
77 | static union usb_ep0_buffer ep0_buffer USB_DEVBSS_ATTR; | ||
78 | |||
79 | /* Internal EP state/info */ | ||
80 | struct usb_dw_ep | ||
81 | { | ||
82 | struct semaphore complete; | ||
83 | uint32_t* req_addr; | ||
84 | uint32_t req_size; | ||
85 | uint32_t* addr; | ||
86 | uint32_t sizeleft; | ||
87 | uint32_t size; | ||
88 | int8_t status; | ||
89 | uint8_t active; | ||
90 | uint8_t busy; | ||
91 | }; | ||
92 | |||
93 | static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS]; | ||
94 | |||
95 | static uint32_t usb_endpoints; /* available EPs mask */ | ||
96 | |||
97 | /* For SHARED_FIFO mode this is the number of periodic Tx FIFOs | ||
98 | (usually 1), otherwise it is the number of dedicated Tx FIFOs | ||
99 | (not counting NPTX FIFO that is always dedicated for IN0). */ | ||
100 | static int n_ptxfifos; | ||
101 | static uint16_t ptxfifo_usage; | ||
102 | |||
103 | static uint32_t hw_maxbytes; | ||
104 | static uint32_t hw_maxpackets; | ||
105 | #ifdef USB_DW_SHARED_FIFO | ||
106 | static uint8_t hw_nptxqdepth; | ||
107 | static uint32_t epmis_msk; | ||
108 | static uint32_t ep_periodic_msk; | ||
109 | #endif | ||
110 | |||
111 | static const char *dw_dir_str[USB_DW_NUM_DIRS] = | ||
112 | { | ||
113 | [USB_DW_EPDIR_IN] = "IN", | ||
114 | [USB_DW_EPDIR_OUT] = "OUT", | ||
115 | }; | ||
116 | |||
117 | |||
118 | static struct usb_dw_ep *usb_dw_get_ep(int epnum, enum usb_dw_epdir epdir) | ||
119 | { | ||
120 | return &usb_dw_ep_list[epnum][epdir]; | ||
121 | } | ||
122 | |||
123 | static int usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir) | ||
124 | { | ||
125 | return epnum ? DWC_EPCTL(epnum, epdir) & 0x3ff : 64; | ||
126 | } | ||
127 | |||
128 | static int usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir) | ||
129 | { | ||
130 | return epnum ? ALIGN_DOWN_P2(MIN(hw_maxbytes, | ||
131 | hw_maxpackets*usb_dw_maxpktsize(epnum, epdir)), CACHEALIGN_BITS) : 64; | ||
132 | } | ||
133 | |||
134 | /* Calculate number of packets (if size == 0 an empty packet will be sent) */ | ||
135 | static int usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize) | ||
136 | { | ||
137 | int packets = (size + maxpktsize - 1) / maxpktsize; | ||
138 | if (!packets) packets = 1; | ||
139 | return packets; | ||
140 | } | ||
141 | |||
142 | static int usb_dw_get_stall(int epnum, enum usb_dw_epdir epdir) | ||
143 | { | ||
144 | return !!(DWC_EPCTL(epnum, epdir) & STALL); | ||
145 | } | ||
146 | |||
147 | static void usb_dw_set_stall(int epnum, enum usb_dw_epdir epdir, int stall) | ||
148 | { | ||
149 | if (stall) | ||
150 | { | ||
151 | DWC_EPCTL(epnum, epdir) |= STALL; | ||
152 | } | ||
153 | else | ||
154 | { | ||
155 | DWC_EPCTL(epnum, epdir) &= ~STALL; | ||
156 | DWC_EPCTL(epnum, epdir) |= SD0PID; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static void usb_dw_set_address(uint8_t address) | ||
161 | { | ||
162 | DWC_DCFG = (DWC_DCFG & ~(0x7f0)) | DAD(address); | ||
163 | } | ||
164 | |||
165 | static void usb_dw_wait_for_ahb_idle(void) | ||
166 | { | ||
167 | while (!(DWC_GRSTCTL & AHBIDL)); | ||
168 | } | ||
169 | |||
170 | #ifdef USB_DW_SHARED_FIFO | ||
171 | static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes) | ||
172 | { | ||
173 | uint32_t size = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN)->size; | ||
174 | if (sentbytes) *sentbytes = size; | ||
175 | uint32_t dieptsiz = DWC_DIEPTSIZ(epnum); | ||
176 | uint32_t packetsleft = (dieptsiz >> 19) & 0x3ff; | ||
177 | if (!packetsleft) return 0; | ||
178 | int maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN); | ||
179 | int packets = usb_dw_calc_packets(size, maxpktsize); | ||
180 | uint32_t bytesleft = dieptsiz & 0x7ffff; | ||
181 | uint32_t bytespushed = size - bytesleft; | ||
182 | uint32_t bytespulled = (packets - packetsleft) * maxpktsize; | ||
183 | |||
184 | if (sentbytes) *sentbytes = bytespulled; | ||
185 | return bytespushed - bytespulled; | ||
186 | } | ||
187 | #endif | ||
188 | |||
189 | #ifdef USB_DW_ARCH_SLAVE | ||
190 | /* Read one packet/token from Rx FIFO */ | ||
191 | static void usb_dw_handle_rxfifo(void) | ||
192 | { | ||
193 | uint32_t rxsts = DWC_GRXSTSP; | ||
194 | int pktsts = (rxsts >> 17) & 0xf; | ||
195 | |||
196 | switch (pktsts) | ||
197 | { | ||
198 | case PKTSTS_OUTRX: | ||
199 | case PKTSTS_SETUPRX: | ||
200 | { | ||
201 | int ep = rxsts & 0xf; | ||
202 | int words = (((rxsts >> 4) & 0x7ff) + 3) >> 2; | ||
203 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT); | ||
204 | if (dw_ep->busy) | ||
205 | { | ||
206 | while (words--) | ||
207 | *dw_ep->addr++ = DWC_DFIFO(0); | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | /* Discard data */ | ||
212 | while (words--) | ||
213 | (void) DWC_DFIFO(0); | ||
214 | } | ||
215 | break; | ||
216 | } | ||
217 | case PKTSTS_OUTDONE: | ||
218 | case PKTSTS_SETUPDONE: | ||
219 | case PKTSTS_GLOBALOUTNAK: | ||
220 | default: | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | #ifdef USB_DW_SHARED_FIFO | ||
226 | static void usb_dw_try_push(int epnum) | ||
227 | { | ||
228 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN); | ||
229 | |||
230 | if (!dw_ep->busy) | ||
231 | return; | ||
232 | |||
233 | if (epmis_msk & (1 << epnum)) | ||
234 | return; | ||
235 | |||
236 | uint32_t wordsleft = ((DWC_DIEPTSIZ(epnum) & 0x7ffff) + 3) >> 2; | ||
237 | if (!wordsleft) return; | ||
238 | |||
239 | /* Get fifo space for NPTXFIFO or PTXFIFO */ | ||
240 | uint32_t fifospace; | ||
241 | int dtxfnum = GET_DTXFNUM(epnum); | ||
242 | if (dtxfnum) | ||
243 | { | ||
244 | uint32_t fifosize = DWC_DIEPTXF(dtxfnum - 1) >> 16; | ||
245 | fifospace = fifosize - ((usb_dw_bytes_in_txfifo(epnum, NULL) + 3) >> 2); | ||
246 | } | ||
247 | else | ||
248 | { | ||
249 | uint32_t gnptxsts = DWC_GNPTXSTS; | ||
250 | fifospace = ((gnptxsts >> 16) & 0xff) ? (gnptxsts & 0xffff) : 0; | ||
251 | } | ||
252 | |||
253 | uint32_t maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN); | ||
254 | uint32_t words = MIN((maxpktsize + 3) >> 2, wordsleft); | ||
255 | |||
256 | if (fifospace >= words) | ||
257 | { | ||
258 | wordsleft -= words; | ||
259 | while (words--) | ||
260 | DWC_DFIFO(epnum) = *dw_ep->addr++; | ||
261 | } | ||
262 | |||
263 | if (wordsleft) | ||
264 | DWC_GINTMSK |= (dtxfnum ? PTXFE : NPTXFE); | ||
265 | } | ||
266 | |||
267 | #else /* !USB_DW_SHARED_FIFO */ | ||
268 | static void usb_dw_handle_dtxfifo(int epnum) | ||
269 | { | ||
270 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN); | ||
271 | |||
272 | if (!dw_ep->busy) | ||
273 | return; | ||
274 | |||
275 | uint32_t wordsleft = ((DWC_DIEPTSIZ(epnum) & 0x7ffff) + 3) >> 2; | ||
276 | |||
277 | while (wordsleft) | ||
278 | { | ||
279 | uint32_t words = wordsleft; | ||
280 | uint32_t fifospace = DWC_DTXFSTS(epnum) & 0xffff; | ||
281 | |||
282 | if (fifospace < words) | ||
283 | { | ||
284 | /* We push whole packets to read consistent info on DIEPTSIZ | ||
285 | (i.e. when FIFO size is not maxpktsize multiplo). */ | ||
286 | int maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2; | ||
287 | words = (fifospace / maxpktwords) * maxpktwords; | ||
288 | } | ||
289 | |||
290 | if (!words) | ||
291 | break; | ||
292 | |||
293 | wordsleft -= words; | ||
294 | while (words--) | ||
295 | DWC_DFIFO(epnum) = *dw_ep->addr++; | ||
296 | } | ||
297 | |||
298 | if (!wordsleft) | ||
299 | DWC_DIEPEMPMSK &= ~(1 << GET_DTXFNUM(epnum)); | ||
300 | } | ||
301 | #endif /* !USB_DW_SHARED_FIFO */ | ||
302 | #endif /* USB_DW_ARCH_SLAVE */ | ||
303 | |||
304 | static void usb_dw_flush_fifo(uint32_t fflsh, int fnum) | ||
305 | { | ||
306 | #ifdef USB_DW_ARCH_SLAVE | ||
307 | /* Rx queue must be emptied before flushing Rx FIFO */ | ||
308 | if (fflsh & RXFFLSH) | ||
309 | while (DWC_GINTSTS & RXFLVL) | ||
310 | usb_dw_handle_rxfifo(); | ||
311 | #else | ||
312 | /* Wait for any DMA activity to stop */ | ||
313 | usb_dw_wait_for_ahb_idle(); | ||
314 | #endif | ||
315 | DWC_GRSTCTL = TXFNUM(fnum) | fflsh; | ||
316 | while (DWC_GRSTCTL & fflsh); | ||
317 | udelay(1); /* Wait 3 PHY cycles */ | ||
318 | } | ||
319 | |||
320 | /* These are the conditions that must be met so that the application can | ||
321 | * disable an endpoint avoiding race conditions: | ||
322 | * | ||
323 | * 1) The endpoint must be enabled when EPDIS is written, otherwise the | ||
324 | * core will never raise EPDISD interrupt (thus EPDIS remains enabled). | ||
325 | * | ||
326 | * 2) - Periodic (SHARED_FIFO) or dedicated (!SHARED_FIFO) IN endpoints: | ||
327 | * IN NAK must be effective, to ensure that the core is not going | ||
328 | * to disable the EP just before EPDIS is written. | ||
329 | * - Non-periodic (SHARED_FIFO) IN endpoints: use usb_dw_nptx_unqueue(). | ||
330 | * - OUT endpoints: GONAK must be effective, this also ensures that the | ||
331 | * core is not going to disable the EP. | ||
332 | */ | ||
333 | static void usb_dw_disable_ep(int epnum, enum usb_dw_epdir epdir) | ||
334 | { | ||
335 | if (!epnum && (epdir == USB_DW_EPDIR_OUT)) | ||
336 | return; /* The application cannot disable OUT0 */ | ||
337 | |||
338 | if (DWC_EPCTL(epnum, epdir) & EPENA) | ||
339 | { | ||
340 | int tmo = 50; | ||
341 | DWC_EPCTL(epnum, epdir) |= EPDIS; | ||
342 | while (DWC_EPCTL(epnum, epdir) & EPDIS) | ||
343 | { | ||
344 | if (!tmo--) | ||
345 | panicf("%s: %s%d failed!", __func__, dw_dir_str[epdir], epnum); | ||
346 | udelay(1); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | static void usb_dw_gonak_effective(bool enable) | ||
352 | { | ||
353 | if (enable) | ||
354 | { | ||
355 | if (!(DWC_DCTL & GONSTS)) | ||
356 | DWC_DCTL |= SGONAK; | ||
357 | |||
358 | /* Wait for global IN NAK effective */ | ||
359 | int tmo = 50; | ||
360 | while (~DWC_GINTSTS & GOUTNAKEFF) | ||
361 | { | ||
362 | if (!tmo--) panicf("%s: failed!", __func__); | ||
363 | #ifdef USB_DW_ARCH_SLAVE | ||
364 | /* Pull Rx queue until GLOBALOUTNAK token is received. */ | ||
365 | if (DWC_GINTSTS & RXFLVL) | ||
366 | usb_dw_handle_rxfifo(); | ||
367 | else | ||
368 | #endif | ||
369 | udelay(1); | ||
370 | } | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | if (DWC_DCTL & GONSTS) | ||
375 | DWC_DCTL |= CGONAK; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | static void usb_dw_set_innak_effective(int epnum) | ||
380 | { | ||
381 | if (~DWC_DIEPCTL(epnum) & NAKSTS) | ||
382 | { | ||
383 | /* Wait for IN NAK effective avoiding race conditions, if the | ||
384 | * endpoint is disabled by the core (or it was already disabled) | ||
385 | * then INEPNE is never raised. | ||
386 | */ | ||
387 | int tmo = 50; | ||
388 | DWC_DIEPCTL(epnum) |= SNAK; | ||
389 | while ((DWC_DIEPCTL(epnum) & EPENA) && !(DWC_DIEPINT(epnum) & INEPNE)) | ||
390 | { | ||
391 | if (!tmo--) panicf("%s: IN%d failed!", __func__, epnum); | ||
392 | udelay(1); | ||
393 | } | ||
394 | } | ||
395 | } | ||
396 | |||
397 | #ifdef USB_DW_SHARED_FIFO | ||
398 | static void usb_dw_ginak_effective(bool enable) | ||
399 | { | ||
400 | if (enable) | ||
401 | { | ||
402 | if (!(DWC_DCTL & GINSTS)) | ||
403 | DWC_DCTL |= SGINAK; | ||
404 | |||
405 | /* Wait for global IN NAK effective */ | ||
406 | int tmo = 50; | ||
407 | while (~DWC_GINTSTS & GINAKEFF) | ||
408 | { | ||
409 | if (!tmo--) panicf("%s: failed!", __func__); | ||
410 | udelay(1); | ||
411 | } | ||
412 | #ifndef USB_DW_ARCH_SLAVE | ||
413 | /* Wait for any DMA activity to stop. */ | ||
414 | usb_dw_wait_for_ahb_idle(); | ||
415 | #endif | ||
416 | } | ||
417 | else | ||
418 | { | ||
419 | if (DWC_DCTL & GINSTS) | ||
420 | DWC_DCTL |= CGINAK; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | static void usb_dw_nptx_unqueue(int epnum) | ||
425 | { | ||
426 | uint32_t reenable_msk = 0; | ||
427 | |||
428 | usb_dw_ginak_effective(true); | ||
429 | |||
430 | /* Disable EPs */ | ||
431 | for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
432 | { | ||
433 | if (usb_endpoints & ~ep_periodic_msk & (1 << ep)) | ||
434 | { | ||
435 | /* Disable */ | ||
436 | if (~DWC_DIEPCTL(ep) & EPENA) | ||
437 | continue; | ||
438 | DWC_DIEPCTL(ep) |= EPDIS|SNAK; | ||
439 | |||
440 | /* Adjust */ | ||
441 | uint32_t packetsleft = (DWC_DIEPTSIZ(ep) >> 19) & 0x3ff; | ||
442 | if (!packetsleft) continue; | ||
443 | |||
444 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_IN); | ||
445 | uint32_t sentbytes; | ||
446 | uint32_t bytesinfifo = usb_dw_bytes_in_txfifo(ep, &sentbytes); | ||
447 | |||
448 | #ifdef USB_DW_ARCH_SLAVE | ||
449 | dw_ep->addr -= (bytesinfifo + 3) >> 2; | ||
450 | #else | ||
451 | (void) bytesinfifo; | ||
452 | DWC_DIEPDMA(ep) = (uint32_t)(dw_ep->addr) + sentbytes; | ||
453 | #endif | ||
454 | DWC_DIEPTSIZ(ep) = PKTCNT(packetsleft) | (dw_ep->size - sentbytes); | ||
455 | |||
456 | /* Do not re-enable the EP we are going to unqueue */ | ||
457 | if (ep == epnum) | ||
458 | continue; | ||
459 | |||
460 | /* Mark EP to be re-enabled later */ | ||
461 | reenable_msk |= (1 << ep); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /* Flush NPTXFIFO */ | ||
466 | usb_dw_flush_fifo(TXFFLSH, 0); | ||
467 | |||
468 | /* Re-enable EPs */ | ||
469 | for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
470 | if (reenable_msk & (1 << ep)) | ||
471 | DWC_DIEPCTL(ep) |= EPENA|CNAK; | ||
472 | |||
473 | #ifdef USB_DW_ARCH_SLAVE | ||
474 | if (reenable_msk) | ||
475 | DWC_GINTMSK |= NPTXFE; | ||
476 | #endif | ||
477 | |||
478 | usb_dw_ginak_effective(false); | ||
479 | } | ||
480 | #endif /* USB_DW_SHARED_FIFO */ | ||
481 | |||
482 | static void usb_dw_flush_endpoint(int epnum, enum usb_dw_epdir epdir) | ||
483 | { | ||
484 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); | ||
485 | dw_ep->busy = false; | ||
486 | dw_ep->status = -1; | ||
487 | semaphore_release(&dw_ep->complete); | ||
488 | |||
489 | if (DWC_EPCTL(epnum, epdir) & EPENA) | ||
490 | { | ||
491 | if (epdir == USB_DW_EPDIR_IN) | ||
492 | { | ||
493 | /* We are shutting down an endpoint that might still have IN | ||
494 | * packets in the FIFO. Disable the endpoint, wait for things | ||
495 | * to settle, and flush the relevant FIFO. | ||
496 | */ | ||
497 | int dtxfnum = GET_DTXFNUM(epnum); | ||
498 | |||
499 | #ifdef USB_DW_SHARED_FIFO | ||
500 | if (!dtxfnum) | ||
501 | { | ||
502 | usb_dw_nptx_unqueue(epnum); | ||
503 | } | ||
504 | else | ||
505 | #endif | ||
506 | { | ||
507 | /* Wait for IN NAK effective to avoid race conditions | ||
508 | while shutting down the endpoint. */ | ||
509 | usb_dw_set_innak_effective(epnum); | ||
510 | |||
511 | /* Disable the EP we are going to flush */ | ||
512 | usb_dw_disable_ep(epnum, epdir); | ||
513 | |||
514 | /* Flush it all the way down! */ | ||
515 | usb_dw_flush_fifo(TXFFLSH, dtxfnum); | ||
516 | |||
517 | #if !defined(USB_DW_SHARED_FIFO) && defined(USB_DW_ARCH_SLAVE) | ||
518 | DWC_DIEPEMPMSK &= ~(1 << dtxfnum); | ||
519 | #endif | ||
520 | } | ||
521 | } | ||
522 | else | ||
523 | { | ||
524 | /* We are waiting for an OUT packet on this endpoint, which | ||
525 | * might arrive any moment. Assert a global output NAK to | ||
526 | * avoid race conditions while shutting down the endpoint. | ||
527 | * Global output NAK also flushes the Rx FIFO. | ||
528 | */ | ||
529 | usb_dw_gonak_effective(true); | ||
530 | usb_dw_disable_ep(epnum, epdir); | ||
531 | usb_dw_gonak_effective(false); | ||
532 | } | ||
533 | } | ||
534 | |||
535 | /* At this point the endpoint is disabled, SNAK it (in case it is not | ||
536 | * already done), it is needed for Tx shared FIFOs (to not to raise | ||
537 | * unwanted EPMIS interrupts) and recomended for dedicated FIFOs. | ||
538 | */ | ||
539 | DWC_EPCTL(epnum, epdir) |= SNAK; | ||
540 | |||
541 | #ifdef USB_DW_SHARED_FIFO | ||
542 | if (epdir == USB_DW_EPDIR_IN) | ||
543 | { | ||
544 | epmis_msk &= ~(1 << epnum); | ||
545 | if (!epmis_msk) | ||
546 | DWC_DIEPMSK &= ~ITTXFE; | ||
547 | } | ||
548 | #endif | ||
549 | |||
550 | /* Clear all channel interrupts to avoid to process | ||
551 | pending tokens for the flushed EP. */ | ||
552 | DWC_EPINT(epnum, epdir) = DWC_EPINT(epnum, epdir); | ||
553 | } | ||
554 | |||
555 | static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir) | ||
556 | { | ||
557 | uint32_t epctl = 0; | ||
558 | |||
559 | if (epdir == USB_DW_EPDIR_IN) | ||
560 | { | ||
561 | #ifdef USB_DW_SHARED_FIFO | ||
562 | #ifndef USB_DW_ARCH_SLAVE | ||
563 | int next; | ||
564 | for (next = epnum + 1; next < USB_NUM_ENDPOINTS; next++) | ||
565 | if (usb_endpoints & (1 << next)) | ||
566 | break; | ||
567 | epctl = NEXTEP(next % USB_NUM_ENDPOINTS); | ||
568 | #endif | ||
569 | ep_periodic_msk &= ~(1 << epnum); | ||
570 | #endif | ||
571 | ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum)); | ||
572 | } | ||
573 | |||
574 | usb_dw_flush_endpoint(epnum, epdir); | ||
575 | DWC_EPCTL(epnum, epdir) = epctl; | ||
576 | } | ||
577 | |||
578 | static int usb_dw_configure_ep(int epnum, | ||
579 | enum usb_dw_epdir epdir, int type, int maxpktsize) | ||
580 | { | ||
581 | uint32_t epctl = SD0PID|EPTYP(type)|USBAEP|maxpktsize; | ||
582 | |||
583 | if (epdir == USB_DW_EPDIR_IN) | ||
584 | { | ||
585 | /* | ||
586 | * If the hardware has dedicated fifos, we must give each | ||
587 | * IN EP a unique tx-fifo even if it is non-periodic. | ||
588 | */ | ||
589 | #ifdef USB_DW_SHARED_FIFO | ||
590 | #ifndef USB_DW_ARCH_SLAVE | ||
591 | epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf); | ||
592 | #endif | ||
593 | if (type == USB_ENDPOINT_XFER_INT) | ||
594 | #endif | ||
595 | { | ||
596 | int fnum; | ||
597 | for (fnum = 1; fnum <= n_ptxfifos; fnum++) | ||
598 | if (~ptxfifo_usage & (1 << fnum)) | ||
599 | break; | ||
600 | if (fnum > n_ptxfifos) | ||
601 | return -1; /* no available fifos */ | ||
602 | ptxfifo_usage |= (1 << fnum); | ||
603 | epctl |= DTXFNUM(fnum); | ||
604 | #ifdef USB_DW_SHARED_FIFO | ||
605 | ep_periodic_msk |= (1 << epnum); | ||
606 | #endif | ||
607 | } | ||
608 | } | ||
609 | |||
610 | DWC_EPCTL(epnum, epdir) = epctl; | ||
611 | return 0; /* ok */ | ||
612 | } | ||
613 | |||
614 | static void usb_dw_reset_endpoints(void) | ||
615 | { | ||
616 | /* Initial state for all endpoints, setting OUT EPs as not busy | ||
617 | * will discard all pending data (if any) on the flush stage. | ||
618 | */ | ||
619 | for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
620 | { | ||
621 | for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++) | ||
622 | { | ||
623 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, dir); | ||
624 | dw_ep->active = !ep; | ||
625 | dw_ep->busy = false; | ||
626 | dw_ep->status = -1; | ||
627 | semaphore_release(&dw_ep->complete); | ||
628 | } | ||
629 | } | ||
630 | |||
631 | #if CONFIG_CPU == S5L8701 | ||
632 | /* | ||
633 | * Workaround for spurious -EPROTO when receiving bulk data on Nano2G. | ||
634 | * | ||
635 | * The Rx FIFO and Rx queue are currupted by the received (corrupted) | ||
636 | * data, must be flushed, otherwise the core can not set GONAK effective. | ||
637 | */ | ||
638 | usb_dw_flush_fifo(RXFFLSH, 0); | ||
639 | #endif | ||
640 | |||
641 | /* Flush and initialize EPs, includes disabling USBAEP on all EPs | ||
642 | * except EP0 (USB HW core keeps EP0 active on all configurations). | ||
643 | */ | ||
644 | for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
645 | { | ||
646 | if (usb_endpoints & (1 << (ep + 16))) | ||
647 | usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_OUT); | ||
648 | if (usb_endpoints & (1 << ep)) | ||
649 | usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN); | ||
650 | } | ||
651 | |||
652 | ptxfifo_usage = 0; | ||
653 | #ifdef USB_DW_SHARED_FIFO | ||
654 | ep_periodic_msk = 0; | ||
655 | #endif | ||
656 | } | ||
657 | |||
658 | static void usb_dw_start_xfer(int epnum, | ||
659 | enum usb_dw_epdir epdir, const void* buf, int size) | ||
660 | { | ||
661 | if ((uint32_t)buf & ((epdir == USB_DW_EPDIR_IN) ? 3 : CACHEALIGN_SIZE-1)) | ||
662 | logf("%s: %s%d %p unaligned", __func__, dw_dir_str[epdir], epnum, buf); | ||
663 | |||
664 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); | ||
665 | |||
666 | dw_ep->busy = true; | ||
667 | dw_ep->status = -1; | ||
668 | dw_ep->sizeleft = size; | ||
669 | size = MIN(size, usb_dw_maxxfersize(epnum, epdir)); | ||
670 | dw_ep->size = size; | ||
671 | |||
672 | int packets = usb_dw_calc_packets(size, usb_dw_maxpktsize(epnum, epdir)); | ||
673 | uint32_t eptsiz = PKTCNT(packets) | size; | ||
674 | uint32_t nak; | ||
675 | |||
676 | /* Set up data source */ | ||
677 | dw_ep->addr = (uint32_t*)buf; | ||
678 | #ifndef USB_DW_ARCH_SLAVE | ||
679 | DWC_EPDMA(epnum, epdir) = (uint32_t)buf; | ||
680 | #endif | ||
681 | |||
682 | if (epdir == USB_DW_EPDIR_IN) | ||
683 | { | ||
684 | #ifndef USB_DW_ARCH_SLAVE | ||
685 | COMMIT_DCACHE_RANGE(buf, size); | ||
686 | #endif | ||
687 | #ifdef USB_DW_SHARED_FIFO | ||
688 | eptsiz |= MCCNT((ep_periodic_msk >> epnum) & 1); | ||
689 | #endif | ||
690 | nak = CNAK; | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | #ifndef USB_DW_ARCH_SLAVE | ||
695 | DISCARD_DCACHE_RANGE(buf, size); | ||
696 | #endif | ||
697 | eptsiz |= STUPCNT(!epnum); | ||
698 | nak = epnum ? CNAK : SNAK; | ||
699 | } | ||
700 | |||
701 | DWC_EPTSIZ(epnum, epdir) = eptsiz; | ||
702 | |||
703 | /* Enable the endpoint */ | ||
704 | DWC_EPCTL(epnum, epdir) |= EPENA | nak; | ||
705 | |||
706 | #ifdef USB_DW_ARCH_SLAVE | ||
707 | /* Enable interrupts to start pushing data into the FIFO */ | ||
708 | if ((epdir == USB_DW_EPDIR_IN) && size) | ||
709 | #ifdef USB_DW_SHARED_FIFO | ||
710 | DWC_GINTMSK |= ((ep_periodic_msk & (1 << epnum)) ? PTXFE : NPTXFE); | ||
711 | #else | ||
712 | DWC_DIEPEMPMSK |= (1 << GET_DTXFNUM(epnum)); | ||
713 | #endif | ||
714 | #endif | ||
715 | } | ||
716 | |||
717 | static void usb_dw_ep0_wait_setup(void) | ||
718 | { | ||
719 | usb_dw_start_xfer(0, USB_DW_EPDIR_OUT, ep0_buffer.raw, 64); | ||
720 | } | ||
721 | |||
722 | static void usb_dw_handle_setup_received(void) | ||
723 | { | ||
724 | static struct usb_ctrlrequest usb_ctrlsetup; | ||
725 | |||
726 | usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN); | ||
727 | |||
728 | memcpy(&usb_ctrlsetup, ep0_buffer.raw, sizeof(usb_ctrlsetup)); | ||
729 | |||
730 | if (((usb_ctrlsetup.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) | ||
731 | && ((usb_ctrlsetup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) | ||
732 | && (usb_ctrlsetup.bRequest == USB_REQ_SET_ADDRESS)) | ||
733 | usb_dw_set_address(usb_ctrlsetup.wValue); | ||
734 | |||
735 | usb_core_control_request(&usb_ctrlsetup); | ||
736 | } | ||
737 | |||
738 | static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir) | ||
739 | { | ||
740 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); | ||
741 | if (dw_ep->busy) | ||
742 | { | ||
743 | usb_dw_flush_endpoint(epnum, epdir); | ||
744 | usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ? | ||
745 | USB_DIR_OUT : USB_DIR_IN, -1, 0); | ||
746 | } | ||
747 | } | ||
748 | |||
749 | static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir) | ||
750 | { | ||
751 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); | ||
752 | |||
753 | if (!dw_ep->busy) | ||
754 | return; | ||
755 | |||
756 | uint32_t bytesleft = DWC_EPTSIZ(epnum, epdir) & 0x7ffff; | ||
757 | |||
758 | if (!epnum && (epdir == USB_DW_EPDIR_OUT)) /* OUT0 */ | ||
759 | { | ||
760 | int recvbytes = 64 - bytesleft; | ||
761 | dw_ep->sizeleft = dw_ep->req_size - recvbytes; | ||
762 | if (dw_ep->req_addr) | ||
763 | memcpy(dw_ep->req_addr, ep0_buffer.raw, dw_ep->req_size); | ||
764 | } | ||
765 | else | ||
766 | { | ||
767 | dw_ep->sizeleft -= (dw_ep->size - bytesleft); | ||
768 | if (!bytesleft && dw_ep->sizeleft) | ||
769 | { | ||
770 | #ifndef USB_DW_ARCH_SLAVE | ||
771 | dw_ep->addr += (dw_ep->size >> 2); /* words */ | ||
772 | #endif | ||
773 | usb_dw_start_xfer(epnum, epdir, dw_ep->addr, dw_ep->sizeleft); | ||
774 | return; | ||
775 | } | ||
776 | |||
777 | if (epdir == USB_DW_EPDIR_IN) | ||
778 | { | ||
779 | /* SNAK the disabled EP, otherwise IN tokens for this | ||
780 | EP could raise unwanted EPMIS interrupts. Useful for | ||
781 | usbserial when there is no data to send. */ | ||
782 | DWC_DIEPCTL(epnum) |= SNAK; | ||
783 | |||
784 | #ifdef USB_DW_SHARED_FIFO | ||
785 | /* See usb-s5l8701.c */ | ||
786 | if (usb_dw_config.use_ptxfifo_as_plain_buffer) | ||
787 | { | ||
788 | int dtxfnum = GET_DTXFNUM(epnum); | ||
789 | if (dtxfnum) | ||
790 | usb_dw_flush_fifo(TXFFLSH, dtxfnum); | ||
791 | } | ||
792 | #endif | ||
793 | } | ||
794 | } | ||
795 | |||
796 | dw_ep->busy = false; | ||
797 | dw_ep->status = 0; | ||
798 | semaphore_release(&dw_ep->complete); | ||
799 | |||
800 | int transfered = dw_ep->req_size - dw_ep->sizeleft; | ||
801 | usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ? | ||
802 | USB_DIR_OUT : USB_DIR_IN, dw_ep->status, transfered); | ||
803 | } | ||
804 | |||
805 | #ifdef USB_DW_SHARED_FIFO | ||
806 | static int usb_dw_get_epmis(void) | ||
807 | { | ||
808 | unsigned epmis; | ||
809 | uint32_t gnptxsts = DWC_GNPTXSTS; | ||
810 | |||
811 | if (((gnptxsts >> 16) & 0xff) >= hw_nptxqdepth) | ||
812 | return -1; /* empty queue */ | ||
813 | |||
814 | /* Get the EP on the top of the queue, 0 < idx < number of available | ||
815 | IN endpoints */ | ||
816 | int idx = (gnptxsts >> 27) & 0xf; | ||
817 | for (epmis = 0; epmis < USB_NUM_ENDPOINTS; epmis++) | ||
818 | if ((usb_endpoints & (1 << epmis)) && !idx--) | ||
819 | break; | ||
820 | |||
821 | /* The maximum EP mismatch counter is configured, so we verify all NPTX | ||
822 | queue entries, 4 bits per entry, first entry at DTKQNR1[11:8] */ | ||
823 | uint32_t volatile *dtknqr = &DWC_DTKNQR1; | ||
824 | for (int i = 2; i < hw_nptxqdepth + 2; i++) | ||
825 | if (((*(dtknqr+(i>>3)) >> ((i & 0x7)*4)) & 0xf) == epmis) | ||
826 | return -1; | ||
827 | |||
828 | return epmis; | ||
829 | } | ||
830 | |||
831 | static void usb_dw_handle_token_mismatch(void) | ||
832 | { | ||
833 | usb_dw_ginak_effective(true); | ||
834 | int epmis = usb_dw_get_epmis(); | ||
835 | if (epmis >= 0) | ||
836 | { | ||
837 | /* The EP is disabled, unqueued, and reconfigured to re-reenable it | ||
838 | later when a token is received, (or it will be cancelled by | ||
839 | timeout if it was a blocking request). */ | ||
840 | usb_dw_nptx_unqueue(epmis); | ||
841 | |||
842 | epmis_msk |= (1 << epmis); | ||
843 | if (epmis_msk) | ||
844 | DWC_DIEPMSK |= ITTXFE; | ||
845 | |||
846 | /* Be sure the status is clear */ | ||
847 | DWC_DIEPINT(epmis) = ITTXFE; | ||
848 | |||
849 | /* Must disable NAK to allow to get ITTXFE interrupts for this EP */ | ||
850 | DWC_DIEPCTL(epmis) |= CNAK; | ||
851 | } | ||
852 | usb_dw_ginak_effective(false); | ||
853 | } | ||
854 | #endif /* USB_DW_SHARED_FIFO */ | ||
855 | |||
856 | static void usb_dw_irq(void) | ||
857 | { | ||
858 | int ep; | ||
859 | uint32_t daint; | ||
860 | |||
861 | #ifdef USB_DW_ARCH_SLAVE | ||
862 | /* Handle one packet at a time, the IRQ will re-trigger if there's | ||
863 | something left. */ | ||
864 | if (DWC_GINTSTS & RXFLVL) | ||
865 | { | ||
866 | usb_dw_handle_rxfifo(); | ||
867 | } | ||
868 | #endif | ||
869 | |||
870 | #ifdef USB_DW_SHARED_FIFO | ||
871 | if (DWC_GINTSTS & EPMIS) | ||
872 | { | ||
873 | usb_dw_handle_token_mismatch(); | ||
874 | DWC_GINTSTS = EPMIS; | ||
875 | } | ||
876 | |||
877 | #ifdef USB_DW_ARCH_SLAVE | ||
878 | uint32_t gintsts = DWC_GINTSTS & DWC_GINTMSK; | ||
879 | if (gintsts & PTXFE) | ||
880 | { | ||
881 | /* First disable the IRQ, it will be re-enabled later if there | ||
882 | is anything left to be done. */ | ||
883 | DWC_GINTMSK &= ~PTXFE; | ||
884 | /* Check all periodic endpoints for anything to be transmitted */ | ||
885 | for (ep = 1; ep < USB_NUM_ENDPOINTS; ep++) | ||
886 | if (usb_endpoints & ep_periodic_msk & (1 << ep)) | ||
887 | usb_dw_try_push(ep); | ||
888 | } | ||
889 | |||
890 | if (gintsts & NPTXFE) | ||
891 | { | ||
892 | /* First disable the IRQ, it will be re-enabled later if there | ||
893 | is anything left to be done. */ | ||
894 | DWC_GINTMSK &= ~NPTXFE; | ||
895 | /* Check all non-periodic endpoints for anything to be transmitted */ | ||
896 | for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
897 | if (usb_endpoints & ~ep_periodic_msk & (1 << ep)) | ||
898 | usb_dw_try_push(ep); | ||
899 | } | ||
900 | #endif /* USB_DW_ARCH_SLAVE */ | ||
901 | #endif /* USB_DW_SHARED_FIFO */ | ||
902 | |||
903 | daint = DWC_DAINT; | ||
904 | |||
905 | /* IN */ | ||
906 | for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
907 | { | ||
908 | if (daint & (1 << ep)) | ||
909 | { | ||
910 | uint32_t epints = DWC_DIEPINT(ep); | ||
911 | |||
912 | if (epints & TOC) | ||
913 | { | ||
914 | usb_dw_abort_endpoint(ep, USB_DW_EPDIR_IN); | ||
915 | } | ||
916 | |||
917 | #ifdef USB_DW_SHARED_FIFO | ||
918 | if (epints & ITTXFE) | ||
919 | { | ||
920 | if (epmis_msk & (1 << ep)) | ||
921 | { | ||
922 | DWC_DIEPCTL(ep) |= EPENA; | ||
923 | epmis_msk &= ~(1 << ep); | ||
924 | if (!epmis_msk) | ||
925 | DWC_DIEPMSK &= ~ITTXFE; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | #elif defined(USB_DW_ARCH_SLAVE) | ||
930 | if (epints & TXFE) | ||
931 | { | ||
932 | usb_dw_handle_dtxfifo(ep); | ||
933 | } | ||
934 | #endif | ||
935 | |||
936 | /* Clear XFRC here, if this is a 'multi-transfer' request then | ||
937 | a new transfer is going to be launched, this ensures it will | ||
938 | not miss a single interrupt. */ | ||
939 | DWC_DIEPINT(ep) = epints; | ||
940 | |||
941 | if (epints & XFRC) | ||
942 | { | ||
943 | usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_IN); | ||
944 | } | ||
945 | } | ||
946 | } | ||
947 | |||
948 | /* OUT */ | ||
949 | for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
950 | { | ||
951 | if (daint & (1 << (ep + 16))) | ||
952 | { | ||
953 | uint32_t epints = DWC_DOEPINT(ep); | ||
954 | |||
955 | if (!ep) | ||
956 | { | ||
957 | if (epints & STUP) | ||
958 | { | ||
959 | usb_dw_handle_setup_received(); | ||
960 | } | ||
961 | else if (epints & XFRC) | ||
962 | { | ||
963 | usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT); | ||
964 | } | ||
965 | usb_dw_ep0_wait_setup(); | ||
966 | /* Clear interrupt after the current EP0 packet is handled */ | ||
967 | DWC_DOEPINT(0) = epints; | ||
968 | } | ||
969 | else | ||
970 | { | ||
971 | DWC_DOEPINT(ep) = epints; | ||
972 | if (epints & XFRC) | ||
973 | { | ||
974 | usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_OUT); | ||
975 | } | ||
976 | } | ||
977 | } | ||
978 | } | ||
979 | |||
980 | if (DWC_GINTSTS & USBRST) | ||
981 | { | ||
982 | DWC_GINTSTS = USBRST; | ||
983 | usb_dw_set_address(0); | ||
984 | usb_dw_reset_endpoints(); | ||
985 | usb_dw_ep0_wait_setup(); | ||
986 | usb_core_bus_reset(); | ||
987 | } | ||
988 | |||
989 | if (DWC_GINTSTS & ENUMDNE) | ||
990 | { | ||
991 | DWC_GINTSTS = ENUMDNE; | ||
992 | /* Nothing to do? */ | ||
993 | } | ||
994 | } | ||
995 | |||
996 | static void usb_dw_check_hw(void) | ||
997 | { | ||
998 | uint32_t ghwcfg2 = DWC_GHWCFG2; | ||
999 | uint32_t ghwcfg3 = DWC_GHWCFG3; | ||
1000 | uint32_t ghwcfg4 = DWC_GHWCFG4; | ||
1001 | const struct usb_dw_config *c = &usb_dw_config; | ||
1002 | int hw_numeps; | ||
1003 | int hw_maxtxfifos; /* periodic or dedicated */ | ||
1004 | char *err; | ||
1005 | |||
1006 | hw_numeps = ((ghwcfg2 >> 10) & 0xf) + 1; | ||
1007 | |||
1008 | if (hw_numeps < USB_NUM_ENDPOINTS) | ||
1009 | { | ||
1010 | err = "USB_NUM_ENDPOINTS too big"; | ||
1011 | goto panic; | ||
1012 | } | ||
1013 | /* HWCFG registers are not checked to detect the PHY, if an option | ||
1014 | is not supported then the related bits should be Read-Only. */ | ||
1015 | DWC_GUSBCFG = c->phytype; | ||
1016 | if (DWC_GUSBCFG != c->phytype) | ||
1017 | { | ||
1018 | err = "PHY type not supported"; | ||
1019 | goto panic; | ||
1020 | } | ||
1021 | #ifndef USB_DW_ARCH_SLAVE | ||
1022 | if (((ghwcfg2 >> 3) & 3) != 2) | ||
1023 | { | ||
1024 | err = "internal DMA not supported"; | ||
1025 | goto panic; | ||
1026 | } | ||
1027 | #endif | ||
1028 | #ifdef USB_DW_SHARED_FIFO | ||
1029 | if ((ghwcfg4 >> 25) & 1) | ||
1030 | { | ||
1031 | err = "shared TxFIFO not supported"; | ||
1032 | goto panic; | ||
1033 | } | ||
1034 | hw_maxtxfifos = ghwcfg4 & 0xf; | ||
1035 | hw_nptxqdepth = (1 << (((ghwcfg2 >> 22) & 3) + 1)); | ||
1036 | #else | ||
1037 | if (!((ghwcfg4 >> 25) & 1)) | ||
1038 | { | ||
1039 | err = "dedicated TxFIFO not supported"; | ||
1040 | goto panic; | ||
1041 | } | ||
1042 | hw_maxtxfifos = (ghwcfg4 >> 26) & 0xf; | ||
1043 | #endif | ||
1044 | hw_maxbytes = (1 << (((ghwcfg3 >> 0) & 0xf) + 11)) - 1; | ||
1045 | hw_maxpackets = (1 << (((ghwcfg3 >> 4) & 0x7) + 4)) - 1; | ||
1046 | uint16_t hw_fifomem = ghwcfg3 >> 16; | ||
1047 | |||
1048 | /* Configure FIFOs, sizes are 32-bit words, we will need at least | ||
1049 | one periodic or dedicated Tx FIFO (really the periodic Tx FIFO | ||
1050 | is not needed if !USB_ENABLE_HID). */ | ||
1051 | if (c->rx_fifosz + c->nptx_fifosz + c->ptx_fifosz > hw_fifomem) | ||
1052 | { | ||
1053 | err = "insufficient FIFO memory"; | ||
1054 | goto panic; | ||
1055 | } | ||
1056 | n_ptxfifos = (hw_fifomem - c->rx_fifosz - c->nptx_fifosz) / c->ptx_fifosz; | ||
1057 | if (n_ptxfifos > hw_maxtxfifos) n_ptxfifos = hw_maxtxfifos; | ||
1058 | |||
1059 | logf("%s():", __func__); | ||
1060 | logf(" HW version: %4lx, num EPs: %d", DWC_GSNPSID & 0xffff, hw_numeps); | ||
1061 | logf(" FIFO mem=%d rx=%d nptx=%d ptx=%dx%d", hw_fifomem, | ||
1062 | c->rx_fifosz, c->nptx_fifosz, n_ptxfifos, c->ptx_fifosz); | ||
1063 | |||
1064 | return; | ||
1065 | |||
1066 | panic: | ||
1067 | panicf("%s: %s", __func__, err); | ||
1068 | } | ||
1069 | |||
1070 | static void usb_dw_init(void) | ||
1071 | { | ||
1072 | static bool initialized = false; | ||
1073 | const struct usb_dw_config *c = &usb_dw_config; | ||
1074 | |||
1075 | if (!initialized) | ||
1076 | { | ||
1077 | for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) | ||
1078 | for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++) | ||
1079 | semaphore_init(&usb_dw_get_ep(ep, dir)->complete, 1, 0); | ||
1080 | initialized = true; | ||
1081 | } | ||
1082 | |||
1083 | /* Disable IRQ during setup */ | ||
1084 | usb_dw_target_disable_irq(); | ||
1085 | |||
1086 | /* Enable OTG clocks */ | ||
1087 | usb_dw_target_enable_clocks(); | ||
1088 | |||
1089 | /* Enable PHY clocks */ | ||
1090 | DWC_PCGCCTL = 0; | ||
1091 | |||
1092 | usb_dw_check_hw(); | ||
1093 | |||
1094 | /* Configure PHY type (must be done before reset) */ | ||
1095 | #ifndef USB_DW_TURNAROUND | ||
1096 | /* | ||
1097 | * Turnaround time (in PHY clocks) = 4*AHB clocks + 1*PHY clock, | ||
1098 | * worst cases are: | ||
1099 | * 16-bit UTMI+: PHY=30MHz, AHB=30Mhz -> 5 | ||
1100 | * 8-bit UTMI+: PHY=60MHz, AHB=30MHz -> 9 | ||
1101 | */ | ||
1102 | int USB_DW_TURNAROUND = (c->phytype == DWC_PHYTYPE_UTMI_16) ? 5 : 9; | ||
1103 | #endif | ||
1104 | uint32_t gusbcfg = c->phytype|TRDT(USB_DW_TURNAROUND)|USB_DW_TOUTCAL; | ||
1105 | DWC_GUSBCFG = gusbcfg; | ||
1106 | |||
1107 | /* Reset the whole USB core */ | ||
1108 | udelay(100); | ||
1109 | usb_dw_wait_for_ahb_idle(); | ||
1110 | DWC_GRSTCTL = CSRST; | ||
1111 | while (DWC_GRSTCTL & CSRST); | ||
1112 | usb_dw_wait_for_ahb_idle(); | ||
1113 | |||
1114 | /* Configure FIFOs */ | ||
1115 | DWC_GRXFSIZ = c->rx_fifosz; | ||
1116 | #ifdef USB_DW_SHARED_FIFO | ||
1117 | DWC_GNPTXFSIZ = (c->nptx_fifosz << 16) | c->rx_fifosz; | ||
1118 | #else | ||
1119 | DWC_TX0FSIZ = (c->nptx_fifosz << 16) | c->rx_fifosz; | ||
1120 | #endif | ||
1121 | for (int i = 0; i < n_ptxfifos; i++) | ||
1122 | DWC_DIEPTXF(i) = (c->ptx_fifosz << 16) | | ||
1123 | (c->nptx_fifosz + c->rx_fifosz + c->ptx_fifosz*i); | ||
1124 | /* | ||
1125 | * According to p428 of the design guide, we need to ensure that | ||
1126 | * fifos are flushed before continuing. | ||
1127 | */ | ||
1128 | usb_dw_flush_fifo(TXFFLSH|RXFFLSH, 0x10); | ||
1129 | |||
1130 | /* Configure the core */ | ||
1131 | DWC_GUSBCFG = gusbcfg; | ||
1132 | |||
1133 | uint32_t gahbcfg = GINT; | ||
1134 | #ifdef USB_DW_ARCH_SLAVE | ||
1135 | #ifdef USB_DW_SHARED_FIFO | ||
1136 | if (c->use_ptxfifo_as_plain_buffer) | ||
1137 | gahbcfg |= PTXFELVL; | ||
1138 | #endif | ||
1139 | if (c->disable_double_buffering) | ||
1140 | gahbcfg |= TXFELVL; | ||
1141 | #else | ||
1142 | gahbcfg |= HBSTLEN(c->ahb_burst_len)|DMAEN; | ||
1143 | #endif | ||
1144 | DWC_GAHBCFG = gahbcfg; | ||
1145 | |||
1146 | DWC_DCFG = NZLSOHSK; | ||
1147 | #ifdef USB_DW_SHARED_FIFO | ||
1148 | /* Set EP mismatch counter to the maximum */ | ||
1149 | DWC_DCFG |= EPMISCNT(0x1f); | ||
1150 | #endif | ||
1151 | |||
1152 | #if !defined(USB_DW_ARCH_SLAVE) && !defined(USB_DW_SHARED_FIFO) | ||
1153 | if (c->ahb_threshold) | ||
1154 | DWC_DTHRCTL = ARPEN|RXTHRLEN(c->ahb_threshold)|RXTHREN; | ||
1155 | #endif | ||
1156 | |||
1157 | /* Set up interrupts */ | ||
1158 | DWC_DOEPMSK = STUP|XFRC; | ||
1159 | DWC_DIEPMSK = TOC|XFRC; | ||
1160 | |||
1161 | /* Unmask all available endpoints */ | ||
1162 | DWC_DAINTMSK = 0xffffffff; | ||
1163 | usb_endpoints = DWC_DAINTMSK; | ||
1164 | |||
1165 | uint32_t gintmsk = USBRST|ENUMDNE|IEPINT|OEPINT; | ||
1166 | #ifdef USB_DW_ARCH_SLAVE | ||
1167 | gintmsk |= RXFLVL; | ||
1168 | #endif | ||
1169 | #ifdef USB_DW_SHARED_FIFO | ||
1170 | gintmsk |= EPMIS; | ||
1171 | #endif | ||
1172 | DWC_GINTMSK = gintmsk; | ||
1173 | |||
1174 | usb_dw_reset_endpoints(); | ||
1175 | |||
1176 | /* Soft disconnect */ | ||
1177 | DWC_DCTL = SDIS; | ||
1178 | |||
1179 | usb_dw_target_clear_irq(); | ||
1180 | usb_dw_target_enable_irq(); | ||
1181 | |||
1182 | /* Soft reconnect */ | ||
1183 | udelay(3000); | ||
1184 | DWC_DCTL &= ~SDIS; | ||
1185 | } | ||
1186 | |||
1187 | static void usb_dw_exit(void) | ||
1188 | { | ||
1189 | /* Soft disconnect */ | ||
1190 | DWC_DCTL = SDIS; | ||
1191 | udelay(10); | ||
1192 | |||
1193 | DWC_PCGCCTL = 1; /* Stop Phy clock */ | ||
1194 | |||
1195 | /* Disable IRQs */ | ||
1196 | usb_dw_target_disable_irq(); | ||
1197 | |||
1198 | /* Disable clocks */ | ||
1199 | usb_dw_target_disable_clocks(); | ||
1200 | } | ||
1201 | |||
1202 | |||
1203 | /* | ||
1204 | * API functions | ||
1205 | */ | ||
1206 | |||
1207 | /* Cancel transfers on configured EPs */ | ||
1208 | void usb_drv_cancel_all_transfers() | ||
1209 | { | ||
1210 | usb_dw_target_disable_irq(); | ||
1211 | for (int ep = 1; ep < USB_NUM_ENDPOINTS; ep++) | ||
1212 | for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++) | ||
1213 | if (usb_endpoints & (1 << (ep + USB_DW_DIR_OFF(dir)))) | ||
1214 | if (usb_dw_get_ep(ep, dir)->active) | ||
1215 | { | ||
1216 | //usb_dw_flush_endpoint(ep, dir); | ||
1217 | usb_dw_abort_endpoint(ep, dir); | ||
1218 | DWC_EPCTL(ep, dir) |= SD0PID; | ||
1219 | } | ||
1220 | usb_dw_target_enable_irq(); | ||
1221 | } | ||
1222 | |||
1223 | bool usb_drv_stalled(int endpoint, bool in) | ||
1224 | { | ||
1225 | return usb_dw_get_stall(EP_NUM(endpoint), | ||
1226 | in ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT); | ||
1227 | } | ||
1228 | |||
1229 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
1230 | { | ||
1231 | usb_dw_target_disable_irq(); | ||
1232 | usb_dw_set_stall(EP_NUM(endpoint), | ||
1233 | in ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT, stall); | ||
1234 | usb_dw_target_enable_irq(); | ||
1235 | } | ||
1236 | |||
1237 | void usb_drv_set_address(int address) | ||
1238 | { | ||
1239 | #if 1 | ||
1240 | /* Ignored intentionally, because the controller requires us to set the | ||
1241 | new address before sending the response for some reason. So we'll | ||
1242 | already set it when the control request arrives, before passing that | ||
1243 | into the USB core, which will then call this dummy function. */ | ||
1244 | (void)address; | ||
1245 | #else | ||
1246 | usb_dw_target_disable_irq(); | ||
1247 | usb_dw_set_address(address); | ||
1248 | usb_dw_target_enable_irq(); | ||
1249 | #endif | ||
1250 | } | ||
1251 | |||
1252 | int usb_drv_port_speed(void) | ||
1253 | { | ||
1254 | return ((DWC_DSTS & 0x6) == 0); | ||
1255 | } | ||
1256 | |||
1257 | void usb_drv_set_test_mode(int mode) | ||
1258 | { | ||
1259 | (void)mode; | ||
1260 | /* Ignore this for now */ | ||
1261 | } | ||
1262 | |||
1263 | void usb_attach(void) | ||
1264 | { | ||
1265 | } | ||
1266 | |||
1267 | void usb_drv_init(void) | ||
1268 | { | ||
1269 | usb_dw_init(); | ||
1270 | } | ||
1271 | |||
1272 | void usb_drv_exit(void) | ||
1273 | { | ||
1274 | usb_dw_exit(); | ||
1275 | } | ||
1276 | |||
1277 | void INT_USB_FUNC(void) | ||
1278 | { | ||
1279 | usb_dw_irq(); | ||
1280 | } | ||
1281 | |||
1282 | int usb_drv_request_endpoint(int type, int dir) | ||
1283 | { | ||
1284 | int request_ep = -1; | ||
1285 | enum usb_dw_epdir epdir = (EP_DIR(dir) == DIR_IN) ? | ||
1286 | USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; | ||
1287 | |||
1288 | usb_dw_target_disable_irq(); | ||
1289 | for (int ep = 1; ep < USB_NUM_ENDPOINTS; ep++) | ||
1290 | { | ||
1291 | if (usb_endpoints & (1 << (ep + USB_DW_DIR_OFF(epdir)))) | ||
1292 | { | ||
1293 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, epdir); | ||
1294 | if (!dw_ep->active) | ||
1295 | { | ||
1296 | if (usb_dw_configure_ep(ep, epdir, type, | ||
1297 | usb_drv_port_speed() ? 512 : 64) >= 0) | ||
1298 | { | ||
1299 | dw_ep->active = true; | ||
1300 | request_ep = ep | dir; | ||
1301 | } | ||
1302 | break; | ||
1303 | } | ||
1304 | } | ||
1305 | } | ||
1306 | usb_dw_target_enable_irq(); | ||
1307 | return request_ep; | ||
1308 | } | ||
1309 | |||
1310 | void usb_drv_release_endpoint(int endpoint) | ||
1311 | { | ||
1312 | int epnum = EP_NUM(endpoint); | ||
1313 | if (!epnum) return; | ||
1314 | enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? | ||
1315 | USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; | ||
1316 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); | ||
1317 | |||
1318 | usb_dw_target_disable_irq(); | ||
1319 | if (dw_ep->active) | ||
1320 | { | ||
1321 | usb_dw_unconfigure_ep(epnum, epdir); | ||
1322 | dw_ep->active = false; | ||
1323 | } | ||
1324 | usb_dw_target_enable_irq(); | ||
1325 | } | ||
1326 | |||
1327 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
1328 | { | ||
1329 | int epnum = EP_NUM(endpoint); | ||
1330 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_OUT); | ||
1331 | |||
1332 | usb_dw_target_disable_irq(); | ||
1333 | if (dw_ep->active) | ||
1334 | { | ||
1335 | dw_ep->req_addr = ptr; | ||
1336 | dw_ep->req_size = length; | ||
1337 | /* OUT0 is always launched waiting for SETUP packet, | ||
1338 | it is CNAKed to receive app data */ | ||
1339 | if (epnum == 0) | ||
1340 | DWC_DOEPCTL(0) |= CNAK; | ||
1341 | else | ||
1342 | usb_dw_start_xfer(epnum, USB_DW_EPDIR_OUT, ptr, length); | ||
1343 | } | ||
1344 | usb_dw_target_enable_irq(); | ||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) | ||
1349 | { | ||
1350 | int epnum = EP_NUM(endpoint); | ||
1351 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN); | ||
1352 | |||
1353 | usb_dw_target_disable_irq(); | ||
1354 | if (dw_ep->active) | ||
1355 | { | ||
1356 | dw_ep->req_addr = ptr; | ||
1357 | dw_ep->req_size = length; | ||
1358 | usb_dw_start_xfer(epnum, USB_DW_EPDIR_IN, ptr, length); | ||
1359 | } | ||
1360 | usb_dw_target_enable_irq(); | ||
1361 | return 0; | ||
1362 | } | ||
1363 | |||
1364 | int usb_drv_send(int endpoint, void *ptr, int length) | ||
1365 | { | ||
1366 | int epnum = EP_NUM(endpoint); | ||
1367 | struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN); | ||
1368 | |||
1369 | semaphore_wait(&dw_ep->complete, 0); | ||
1370 | |||
1371 | usb_drv_send_nonblocking(endpoint, ptr, length); | ||
1372 | |||
1373 | if (semaphore_wait(&dw_ep->complete, HZ) == OBJ_WAIT_TIMEDOUT) | ||
1374 | { | ||
1375 | usb_dw_target_disable_irq(); | ||
1376 | usb_dw_abort_endpoint(epnum, USB_DW_EPDIR_IN); | ||
1377 | usb_dw_target_enable_irq(); | ||
1378 | } | ||
1379 | |||
1380 | return dw_ep->status; | ||
1381 | } | ||