diff options
Diffstat (limited to 'firmware/target/mips/ingenic_jz47xx/usb-jz4760.c')
-rw-r--r-- | firmware/target/mips/ingenic_jz47xx/usb-jz4760.c | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c new file mode 100644 index 0000000000..bc2158fb6f --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c | |||
@@ -0,0 +1,870 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | /*#define LOGF_ENABLE*/ | ||
24 | #include "logf.h" | ||
25 | #include "system.h" | ||
26 | #include "usb_ch9.h" | ||
27 | #include "usb_drv.h" | ||
28 | #include "usb_core.h" | ||
29 | #include "cpu.h" | ||
30 | #include "thread.h" | ||
31 | |||
32 | #define PIN_USB_DET (32*4+19) | ||
33 | #define IRQ_USB_DET GPIO_IRQ(PIN_USB_DET) | ||
34 | #define GPIO_USB_DET GPIO147 | ||
35 | |||
36 | #define PIN_USB_DRVVBUS (32*4+10) | ||
37 | #define PIN_USB_OTG_ID (32*3+7) | ||
38 | |||
39 | #define EP_BUF_LEFT(ep) ((ep)->length - (ep)->sent) | ||
40 | #define EP_PTR(ep) ((void*)((unsigned int)(ep)->buf + (ep)->sent)) | ||
41 | #define EP_NUMBER(ep) (((int)(ep) - (int)&endpoints[0])/sizeof(struct usb_endpoint)) | ||
42 | #define EP_NUMBER2(ep) (EP_NUMBER((ep))/2) | ||
43 | #define TOTAL_EP() (sizeof(endpoints)/sizeof(struct usb_endpoint)) | ||
44 | #define EP_IS_IN(ep) (EP_NUMBER((ep))%2) | ||
45 | |||
46 | enum ep_type | ||
47 | { | ||
48 | ep_control, | ||
49 | ep_bulk, | ||
50 | ep_interrupt, | ||
51 | ep_isochronous | ||
52 | }; | ||
53 | |||
54 | struct usb_endpoint | ||
55 | { | ||
56 | void *buf; | ||
57 | size_t length; | ||
58 | union | ||
59 | { | ||
60 | size_t sent; | ||
61 | size_t received; | ||
62 | }; | ||
63 | bool busy; | ||
64 | |||
65 | const enum ep_type type; | ||
66 | const bool use_dma; | ||
67 | |||
68 | const long fifo_addr; | ||
69 | unsigned short fifo_size; | ||
70 | |||
71 | bool wait; | ||
72 | struct semaphore complete; | ||
73 | }; | ||
74 | |||
75 | #define EP_INIT(_type, _fifo_addr, _fifo_size, _buf, _use_dma) \ | ||
76 | { .type = (_type), .fifo_addr = (_fifo_addr), .fifo_size = (_fifo_size), \ | ||
77 | .buf = (_buf), .use_dma = (_use_dma), .length = 0, .busy = false, .wait = false } | ||
78 | |||
79 | static unsigned char ep0_rx_buf[64]; | ||
80 | static struct usb_endpoint endpoints[] = | ||
81 | { | ||
82 | EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL, false), | ||
83 | EP_INIT(ep_control, USB_FIFO_EP(0), 64, &ep0_rx_buf, false), | ||
84 | EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), | ||
85 | EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), | ||
86 | EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), | ||
87 | EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), | ||
88 | }; | ||
89 | |||
90 | static inline void select_endpoint(int ep) | ||
91 | { | ||
92 | REG_USB_INDEX = ep; | ||
93 | } | ||
94 | |||
95 | static void readFIFO(struct usb_endpoint *ep, unsigned int size) | ||
96 | { | ||
97 | logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); | ||
98 | |||
99 | register unsigned char *ptr = (unsigned char*)EP_PTR(ep); | ||
100 | register unsigned int *ptr32 = (unsigned int*)ptr; | ||
101 | register unsigned int s = size >> 2; | ||
102 | register unsigned int x; | ||
103 | |||
104 | if(size > 0) | ||
105 | { | ||
106 | if( ((unsigned int)ptr & 3) == 0 ) | ||
107 | { | ||
108 | while(s--) | ||
109 | *ptr32++ = REG32(ep->fifo_addr); | ||
110 | |||
111 | ptr = (unsigned char*)ptr32; | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | while(s--) | ||
116 | { | ||
117 | x = REG32(ep->fifo_addr); | ||
118 | *ptr++ = x & 0xFF; x >>= 8; | ||
119 | *ptr++ = x & 0xFF; x >>= 8; | ||
120 | *ptr++ = x & 0xFF; x >>= 8; | ||
121 | *ptr++ = x; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | s = size & 3; | ||
126 | while(s--) | ||
127 | *ptr++ = REG8(ep->fifo_addr); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | static void writeFIFO(struct usb_endpoint *ep, size_t size) | ||
132 | { | ||
133 | logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); | ||
134 | |||
135 | register unsigned int *d32 = (unsigned int *)EP_PTR(ep); | ||
136 | register size_t s = size >> 2; | ||
137 | |||
138 | if(size > 0) | ||
139 | { | ||
140 | while (s--) | ||
141 | REG32(ep->fifo_addr) = *d32++; | ||
142 | |||
143 | if( (s = size & 3) ) | ||
144 | { | ||
145 | register unsigned char *d8 = (unsigned char *)d32; | ||
146 | while (s--) | ||
147 | REG8(ep->fifo_addr) = *d8++; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void flushFIFO(struct usb_endpoint *ep) | ||
153 | { | ||
154 | logf("%s(%d)", __func__, EP_NUMBER(ep)); | ||
155 | |||
156 | switch (ep->type) | ||
157 | { | ||
158 | case ep_control: | ||
159 | break; | ||
160 | |||
161 | case ep_bulk: | ||
162 | case ep_interrupt: | ||
163 | case ep_isochronous: | ||
164 | if(EP_IS_IN(ep)) | ||
165 | REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT); | ||
166 | else | ||
167 | REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT); | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static inline void ep_transfer_completed(struct usb_endpoint* ep) | ||
173 | { | ||
174 | ep->sent = 0; | ||
175 | ep->length = 0; | ||
176 | ep->buf = NULL; | ||
177 | ep->busy = false; | ||
178 | if(ep->wait) | ||
179 | semaphore_release(&ep->complete); | ||
180 | } | ||
181 | |||
182 | static void EP0_send(void) | ||
183 | { | ||
184 | struct usb_endpoint* ep = &endpoints[0]; | ||
185 | unsigned int length; | ||
186 | unsigned char csr0; | ||
187 | |||
188 | select_endpoint(0); | ||
189 | csr0 = REG_USB_CSR0; | ||
190 | |||
191 | if(ep->sent == 0) | ||
192 | length = MIN(ep->length, ep->fifo_size); | ||
193 | else | ||
194 | length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); | ||
195 | |||
196 | writeFIFO(ep, length); | ||
197 | ep->sent += length; | ||
198 | |||
199 | if(ep->sent >= ep->length) | ||
200 | { | ||
201 | REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */ | ||
202 | usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent); | ||
203 | ep_transfer_completed(ep); | ||
204 | } | ||
205 | else | ||
206 | REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY); | ||
207 | } | ||
208 | |||
209 | static void EP0_handler(void) | ||
210 | { | ||
211 | logf("%s()", __func__); | ||
212 | |||
213 | unsigned char csr0; | ||
214 | struct usb_endpoint *ep_send = &endpoints[0]; | ||
215 | struct usb_endpoint *ep_recv = &endpoints[1]; | ||
216 | |||
217 | /* Read CSR0 */ | ||
218 | select_endpoint(0); | ||
219 | csr0 = REG_USB_CSR0; | ||
220 | |||
221 | /* Check for SentStall: | ||
222 | This bit is set when a STALL handshake is transmitted. The CPU should clear this bit. | ||
223 | */ | ||
224 | if(csr0 & USB_CSR0_SENTSTALL) | ||
225 | { | ||
226 | REG_USB_CSR0 = csr0 & ~USB_CSR0_SENTSTALL; | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | /* Check for SetupEnd: | ||
231 | This bit will be set when a control transaction ends before the DataEnd bit has been set. | ||
232 | An interrupt will be generated and the FIFO flushed at this time. | ||
233 | The bit is cleared by the CPU writing a 1 to the ServicedSetupEnd bit. | ||
234 | */ | ||
235 | if(csr0 & USB_CSR0_SETUPEND) | ||
236 | { | ||
237 | REG_USB_CSR0 = csr0 | USB_CSR0_SVDSETUPEND; | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | /* Call relevant routines for endpoint 0 state */ | ||
242 | if(ep_send->busy) | ||
243 | EP0_send(); | ||
244 | else if(csr0 & USB_CSR0_OUTPKTRDY) /* There is a packet in the fifo */ | ||
245 | { | ||
246 | readFIFO(ep_recv, REG_USB_COUNT0); | ||
247 | REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */ | ||
248 | usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | static void EPIN_handler(unsigned int endpoint) | ||
253 | { | ||
254 | struct usb_endpoint* ep = &endpoints[endpoint*2]; | ||
255 | unsigned int length, csr; | ||
256 | |||
257 | select_endpoint(endpoint); | ||
258 | csr = REG_USB_INCSR; | ||
259 | logf("%s(%d): 0x%x", __func__, endpoint, csr); | ||
260 | |||
261 | if(!ep->busy) | ||
262 | { | ||
263 | logf("Entered EPIN handler without work!"); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | if(csr & USB_INCSR_SENTSTALL) | ||
268 | { | ||
269 | REG_USB_INCSR = csr & ~USB_INCSR_SENTSTALL; | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | if(ep->use_dma) | ||
274 | return; | ||
275 | |||
276 | if(csr & USB_INCSR_FFNOTEMPT) | ||
277 | { | ||
278 | logf("FIFO is not empty! 0x%x", csr); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | logf("EP%d: %d -> %d", endpoint, ep->sent, ep->length); | ||
283 | |||
284 | if(ep->sent == 0) | ||
285 | length = MIN(ep->length, ep->fifo_size); | ||
286 | else | ||
287 | length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); | ||
288 | |||
289 | writeFIFO(ep, length); | ||
290 | REG_USB_INCSR = csr | USB_INCSR_INPKTRDY; | ||
291 | ep->sent += length; | ||
292 | |||
293 | if(ep->sent >= ep->length) | ||
294 | { | ||
295 | usb_core_transfer_complete(endpoint, USB_DIR_IN, 0, ep->sent); | ||
296 | ep_transfer_completed(ep); | ||
297 | logf("sent complete"); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | static void EPOUT_handler(unsigned int endpoint) | ||
302 | { | ||
303 | struct usb_endpoint* ep = &endpoints[endpoint*2+1]; | ||
304 | unsigned int size, csr; | ||
305 | |||
306 | if(!ep->busy) | ||
307 | { | ||
308 | logf("Entered EPOUT handler without work!"); | ||
309 | return; | ||
310 | } | ||
311 | |||
312 | select_endpoint(endpoint); | ||
313 | while((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) | ||
314 | { | ||
315 | logf("%s(%d): 0x%x", __func__, endpoint, csr); | ||
316 | if(csr & USB_OUTCSR_SENTSTALL) | ||
317 | { | ||
318 | logf("stall sent, flushing fifo.."); | ||
319 | flushFIFO(ep); | ||
320 | REG_USB_OUTCSR = csr & ~USB_OUTCSR_SENTSTALL; | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | if(ep->use_dma) | ||
325 | return; | ||
326 | |||
327 | if(csr & USB_OUTCSR_OUTPKTRDY) /* There is a packet in the fifo */ | ||
328 | { | ||
329 | size = REG_USB_OUTCOUNT; | ||
330 | |||
331 | readFIFO(ep, size); | ||
332 | ep->received += size; | ||
333 | |||
334 | /*if(csr & USB_OUTCSR_FFFULL) | ||
335 | csr &= ~USB_OUTCSR_FFFULL;*/ | ||
336 | |||
337 | REG_USB_OUTCSR = csr & ~USB_OUTCSR_OUTPKTRDY; | ||
338 | |||
339 | logf("received: %d max length: %d", ep->received, ep->length); | ||
340 | |||
341 | if(size < ep->fifo_size || ep->received >= ep->length) | ||
342 | { | ||
343 | usb_core_transfer_complete(endpoint, USB_DIR_OUT, 0, ep->received); | ||
344 | ep_transfer_completed(ep); | ||
345 | logf("receive transfer_complete"); | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | static void EPDMA_handler(int number) | ||
352 | { | ||
353 | int endpoint = -1; | ||
354 | unsigned int size = 0; | ||
355 | |||
356 | if(number == USB_INTR_DMA_BULKIN) | ||
357 | endpoint = (REG_USB_CNTL(0) >> 4) & 0xF; | ||
358 | else if(number == USB_INTR_DMA_BULKOUT) | ||
359 | endpoint = (REG_USB_CNTL(1) >> 4) & 0xF; | ||
360 | |||
361 | struct usb_endpoint* ep = &endpoints[endpoint]; | ||
362 | logf("DMA_BULK%d %d", number, endpoint); | ||
363 | |||
364 | if(number == USB_INTR_DMA_BULKIN) | ||
365 | size = (unsigned int)ep->buf - REG_USB_ADDR(0); | ||
366 | else if(number == USB_INTR_DMA_BULKOUT) | ||
367 | size = (unsigned int)ep->buf - REG_USB_ADDR(1); | ||
368 | |||
369 | if(number == USB_INTR_DMA_BULKOUT) | ||
370 | { | ||
371 | /* Disable DMA */ | ||
372 | REG_USB_CNTL(1) = 0; | ||
373 | |||
374 | __dcache_invalidate_all(); | ||
375 | |||
376 | select_endpoint(endpoint); | ||
377 | /* Read out last packet manually */ | ||
378 | unsigned int lpack_size = REG_USB_OUTCOUNT; | ||
379 | if(lpack_size > 0) | ||
380 | { | ||
381 | ep->buf += ep->length - lpack_size; | ||
382 | readFIFO(ep, lpack_size); | ||
383 | REG_USB_OUTCSR &= ~USB_OUTCSR_OUTPKTRDY; | ||
384 | } | ||
385 | } | ||
386 | else if(number == USB_INTR_DMA_BULKIN && size % ep->fifo_size) | ||
387 | { | ||
388 | /* If the last packet is less than MAXP, set INPKTRDY manually */ | ||
389 | REG_USB_INCSR |= USB_INCSR_INPKTRDY; | ||
390 | } | ||
391 | |||
392 | usb_core_transfer_complete(endpoint, EP_IS_IN(ep) ? USB_DIR_IN : USB_DIR_OUT, | ||
393 | 0, ep->length); | ||
394 | ep_transfer_completed(ep); | ||
395 | } | ||
396 | |||
397 | static void setup_endpoint(struct usb_endpoint *ep) | ||
398 | { | ||
399 | int csr, csrh; | ||
400 | |||
401 | select_endpoint(EP_NUMBER2(ep)); | ||
402 | |||
403 | ep->busy = false; | ||
404 | ep->wait = false; | ||
405 | ep->sent = 0; | ||
406 | ep->length = 0; | ||
407 | |||
408 | if(ep->type == ep_bulk) | ||
409 | { | ||
410 | if(REG_USB_POWER & USB_POWER_HSMODE) | ||
411 | ep->fifo_size = 512; | ||
412 | else | ||
413 | ep->fifo_size = 64; | ||
414 | } | ||
415 | |||
416 | if(EP_IS_IN(ep)) | ||
417 | { | ||
418 | csr = (USB_INCSR_FF | USB_INCSR_CDT); | ||
419 | csrh = USB_INCSRH_MODE; | ||
420 | |||
421 | if(ep->use_dma) | ||
422 | csrh |= (USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); | ||
423 | |||
424 | if(ep->type == ep_interrupt) | ||
425 | csrh |= USB_INCSRH_FRCDATATOG; | ||
426 | |||
427 | REG_USB_INMAXP = ep->fifo_size; | ||
428 | REG_USB_INCSR = csr; | ||
429 | REG_USB_INCSRH = csrh; | ||
430 | REG_USB_INTRINE |= USB_INTR_EP(EP_NUMBER2(ep)); | ||
431 | } | ||
432 | else | ||
433 | { | ||
434 | csr = (USB_OUTCSR_FF | USB_OUTCSR_CDT); | ||
435 | csrh = 0; | ||
436 | |||
437 | if(ep->type == ep_interrupt) | ||
438 | csrh |= USB_OUTCSRH_DNYT; | ||
439 | |||
440 | if(ep->use_dma) | ||
441 | csrh |= (USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); | ||
442 | |||
443 | REG_USB_OUTMAXP = ep->fifo_size; | ||
444 | REG_USB_OUTCSR = csr; | ||
445 | REG_USB_OUTCSRH = csrh; | ||
446 | REG_USB_INTROUTE |= USB_INTR_EP(EP_NUMBER2(ep)); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | static void udc_reset(void) | ||
451 | { | ||
452 | /* From the datasheet: | ||
453 | |||
454 | When a reset condition is detected on the USB, the controller performs the following actions: | ||
455 | * Sets FAddr to 0. | ||
456 | * Sets Index to 0. | ||
457 | * Flushes all endpoint FIFOs. | ||
458 | * Clears all control/status registers. | ||
459 | * Enables all endpoint interrupts. | ||
460 | * Generates a Reset interrupt. | ||
461 | */ | ||
462 | |||
463 | logf("%s()", __func__); | ||
464 | |||
465 | unsigned int i; | ||
466 | |||
467 | REG_USB_FADDR = 0; | ||
468 | REG_USB_INDEX = 0; | ||
469 | |||
470 | /* Disable interrupts */ | ||
471 | REG_USB_INTRINE = 0; | ||
472 | REG_USB_INTROUTE = 0; | ||
473 | REG_USB_INTRUSBE = 0; | ||
474 | |||
475 | /* Disable DMA */ | ||
476 | REG_USB_CNTL(0) = 0; | ||
477 | REG_USB_CNTL(1) = 0; | ||
478 | |||
479 | /* High speed, softconnect */ | ||
480 | REG_USB_POWER = (USB_POWER_SOFTCONN | USB_POWER_HSENAB); | ||
481 | |||
482 | /* Reset EP0 */ | ||
483 | select_endpoint(0); | ||
484 | REG_USB_CSR0 = (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_SVDSETUPEND | USB_CSR0_FLUSHFIFO); | ||
485 | |||
486 | /* Reset other endpoints */ | ||
487 | for(i=2; i<TOTAL_EP(); i++) | ||
488 | setup_endpoint(&endpoints[i]); | ||
489 | |||
490 | /* Enable interrupts */ | ||
491 | REG_USB_INTRINE |= USB_INTR_EP(0); | ||
492 | REG_USB_INTRUSBE |= USB_INTR_RESET; | ||
493 | |||
494 | usb_core_bus_reset(); | ||
495 | } | ||
496 | |||
497 | /* Interrupt handler */ | ||
498 | void OTG(void) | ||
499 | { | ||
500 | /* Read interrupt registers */ | ||
501 | unsigned char intrUSB = REG_USB_INTRUSB & 0x07; /* Mask SOF */ | ||
502 | unsigned short intrIn = REG_USB_INTRIN; | ||
503 | unsigned short intrOut = REG_USB_INTROUT; | ||
504 | unsigned char intrDMA = REG_USB_INTR; | ||
505 | |||
506 | logf("%x %x %x %x", intrUSB, intrIn, intrOut, intrDMA); | ||
507 | |||
508 | /* EPIN & EPOUT are all handled in DMA */ | ||
509 | if(intrIn & USB_INTR_EP(0)) | ||
510 | EP0_handler(); | ||
511 | if(intrIn & USB_INTR_EP(1)) | ||
512 | EPIN_handler(1); | ||
513 | if(intrIn & USB_INTR_EP(2)) | ||
514 | EPIN_handler(2); | ||
515 | if(intrOut & USB_INTR_EP(1)) | ||
516 | EPOUT_handler(1); | ||
517 | if(intrOut & USB_INTR_EP(2)) | ||
518 | EPOUT_handler(2); | ||
519 | if(intrUSB & USB_INTR_RESET) | ||
520 | udc_reset(); | ||
521 | if(intrUSB & USB_INTR_SUSPEND) | ||
522 | logf("USB suspend"); | ||
523 | if(intrUSB & USB_INTR_RESUME) | ||
524 | logf("USB resume"); | ||
525 | if(intrDMA & USB_INTR_DMA_BULKIN) | ||
526 | EPDMA_handler(USB_INTR_DMA_BULKIN); | ||
527 | if(intrDMA & USB_INTR_DMA_BULKOUT) | ||
528 | EPDMA_handler(USB_INTR_DMA_BULKOUT); | ||
529 | } | ||
530 | |||
531 | bool usb_drv_stalled(int endpoint, bool in) | ||
532 | { | ||
533 | endpoint &= 0x7F; | ||
534 | |||
535 | logf("%s(%d, %s)", __func__, endpoint, in?"IN":"OUT"); | ||
536 | |||
537 | select_endpoint(endpoint); | ||
538 | |||
539 | if(endpoint == EP_CONTROL) | ||
540 | return (REG_USB_CSR0 & USB_CSR0_SENDSTALL) != 0; | ||
541 | else | ||
542 | { | ||
543 | if(in) | ||
544 | return (REG_USB_INCSR & USB_INCSR_SENDSTALL) != 0; | ||
545 | else | ||
546 | return (REG_USB_OUTCSR & USB_OUTCSR_SENDSTALL) != 0; | ||
547 | } | ||
548 | } | ||
549 | |||
550 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
551 | { | ||
552 | endpoint &= 0x7F; | ||
553 | |||
554 | logf("%s(%d,%s,%s)", __func__, endpoint, stall?"Y":"N", in?"IN":"OUT"); | ||
555 | |||
556 | select_endpoint(endpoint); | ||
557 | |||
558 | if(endpoint == EP_CONTROL) | ||
559 | { | ||
560 | if(stall) | ||
561 | REG_USB_CSR0 |= USB_CSR0_SENDSTALL; | ||
562 | else | ||
563 | REG_USB_CSR0 &= ~USB_CSR0_SENDSTALL; | ||
564 | } | ||
565 | else | ||
566 | { | ||
567 | if(in) | ||
568 | { | ||
569 | if(stall) | ||
570 | REG_USB_INCSR |= USB_INCSR_SENDSTALL; | ||
571 | else | ||
572 | REG_USB_INCSR = (REG_USB_INCSR & ~USB_INCSR_SENDSTALL) | USB_INCSR_CDT; | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | if(stall) | ||
577 | REG_USB_OUTCSR |= USB_OUTCSR_SENDSTALL; | ||
578 | else | ||
579 | REG_USB_OUTCSR = (REG_USB_OUTCSR & ~USB_OUTCSR_SENDSTALL) | USB_OUTCSR_CDT; | ||
580 | } | ||
581 | } | ||
582 | } | ||
583 | |||
584 | int usb_detect(void) | ||
585 | { | ||
586 | return (__gpio_get_pin(PIN_USB_DET) == 1) | ||
587 | ? USB_INSERTED : USB_EXTRACTED; | ||
588 | } | ||
589 | |||
590 | void usb_init_device(void) | ||
591 | { | ||
592 | __gpio_clear_pin(PIN_USB_DRVVBUS); | ||
593 | __gpio_as_output(PIN_USB_DRVVBUS); | ||
594 | |||
595 | __gpio_as_input(PIN_USB_OTG_ID); | ||
596 | __gpio_as_input(PIN_USB_DET); | ||
597 | |||
598 | __gpio_disable_pull(PIN_USB_OTG_ID); | ||
599 | __gpio_disable_pull(PIN_USB_DET); | ||
600 | |||
601 | #ifdef USB_STATUS_BY_EVENT | ||
602 | __gpio_as_irq_rise_edge(PIN_USB_DET); | ||
603 | system_enable_irq(IRQ_USB_DET); | ||
604 | #endif | ||
605 | |||
606 | system_enable_irq(IRQ_OTG); | ||
607 | |||
608 | for(unsigned i=0; i<TOTAL_EP(); i++) | ||
609 | semaphore_init(&endpoints[i].complete, 1, 0); | ||
610 | } | ||
611 | |||
612 | #ifdef USB_STATUS_BY_EVENT | ||
613 | static int usb_oneshot_callback(struct timeout *tmo) | ||
614 | { | ||
615 | (void)tmo; | ||
616 | int state = usb_detect(); | ||
617 | |||
618 | /* This is called only if the state was stable for HZ/16 - check state | ||
619 | * and post appropriate event. */ | ||
620 | usb_status_event(state); | ||
621 | |||
622 | if(state == USB_EXTRACTED) | ||
623 | __gpio_as_irq_rise_edge(PIN_USB_DET); | ||
624 | else | ||
625 | __gpio_as_irq_fall_edge(PIN_USB_DET); | ||
626 | |||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | void GPIO_USB_DET(void) | ||
631 | { | ||
632 | static struct timeout usb_oneshot; | ||
633 | timeout_register(&usb_oneshot, usb_oneshot_callback, (HZ/16), 0); | ||
634 | } | ||
635 | #endif | ||
636 | |||
637 | void usb_enable(bool on) | ||
638 | { | ||
639 | if(on) | ||
640 | usb_core_init(); | ||
641 | else | ||
642 | usb_core_exit(); | ||
643 | } | ||
644 | |||
645 | void usb_attach(void) | ||
646 | { | ||
647 | usb_enable(true); | ||
648 | } | ||
649 | |||
650 | void usb_drv_init(void) | ||
651 | { | ||
652 | logf("%s()", __func__); | ||
653 | |||
654 | /* Dis- and reconnect from USB */ | ||
655 | REG_USB_POWER &= ~USB_POWER_SOFTCONN; | ||
656 | mdelay(20); | ||
657 | REG_USB_POWER |= USB_POWER_SOFTCONN; | ||
658 | mdelay(20); | ||
659 | |||
660 | udc_reset(); | ||
661 | } | ||
662 | |||
663 | void usb_drv_exit(void) | ||
664 | { | ||
665 | logf("%s()", __func__); | ||
666 | |||
667 | REG_USB_FADDR = 0; | ||
668 | REG_USB_INDEX = 0; | ||
669 | |||
670 | /* Disable interrupts */ | ||
671 | REG_USB_INTRINE = 0; | ||
672 | REG_USB_INTROUTE = 0; | ||
673 | REG_USB_INTRUSBE = 0; | ||
674 | |||
675 | /* Disable DMA */ | ||
676 | REG_USB_CNTL(0) = 0; | ||
677 | REG_USB_CNTL(1) = 0; | ||
678 | |||
679 | /* Disconnect from USB */ | ||
680 | REG_USB_POWER &= ~USB_POWER_SOFTCONN; | ||
681 | } | ||
682 | |||
683 | void usb_drv_set_address(int address) | ||
684 | { | ||
685 | logf("%s(%d)", __func__, address); | ||
686 | |||
687 | REG_USB_FADDR = address; | ||
688 | } | ||
689 | |||
690 | static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length, bool blocking) | ||
691 | { | ||
692 | if(ep->type == ep_control && ptr == NULL && length == 0) | ||
693 | return; /* ACK request, handled in the ISR */ | ||
694 | |||
695 | int flags = disable_irq_save(); | ||
696 | |||
697 | ep->buf = ptr; | ||
698 | ep->sent = 0; | ||
699 | ep->length = length; | ||
700 | ep->busy = true; | ||
701 | if(blocking) | ||
702 | ep->wait = true; | ||
703 | |||
704 | if(ep->type == ep_control) | ||
705 | { | ||
706 | EP0_send(); | ||
707 | } | ||
708 | else | ||
709 | { | ||
710 | if(ep->use_dma) | ||
711 | { | ||
712 | //dma_cache_wback_inv((unsigned long)ptr, length); | ||
713 | __dcache_writeback_all(); | ||
714 | REG_USB_ADDR(0) = PHYSADDR((unsigned long)ptr); | ||
715 | REG_USB_COUNT(0) = length; | ||
716 | REG_USB_CNTL(0) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | | ||
717 | USB_CNTL_DIR_IN | USB_CNTL_ENA | | ||
718 | USB_CNTL_EP(EP_NUMBER2(ep)) | USB_CNTL_BURST_16); | ||
719 | } | ||
720 | else | ||
721 | EPIN_handler(EP_NUMBER2(ep)); | ||
722 | } | ||
723 | |||
724 | restore_irq(flags); | ||
725 | |||
726 | if(blocking) | ||
727 | { | ||
728 | semaphore_wait(&ep->complete, TIMEOUT_BLOCK); | ||
729 | ep->wait = false; | ||
730 | } | ||
731 | } | ||
732 | |||
733 | int usb_drv_send_nonblocking(int endpoint, void* ptr, int length) | ||
734 | { | ||
735 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
736 | |||
737 | usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, false); | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | int usb_drv_send(int endpoint, void* ptr, int length) | ||
743 | { | ||
744 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
745 | |||
746 | usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, true); | ||
747 | |||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
752 | { | ||
753 | int flags; | ||
754 | struct usb_endpoint *ep; | ||
755 | endpoint &= 0x7F; | ||
756 | |||
757 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
758 | |||
759 | if(endpoint == EP_CONTROL) | ||
760 | return 0; /* all EP0 OUT transactions are handled within the ISR */ | ||
761 | else | ||
762 | { | ||
763 | flags = disable_irq_save(); | ||
764 | ep = &endpoints[endpoint*2+1]; | ||
765 | |||
766 | ep->buf = ptr; | ||
767 | ep->received = 0; | ||
768 | ep->length = length; | ||
769 | ep->busy = true; | ||
770 | if(ep->use_dma) | ||
771 | { | ||
772 | //dma_cache_wback_inv((unsigned long)ptr, length); | ||
773 | __dcache_writeback_all(); | ||
774 | REG_USB_ADDR(1) = PHYSADDR((unsigned long)ptr); | ||
775 | REG_USB_COUNT(1) = length; | ||
776 | REG_USB_CNTL(1) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | | ||
777 | USB_CNTL_ENA | USB_CNTL_EP(endpoint) | | ||
778 | USB_CNTL_BURST_16); | ||
779 | } | ||
780 | else | ||
781 | EPOUT_handler(endpoint); | ||
782 | |||
783 | restore_irq(flags); | ||
784 | return 0; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | void usb_drv_set_test_mode(int mode) | ||
789 | { | ||
790 | logf("%s(%d)", __func__, mode); | ||
791 | |||
792 | switch(mode) | ||
793 | { | ||
794 | case 0: | ||
795 | REG_USB_TESTMODE &= ~USB_TEST_ALL; | ||
796 | break; | ||
797 | case 1: | ||
798 | REG_USB_TESTMODE |= USB_TEST_J; | ||
799 | break; | ||
800 | case 2: | ||
801 | REG_USB_TESTMODE |= USB_TEST_K; | ||
802 | break; | ||
803 | case 3: | ||
804 | REG_USB_TESTMODE |= USB_TEST_SE0NAK; | ||
805 | break; | ||
806 | case 4: | ||
807 | REG_USB_TESTMODE |= USB_TEST_PACKET; | ||
808 | break; | ||
809 | } | ||
810 | } | ||
811 | |||
812 | int usb_drv_port_speed(void) | ||
813 | { | ||
814 | return (REG_USB_POWER & USB_POWER_HSMODE) ? 1 : 0; | ||
815 | } | ||
816 | |||
817 | void usb_drv_cancel_all_transfers(void) | ||
818 | { | ||
819 | logf("%s()", __func__); | ||
820 | |||
821 | unsigned int i, flags; | ||
822 | flags = disable_irq_save(); | ||
823 | |||
824 | for(i=0; i<TOTAL_EP(); i++) | ||
825 | { | ||
826 | if(i != 1) /* ep0 out needs special handling */ | ||
827 | endpoints[i].buf = NULL; | ||
828 | |||
829 | endpoints[i].sent = 0; | ||
830 | endpoints[i].length = 0; | ||
831 | |||
832 | select_endpoint(i/2); | ||
833 | flushFIFO(&endpoints[i]); | ||
834 | } | ||
835 | restore_irq(flags); | ||
836 | } | ||
837 | |||
838 | |||
839 | void usb_drv_release_endpoint(int ep) | ||
840 | { | ||
841 | (void)ep; | ||
842 | logf("%s(%d, %s)", __func__, (ep & 0x7F), (ep >> 7) ? "IN" : "OUT"); | ||
843 | } | ||
844 | |||
845 | int usb_drv_request_endpoint(int type, int dir) | ||
846 | { | ||
847 | logf("%s(%d, %s)", __func__, type, (dir == USB_DIR_IN) ? "IN" : "OUT"); | ||
848 | |||
849 | dir &= USB_ENDPOINT_DIR_MASK; | ||
850 | type &= USB_ENDPOINT_XFERTYPE_MASK; | ||
851 | |||
852 | /* There are only 3+2 endpoints, so hardcode this ... */ | ||
853 | switch(type) | ||
854 | { | ||
855 | case USB_ENDPOINT_XFER_BULK: | ||
856 | if(dir == USB_DIR_IN) | ||
857 | return (1 | USB_DIR_IN); | ||
858 | else | ||
859 | return (1 | USB_DIR_OUT); | ||
860 | |||
861 | case USB_ENDPOINT_XFER_INT: | ||
862 | if(dir == USB_DIR_IN) | ||
863 | return (2 | USB_DIR_IN); | ||
864 | else | ||
865 | return (2 | USB_DIR_OUT); | ||
866 | |||
867 | default: | ||
868 | return -1; | ||
869 | } | ||
870 | } | ||