diff options
Diffstat (limited to 'firmware/target/arm/usb-tcc.c')
-rw-r--r-- | firmware/target/arm/usb-tcc.c | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c new file mode 100644 index 0000000000..345f6bed78 --- /dev/null +++ b/firmware/target/arm/usb-tcc.c | |||
@@ -0,0 +1,799 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2008 by Vitja Makarov | ||
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 | #include "usb.h" | ||
24 | |||
25 | #include "usb-tcc.h" | ||
26 | |||
27 | #include "cpu.h" | ||
28 | #include "system.h" | ||
29 | #include "kernel.h" | ||
30 | #include "panic.h" | ||
31 | |||
32 | #ifdef HAVE_USBSTACK | ||
33 | #include "usb_ch9.h" | ||
34 | #include "usb_core.h" | ||
35 | |||
36 | #define TCC7xx_USB_EPIF_IRQ_MASK 0xf | ||
37 | |||
38 | static int dbg_level = 0x00; | ||
39 | static int global_ep_irq_mask = 0x1; | ||
40 | #define DEBUG(level, fmt, args...) do { if (dbg_level & (level)) printf(fmt, ## args); } while (0) | ||
41 | |||
42 | #include <inttypes.h> | ||
43 | |||
44 | |||
45 | #include "sprintf.h" | ||
46 | #include "power.h" | ||
47 | |||
48 | #ifndef BOOTLOADER | ||
49 | #define printf(...) do {} while (0) | ||
50 | #define panicf_my panicf | ||
51 | #else | ||
52 | int printf(const char *fmt, ...); | ||
53 | #define panicf_my(fmt, args...) { \ | ||
54 | int flags = disable_irq_save(); \ | ||
55 | printf("*** PANIC ***"); \ | ||
56 | printf(fmt, ## args); \ | ||
57 | printf("*** PANIC ***"); \ | ||
58 | while (usb_detect() == USB_INSERTED) \ | ||
59 | ; \ | ||
60 | power_off(); \ | ||
61 | while(1); \ | ||
62 | restore_irq(flags); \ | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | struct tcc_ep { | ||
67 | unsigned char dir; /* endpoint direction */ | ||
68 | volatile uint16_t *ep; /* hw ep buffer */ | ||
69 | int id; /* Endpoint id */ | ||
70 | int mask; /* Endpoint bit mask */ | ||
71 | char *buf; /* user buffer to store data */ | ||
72 | int max_len; /* how match data will fit */ | ||
73 | int count; /* actual data count */ | ||
74 | bool busy; | ||
75 | } ; | ||
76 | |||
77 | static struct tcc_ep tcc_endpoints[] = { | ||
78 | /* control */ | ||
79 | { | ||
80 | .dir = -1, | ||
81 | .ep = &TCC7xx_USB_EP0_BUF, | ||
82 | }, { /* bulk */ | ||
83 | .dir = -1, | ||
84 | .ep = &TCC7xx_USB_EP1_BUF, | ||
85 | }, { /* bulk */ | ||
86 | .dir = -1, | ||
87 | .ep = &TCC7xx_USB_EP2_BUF, | ||
88 | }, { /* interrupt */ | ||
89 | .dir = -1, | ||
90 | .ep = &TCC7xx_USB_EP3_BUF, | ||
91 | }, | ||
92 | } ; | ||
93 | |||
94 | static bool usb_drv_write_ep(struct tcc_ep *ep); | ||
95 | static void usb_set_speed(int); | ||
96 | |||
97 | int usb_drv_request_endpoint(int dir) | ||
98 | { | ||
99 | int flags = disable_irq_save(); | ||
100 | size_t ep; | ||
101 | int ret = 0; | ||
102 | |||
103 | if (dir == USB_DIR_IN) | ||
104 | ep = 1; | ||
105 | else | ||
106 | ep = 2; | ||
107 | |||
108 | if (!tcc_endpoints[ep].busy) { | ||
109 | tcc_endpoints[ep].busy = true; | ||
110 | tcc_endpoints[ep].dir = dir; | ||
111 | ret = ep | dir; | ||
112 | } else { | ||
113 | ret = -1; | ||
114 | } | ||
115 | |||
116 | restore_irq(flags); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | void usb_drv_release_endpoint(int ep) | ||
121 | { | ||
122 | int flags; | ||
123 | ep = ep & 0x7f; | ||
124 | |||
125 | if (ep < 1 || ep > NUM_ENDPOINTS) | ||
126 | return ; | ||
127 | |||
128 | flags = disable_irq_save(); | ||
129 | |||
130 | tcc_endpoints[ep].busy = false; | ||
131 | tcc_endpoints[ep].dir = -1; | ||
132 | |||
133 | restore_irq(flags); | ||
134 | } | ||
135 | |||
136 | static void udelay(unsigned long msecs) | ||
137 | { | ||
138 | /* TODO: implement me other way */ | ||
139 | msecs*=126; | ||
140 | while (msecs--) | ||
141 | asm("nop;"); | ||
142 | } | ||
143 | |||
144 | static inline void pullup_on(void) | ||
145 | { | ||
146 | TCC7xx_USB_PHY_CFG = 0x000c; | ||
147 | } | ||
148 | |||
149 | static inline void pullup_off(void) | ||
150 | { | ||
151 | TCC7xx_USB_PHY_CFG = 0x3e4c; | ||
152 | } | ||
153 | |||
154 | #if 0 | ||
155 | static | ||
156 | char *dump_data(char *data, int count) | ||
157 | { | ||
158 | static char buf[1024]; | ||
159 | char *dump = buf; | ||
160 | int i; | ||
161 | |||
162 | for (i = 0; i < count; i++) | ||
163 | dump += snprintf(dump, sizeof(buf) - (dump - buf), "%02x", data[i]); | ||
164 | return buf; | ||
165 | } | ||
166 | #endif | ||
167 | |||
168 | static | ||
169 | void handle_control(void) | ||
170 | { | ||
171 | /* control are always 8 bytes len */ | ||
172 | static unsigned char ep_control[8]; | ||
173 | struct usb_ctrlrequest *req = | ||
174 | (struct usb_ctrlrequest *) ep_control; | ||
175 | unsigned short stat; | ||
176 | unsigned short count = 0; | ||
177 | int i; | ||
178 | int type; | ||
179 | |||
180 | /* select control endpoint */ | ||
181 | TCC7xx_USB_INDEX = 0x00; | ||
182 | stat = TCC7xx_USB_EP0_STAT; | ||
183 | |||
184 | if (stat & 0x10) { | ||
185 | DEBUG(2, "stall"); | ||
186 | TCC7xx_USB_EP0_STAT = 0x10; | ||
187 | } | ||
188 | |||
189 | if (TCC7xx_USB_EP0_STAT & 0x01) { /* RX */ | ||
190 | uint16_t *ptr = (uint16_t *) ep_control; | ||
191 | |||
192 | count = TCC7xx_USB_EP_BRCR; | ||
193 | |||
194 | if (TCC7xx_USB_EP0_STAT & 0x2) | ||
195 | TCC7xx_USB_EP0_STAT = 0x02; | ||
196 | |||
197 | if (count != 4) { /* bad control? */ | ||
198 | unsigned short dummy; | ||
199 | |||
200 | while (count--) | ||
201 | dummy = TCC7xx_USB_EP0_BUF; | ||
202 | DEBUG(1, "WTF: count = %d", count); | ||
203 | } else { | ||
204 | /* simply read control packet */ | ||
205 | for (i = 0; i < count; i++) | ||
206 | ptr[i] = TCC7xx_USB_EP0_BUF; | ||
207 | } | ||
208 | |||
209 | count *= 2; | ||
210 | TCC7xx_USB_EP0_STAT = 0x01; | ||
211 | DEBUG(1, "CTRL: len = %d %04x", count, stat); | ||
212 | } else if (TCC7xx_USB_EP0_STAT & 0x02) { /* TX */ | ||
213 | TCC7xx_USB_EP0_STAT = 0x02; | ||
214 | DEBUG(2, "TX Done\n"); | ||
215 | } else { | ||
216 | DEBUG(1, "stat: %04x", stat); | ||
217 | } | ||
218 | |||
219 | TCC7xx_USB_EPIF = 1; | ||
220 | |||
221 | if (0 == (stat & 0x1) || count != 8) | ||
222 | return ; | ||
223 | #if 1 /* TODO: remove me someday */ | ||
224 | { | ||
225 | int i; | ||
226 | uint16_t *ptr = (uint16_t *) ep_control; | ||
227 | for (i = 1; i < (count>>1); i++) { | ||
228 | if (ptr[i] != ptr[0]) | ||
229 | break; | ||
230 | } | ||
231 | if (i == (count>>1)) { | ||
232 | /*DEBUG(2, */panicf_my("sanity failed"); | ||
233 | return ; | ||
234 | } | ||
235 | } | ||
236 | #endif | ||
237 | type = req->bRequestType; | ||
238 | |||
239 | /* TODO: don't pass some kinds of requests to upper level */ | ||
240 | switch (req->bRequest) { | ||
241 | case USB_REQ_CLEAR_FEATURE: | ||
242 | DEBUG(2, "USB_REQ_CLEAR_FEATURE"); | ||
243 | DEBUG(2, "...%04x %04x", req->wValue, req->wIndex); | ||
244 | break; | ||
245 | case USB_REQ_SET_ADDRESS: | ||
246 | //DEBUG(2, "USB_REQ_SET_ADDRESS, %d %d", req->wValue, TCC7xx_USB_FUNC); | ||
247 | /* seems we don't have to set it manually | ||
248 | TCC7xx_USB_FUNC = req->wValue; */ | ||
249 | break; | ||
250 | case USB_REQ_GET_DESCRIPTOR: | ||
251 | DEBUG(2, "gd, %02x %02x", req->wValue, req->wIndex); | ||
252 | break; | ||
253 | case USB_REQ_GET_CONFIGURATION: | ||
254 | DEBUG(2, "USB_REQ_GET_CONFIGURATION"); | ||
255 | break; | ||
256 | default: | ||
257 | DEBUG(2, "req: %02x %02d", req->bRequestType, req->bRequest); | ||
258 | } | ||
259 | |||
260 | usb_core_control_request(req); | ||
261 | } | ||
262 | |||
263 | static | ||
264 | void handle_ep_in(struct tcc_ep *tcc_ep, uint16_t stat) | ||
265 | { | ||
266 | uint8_t *buf = tcc_ep->buf; | ||
267 | uint16_t *wbuf = (uint16_t *) buf; | ||
268 | int wcount; | ||
269 | int count; | ||
270 | int i; | ||
271 | |||
272 | if (tcc_ep->dir != USB_DIR_OUT) { | ||
273 | panicf_my("ep%d: is input only", tcc_ep->id); | ||
274 | } | ||
275 | |||
276 | wcount = TCC7xx_USB_EP_BRCR; | ||
277 | |||
278 | DEBUG(2, "ep%d: %04x %04x", tcc_ep->id, stat, wcount); | ||
279 | |||
280 | /* read data */ | ||
281 | count = wcount * 2; | ||
282 | if (stat & TCC7xx_USP_EP_STAT_LWO) { | ||
283 | count--; | ||
284 | wcount--; | ||
285 | } | ||
286 | |||
287 | if (buf == NULL) | ||
288 | panicf_my("ep%d: Unexpected packet! %d %x", tcc_ep->id, count, TCC7xx_USB_EP_CTRL); | ||
289 | if (tcc_ep->max_len < count) | ||
290 | panicf_my("Too big packet: %d excepted %d %x", count, tcc_ep->max_len, TCC7xx_USB_EP_CTRL); | ||
291 | |||
292 | for (i = 0; i < wcount; i++) | ||
293 | wbuf[i] = *tcc_ep->ep; | ||
294 | |||
295 | if (count & 1) { /* lwo */ | ||
296 | uint16_t tmp = *tcc_ep->ep; | ||
297 | buf[count - 1] = tmp & 0xff; | ||
298 | } | ||
299 | |||
300 | tcc_ep->buf = NULL; | ||
301 | |||
302 | TCC7xx_USB_EP_STAT = TCC7xx_USB_EP_STAT; | ||
303 | TCC7xx_USB_EPIF = tcc_ep->mask; | ||
304 | TCC7xx_USB_EPIE &= ~tcc_ep->mask; /* TODO: use INGLD? */ | ||
305 | global_ep_irq_mask &= ~tcc_ep->mask; | ||
306 | |||
307 | if (TCC7xx_USB_EP_STAT & 0x1) | ||
308 | panicf_my("One more packet?"); | ||
309 | |||
310 | TCC7xx_USB_EP_CTRL |= TCC7xx_USB_EP_CTRL_OUTHD; | ||
311 | |||
312 | usb_core_transfer_complete(tcc_ep->id, USB_DIR_OUT, 0, count); | ||
313 | } | ||
314 | |||
315 | static | ||
316 | void handle_ep_out(struct tcc_ep *tcc_ep, uint16_t stat) | ||
317 | { | ||
318 | bool done; | ||
319 | (void) stat; | ||
320 | |||
321 | if (tcc_ep->dir != USB_DIR_IN) { | ||
322 | panicf_my("ep%d: is out only", tcc_ep->id); | ||
323 | } | ||
324 | |||
325 | // if (tcc_ep->buf == NULL) { | ||
326 | // panicf_my("%s:%d", __FILE__, __LINE__); | ||
327 | // } | ||
328 | |||
329 | done = usb_drv_write_ep(tcc_ep); | ||
330 | |||
331 | // TCC7xx_USB_EP_STAT = 0x2; /* Clear TX stat */ | ||
332 | TCC7xx_USB_EPIF = tcc_ep->mask; | ||
333 | |||
334 | if (done) { // tcc_ep->buf == NULL) { | ||
335 | TCC7xx_USB_EPIE &= ~tcc_ep->mask; | ||
336 | global_ep_irq_mask &= ~tcc_ep->mask; | ||
337 | |||
338 | // usb_core_transfer_complete(tcc_ep->id, USB_DIR_IN, 0, tcc_ep->count); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | static | ||
343 | void handle_ep(unsigned short ep_irq) | ||
344 | { | ||
345 | if (ep_irq & 0x1) { | ||
346 | handle_control(); | ||
347 | } | ||
348 | |||
349 | if (ep_irq & 0xe) { | ||
350 | int endpoint; | ||
351 | |||
352 | for (endpoint = 1; endpoint < 4; endpoint++) { | ||
353 | struct tcc_ep *tcc_ep = &tcc_endpoints[endpoint]; | ||
354 | uint16_t stat; | ||
355 | |||
356 | if (0 == (ep_irq & (1 << endpoint))) | ||
357 | continue; | ||
358 | if (!tcc_ep->busy) | ||
359 | panicf_my("ep%d: wasn't requested", endpoint); | ||
360 | |||
361 | TCC7xx_USB_INDEX = endpoint; | ||
362 | stat = TCC7xx_USB_EP_STAT; | ||
363 | |||
364 | DEBUG(1, "ep%d: %04x", endpoint, stat); | ||
365 | |||
366 | if (stat & 0x1) | ||
367 | handle_ep_in(tcc_ep, stat); | ||
368 | else if (stat & 0x2) | ||
369 | handle_ep_out(tcc_ep, stat); | ||
370 | else /* TODO: remove me? */ | ||
371 | panicf_my("Unhandled ep%d state: %x, %d", endpoint, TCC7xx_USB_EP_STAT, TCC7xx_USB_INDEX); | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | static void usb_set_speed(int high_speed) | ||
377 | { | ||
378 | TCC7xx_USB_EP_DIR = 0x0000; | ||
379 | |||
380 | /* control endpoint */ | ||
381 | TCC7xx_USB_INDEX = 0; | ||
382 | TCC7xx_USB_EP0_CTRL = 0x0000; | ||
383 | TCC7xx_USB_EP_MAXP = 64; | ||
384 | TCC7xx_USB_EP_CTRL = TCC7xx_USB_EP_CTRL_CDP | TCC7xx_USB_EP_CTRL_FLUSH; | ||
385 | |||
386 | /* ep1: bulk-in, to host */ | ||
387 | TCC7xx_USB_INDEX = 1; | ||
388 | TCC7xx_USB_EP_DIR |= (1 << 1); | ||
389 | TCC7xx_USB_EP_CTRL = TCC7xx_USB_EP_CTRL_CDP; | ||
390 | |||
391 | if (high_speed) | ||
392 | TCC7xx_USB_EP_MAXP = 512; | ||
393 | else | ||
394 | TCC7xx_USB_EP_MAXP = 64; | ||
395 | |||
396 | TCC7xx_USB_EP_DMA_CTRL = 0x0; | ||
397 | |||
398 | /* ep2: bulk-out, from host */ | ||
399 | TCC7xx_USB_INDEX = 2; | ||
400 | TCC7xx_USB_EP_DIR &= ~(1 << 2); | ||
401 | TCC7xx_USB_EP_CTRL = TCC7xx_USB_EP_CTRL_CDP; | ||
402 | |||
403 | if (high_speed) | ||
404 | TCC7xx_USB_EP_MAXP = 512; | ||
405 | else | ||
406 | TCC7xx_USB_EP_MAXP = 64; | ||
407 | |||
408 | TCC7xx_USB_EP_DMA_CTRL = 0x0; | ||
409 | |||
410 | /* ep3: interrupt in */ | ||
411 | TCC7xx_USB_INDEX = 3; | ||
412 | TCC7xx_USB_EP_DIR &= ~(1 << 3); | ||
413 | TCC7xx_USB_EP_CTRL = TCC7xx_USB_EP_CTRL_CDP; | ||
414 | TCC7xx_USB_EP_MAXP = 64; | ||
415 | |||
416 | TCC7xx_USB_EP_DMA_CTRL = 0x0; | ||
417 | } | ||
418 | |||
419 | /* | ||
420 | Reset TCC7xx usb device | ||
421 | */ | ||
422 | static void usb_reset(void) | ||
423 | { | ||
424 | pullup_on(); | ||
425 | |||
426 | TCC7xx_USB_DELAY_CTRL |= 0x81; | ||
427 | |||
428 | TCC7xx_USB_SYS_CTRL = 0xa000 | | ||
429 | TCC7xx_USB_SYS_CTRL_RESET | | ||
430 | TCC7xx_USB_SYS_CTRL_RFRE | | ||
431 | TCC7xx_USB_SYS_CTRL_SPDEN | | ||
432 | TCC7xx_USB_SYS_CTRL_VBONE | | ||
433 | TCC7xx_USB_SYS_CTRL_VBOFE; | ||
434 | |||
435 | usb_set_speed(1); | ||
436 | pullup_on(); | ||
437 | |||
438 | TCC7xx_USB_EPIF = TCC7xx_USB_EPIF_IRQ_MASK; | ||
439 | global_ep_irq_mask = 0x1; | ||
440 | TCC7xx_USB_EPIE = global_ep_irq_mask; | ||
441 | |||
442 | usb_core_bus_reset(); | ||
443 | } | ||
444 | |||
445 | /* IRQ handler */ | ||
446 | void USB_DEVICE(void) | ||
447 | { | ||
448 | unsigned short sys_stat; | ||
449 | unsigned short ep_irq; | ||
450 | unsigned short index_save; | ||
451 | |||
452 | sys_stat = TCC7xx_USB_SYS_STAT; | ||
453 | |||
454 | if (sys_stat & TCC7xx_USB_SYS_STAT_RESET) { | ||
455 | TCC7xx_USB_SYS_STAT = TCC7xx_USB_SYS_STAT_RESET; | ||
456 | usb_reset(); | ||
457 | TCC7xx_USB_SYS_CTRL |= TCC7xx_USB_SYS_CTRL_SUSPEND; | ||
458 | DEBUG(2, "reset"); | ||
459 | } | ||
460 | |||
461 | if (sys_stat & TCC7xx_USB_SYS_STAT_RESUME) { | ||
462 | TCC7xx_USB_SYS_STAT = TCC7xx_USB_SYS_STAT_RESUME; | ||
463 | usb_reset(); | ||
464 | TCC7xx_USB_SYS_CTRL |= TCC7xx_USB_SYS_CTRL_SUSPEND; | ||
465 | DEBUG(2, "resume"); | ||
466 | } | ||
467 | |||
468 | if (sys_stat & TCC7xx_USB_SYS_STAT_SPD_END) { | ||
469 | usb_set_speed(1); | ||
470 | TCC7xx_USB_SYS_STAT = TCC7xx_USB_SYS_STAT_SPD_END; | ||
471 | DEBUG(2, "spd end"); | ||
472 | } | ||
473 | |||
474 | if (sys_stat & TCC7xx_USB_SYS_STAT_ERRORS) { | ||
475 | DEBUG(2, "errors: %4x", sys_stat & TCC7xx_USB_SYS_STAT_ERRORS); | ||
476 | TCC7xx_USB_SYS_STAT = sys_stat & TCC7xx_USB_SYS_STAT_ERRORS; | ||
477 | } | ||
478 | |||
479 | // TCC7xx_USB_SYS_STAT = sys_stat; | ||
480 | |||
481 | index_save = TCC7xx_USB_INDEX; | ||
482 | |||
483 | ep_irq = TCC7xx_USB_EPIF & global_ep_irq_mask; | ||
484 | |||
485 | while (ep_irq & TCC7xx_USB_EPIF_IRQ_MASK) { | ||
486 | handle_ep(ep_irq); | ||
487 | |||
488 | /* is that really needed, btw not a problem for rockbox */ | ||
489 | udelay(50); | ||
490 | ep_irq = TCC7xx_USB_EPIF & global_ep_irq_mask; | ||
491 | } | ||
492 | |||
493 | TCC7xx_USB_INDEX = index_save; | ||
494 | } | ||
495 | |||
496 | void usb_drv_set_address(int address) | ||
497 | { | ||
498 | DEBUG(2, "setting address %d %d", address, TCC7xx_USB_FUNC); | ||
499 | } | ||
500 | |||
501 | int usb_drv_port_speed(void) | ||
502 | { | ||
503 | return (TCC7xx_USB_SYS_STAT & 0x10) ? 1 : 0; | ||
504 | } | ||
505 | |||
506 | static int usb_drv_write_packet(volatile unsigned short *buf, unsigned char *data, int len, int max) | ||
507 | { | ||
508 | uint16_t *wbuf = (uint16_t *) data; | ||
509 | int count, i; | ||
510 | |||
511 | len = MIN(len, max); | ||
512 | count = (len + 1) / 2; | ||
513 | |||
514 | TCC7xx_USB_EP_BWCR = len; | ||
515 | |||
516 | for (i = 0; i < count; i++) | ||
517 | *buf = *wbuf++; | ||
518 | |||
519 | return len; | ||
520 | } | ||
521 | |||
522 | static bool usb_drv_write_ep(struct tcc_ep *ep) | ||
523 | { | ||
524 | int count; | ||
525 | |||
526 | if (ep->max_len == 0) | ||
527 | return true; | ||
528 | |||
529 | count = usb_drv_write_packet(ep->ep, ep->buf, ep->max_len, 512); | ||
530 | TCC7xx_USB_EP_STAT = 0x2; /* Clear TX stat */ | ||
531 | |||
532 | ep->buf += count; | ||
533 | ep->count += count; | ||
534 | ep->max_len -= count; | ||
535 | |||
536 | if (ep->max_len == 0) { | ||
537 | usb_core_transfer_complete(ep->id, USB_DIR_IN, 0, ep->count); | ||
538 | ep->buf = NULL; | ||
539 | // return true; | ||
540 | } | ||
541 | |||
542 | return false; | ||
543 | } | ||
544 | |||
545 | int usb_drv_send(int endpoint, void *ptr, int length) | ||
546 | { | ||
547 | int flags = disable_irq_save(); | ||
548 | int rc = 0; | ||
549 | char *data = (unsigned char*) ptr;; | ||
550 | |||
551 | DEBUG(2, "%s(%d,%d)" , __func__, endpoint, length); | ||
552 | |||
553 | if (endpoint != 0) | ||
554 | panicf_my("%s(%d,%d)", __func__, endpoint, length); | ||
555 | |||
556 | TCC7xx_USB_INDEX = 0; | ||
557 | while (length > 0) { | ||
558 | int ret; | ||
559 | |||
560 | ret = usb_drv_write_packet(&TCC7xx_USB_EP0_BUF, data, length, 64); | ||
561 | length -= ret; | ||
562 | data += ret; | ||
563 | |||
564 | while (0 == (TCC7xx_USB_EP0_STAT & 0x2)) | ||
565 | ; | ||
566 | TCC7xx_USB_EP0_STAT = 0x2; | ||
567 | } | ||
568 | |||
569 | restore_irq(flags); | ||
570 | return rc; | ||
571 | } | ||
572 | |||
573 | |||
574 | int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) | ||
575 | { | ||
576 | int flags; | ||
577 | int rc = 0, count = length; | ||
578 | char *data = (unsigned char*) ptr; | ||
579 | struct tcc_ep *ep = &tcc_endpoints[endpoint & 0x7f]; | ||
580 | |||
581 | if (ep->dir != USB_DIR_IN || length == 0) | ||
582 | panicf_my("%s(%d,%d): Not supported", __func__, endpoint, length); | ||
583 | |||
584 | DEBUG(2, "%s(%d,%d):", __func__, endpoint, length); | ||
585 | |||
586 | flags = disable_irq_save(); | ||
587 | |||
588 | if(ep->buf != NULL) { | ||
589 | panicf_my("%s: ep is already busy", __func__); | ||
590 | } | ||
591 | |||
592 | ep->buf = data; | ||
593 | ep->max_len = length; | ||
594 | ep->count = count; | ||
595 | |||
596 | TCC7xx_USB_INDEX = ep->id; | ||
597 | #if 1 | ||
598 | TCC7xx_USB_EP_STAT = 0x2; | ||
599 | /* TODO: use interrupts instead */ | ||
600 | while (!usb_drv_write_ep(ep)) { | ||
601 | while (0==(TCC7xx_USB_EP_STAT & 0x2)) | ||
602 | ; | ||
603 | } | ||
604 | #else | ||
605 | if (!usb_drv_write_ep(ep)) { | ||
606 | TCC7xx_USB_EPIE |= ep->mask; | ||
607 | global_ep_irq_mask |= ep->mask; | ||
608 | } | ||
609 | #endif | ||
610 | restore_irq(flags); | ||
611 | |||
612 | DEBUG(2, "%s end", __func__); | ||
613 | |||
614 | return rc; | ||
615 | } | ||
616 | |||
617 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
618 | { | ||
619 | volatile struct tcc_ep *tcc_ep = &tcc_endpoints[endpoint & 0x7f]; | ||
620 | int flags; | ||
621 | |||
622 | if (length == 0) { | ||
623 | if (endpoint != 0) | ||
624 | panicf_my("%s(%d,%d) zero length?", __func__, endpoint, length); | ||
625 | return 0; | ||
626 | } | ||
627 | // TODO: check ep | ||
628 | if (tcc_ep->dir != USB_DIR_OUT) | ||
629 | panicf_my("%s(%d,%d)", __func__, endpoint, length); | ||
630 | |||
631 | DEBUG(2, "%s(%d,%d)", __func__, endpoint, length); | ||
632 | |||
633 | flags = disable_irq_save(); | ||
634 | |||
635 | if (tcc_ep->buf) { | ||
636 | panicf_my("%s: overrun: %x %x", __func__, tcc_ep->buf, tcc_ep); | ||
637 | } | ||
638 | |||
639 | tcc_ep->buf = ptr; | ||
640 | tcc_ep->max_len = length; | ||
641 | tcc_ep->count = 0; | ||
642 | |||
643 | TCC7xx_USB_INDEX = tcc_ep->id; | ||
644 | |||
645 | TCC7xx_USB_EP_CTRL &= ~TCC7xx_USB_EP_CTRL_OUTHD; | ||
646 | TCC7xx_USB_EPIE |= tcc_ep->mask; | ||
647 | global_ep_irq_mask |= tcc_ep->mask; | ||
648 | |||
649 | restore_irq(flags); | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | void usb_drv_cancel_all_transfers(void) | ||
655 | { | ||
656 | int endpoint; | ||
657 | int flags; | ||
658 | |||
659 | DEBUG(2, "%s", __func__); | ||
660 | |||
661 | flags = disable_irq_save(); | ||
662 | for (endpoint = 0; endpoint < 4; endpoint++) { | ||
663 | if (tcc_endpoints[endpoint].buf) { | ||
664 | /* usb_core_transfer_complete(tcc_endpoints[endpoint].id, | ||
665 | tcc_endpoints[endpoint].dir, -1, 0); */ | ||
666 | tcc_endpoints[endpoint].buf = NULL; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | global_ep_irq_mask = 1; | ||
671 | TCC7xx_USB_EPIE = global_ep_irq_mask; | ||
672 | TCC7xx_USB_EPIF = TCC7xx_USB_EPIF_IRQ_MASK; | ||
673 | restore_irq(flags); | ||
674 | } | ||
675 | |||
676 | void usb_drv_set_test_mode(int mode) | ||
677 | { | ||
678 | panicf_my("%s(%d)", __func__, mode); | ||
679 | } | ||
680 | |||
681 | bool usb_drv_stalled(int endpoint, bool in) | ||
682 | { | ||
683 | panicf_my("%s(%d,%d)", __func__, endpoint, in); | ||
684 | } | ||
685 | |||
686 | void usb_drv_stall(int endpoint, bool stall,bool in) | ||
687 | { | ||
688 | printf("%s(%d,%d,%d)", __func__, endpoint, stall, in); | ||
689 | } | ||
690 | |||
691 | void usb_drv_init(void) | ||
692 | { | ||
693 | size_t i; | ||
694 | |||
695 | DEBUG(2, "%s", __func__); | ||
696 | |||
697 | for (i = 0; i < sizeof(tcc_endpoints)/sizeof(struct tcc_ep); i++) { | ||
698 | tcc_endpoints[i].id = i; | ||
699 | tcc_endpoints[i].mask = 1 << i; | ||
700 | tcc_endpoints[i].buf = NULL; | ||
701 | tcc_endpoints[i].busy = false; | ||
702 | tcc_endpoints[i].dir = -1; | ||
703 | } | ||
704 | |||
705 | /* Enable USB clock */ | ||
706 | BCLKCTR |= DEV_USBD; | ||
707 | |||
708 | /* switch USB to host and then reset */ | ||
709 | TCC7xx_USB_PHY_CFG = 0x3e4c; | ||
710 | SWRESET |= DEV_USBD; | ||
711 | udelay(50); | ||
712 | SWRESET &= ~DEV_USBD; | ||
713 | |||
714 | usb_reset(); | ||
715 | |||
716 | /* unmask irq */ | ||
717 | CREQ = USBD_IRQ_MASK; | ||
718 | IRQSEL |= USBD_IRQ_MASK; | ||
719 | TMODE &= ~USBD_IRQ_MASK; | ||
720 | IEN |= USBD_IRQ_MASK; | ||
721 | } | ||
722 | |||
723 | void usb_drv_exit(void) | ||
724 | { | ||
725 | TCC7xx_USB_EPIE = 0; | ||
726 | BCLKCTR &= ~DEV_USBD; | ||
727 | |||
728 | SWRESET |= DEV_USBD; | ||
729 | udelay(50); | ||
730 | SWRESET &= ~DEV_USBD; | ||
731 | |||
732 | pullup_off(); | ||
733 | } | ||
734 | |||
735 | void usb_init_device(void) | ||
736 | { | ||
737 | } | ||
738 | |||
739 | void usb_enable(bool on) | ||
740 | { | ||
741 | if (on) | ||
742 | usb_core_init(); | ||
743 | else | ||
744 | usb_core_exit(); | ||
745 | } | ||
746 | |||
747 | |||
748 | int usb_detect(void) | ||
749 | { | ||
750 | /* TODO: not correct for all targets, we should poll VBUS | ||
751 | signal on USB bus. */ | ||
752 | if (charger_inserted()) | ||
753 | return USB_INSERTED; | ||
754 | return USB_EXTRACTED; | ||
755 | } | ||
756 | |||
757 | #ifdef BOOTLOADER | ||
758 | #include "ata.h" | ||
759 | void usb_test(void) | ||
760 | { | ||
761 | int rc; | ||
762 | |||
763 | printf("ATA"); | ||
764 | rc = ata_init(); | ||
765 | |||
766 | if(rc) { | ||
767 | panicf("ata_init failed"); | ||
768 | } | ||
769 | |||
770 | usb_init(); | ||
771 | usb_start_monitoring(); | ||
772 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
773 | |||
774 | while (1) { | ||
775 | sleep(HZ); | ||
776 | // usb_serial_send("Hello\r\n", 7); | ||
777 | } | ||
778 | } | ||
779 | #endif | ||
780 | #else | ||
781 | void usb_init_device(void) | ||
782 | { | ||
783 | /* simply switch USB off for now */ | ||
784 | BCLKCTR |= DEV_USBD; | ||
785 | TCC7xx_USB_PHY_CFG = 0x3e4c; | ||
786 | BCLKCTR &= ~DEV_USBD; | ||
787 | } | ||
788 | |||
789 | void usb_enable(bool on) | ||
790 | { | ||
791 | (void)on; | ||
792 | } | ||
793 | |||
794 | /* Always return false for now */ | ||
795 | int usb_detect(void) | ||
796 | { | ||
797 | return USB_EXTRACTED; | ||
798 | } | ||
799 | #endif | ||