diff options
Diffstat (limited to 'firmware/target/arm/rk27xx/usb-drv-rk27xx.c')
-rw-r--r-- | firmware/target/arm/rk27xx/usb-drv-rk27xx.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c new file mode 100644 index 0000000000..a8b1bea1fe --- /dev/null +++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c | |||
@@ -0,0 +1,733 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Marcin Bukat | ||
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 | #include "usb-target.h" | ||
25 | #include "usb_drv.h" | ||
26 | |||
27 | #include "cpu.h" | ||
28 | #include "system.h" | ||
29 | #include "kernel.h" | ||
30 | #include "panic.h" | ||
31 | |||
32 | //#include "usb-s3c6400x.h" | ||
33 | |||
34 | #include "usb_ch9.h" | ||
35 | #include "usb_core.h" | ||
36 | #include <inttypes.h> | ||
37 | #include "power.h" | ||
38 | |||
39 | #include "logf.h" | ||
40 | |||
41 | typedef volatile uint32_t reg32; | ||
42 | |||
43 | /* Bulk OUT: ep1, ep4, ep7, ep10, ep13 */ | ||
44 | #define BOUT_RXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x54+0x38*(ep_num/3))) | ||
45 | #define BOUT_RXCON(ep_num) (*(reg32*)(AHB0_UDC+0x58+0x38*(ep_num/3))) | ||
46 | #define BOUT_DMAOUTCTL(ep_num) (*(reg32*)(AHB0_UDC+0x5C+0x38*(ep_num/3))) | ||
47 | #define BOUT_DMAOUTLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x60+0x38*(ep_num/3))) | ||
48 | |||
49 | /* Bulk IN: ep2, ep5, ep8, ep11, ep4 */ | ||
50 | #define BIN_TXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x64+0x38*(ep_num/3))) | ||
51 | #define BIN_TXCON(ep_num) (*(reg32*)(AHB0_UDC+0x68+0x38*(ep_num/3))) | ||
52 | #define BIN_TXBUF(ep_num) (*(reg32*)(AHB0_UDC+0x6C+0x38*(ep_num/3))) | ||
53 | #define BIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x70+0x38*(ep_num/3))) | ||
54 | #define BIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x74+0x38*(ep_num/3))) | ||
55 | |||
56 | /* INTERRUPT IN: ep3, ep6, ep9, ep12, ep15 */ | ||
57 | #define IIN_TXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x78+0x38*((ep_num/3)-1))) | ||
58 | #define IIN_TXCON(ep_num) (*(reg32*)(AHB0_UDC+0x7C+0x38*((ep_num/3)-1))) | ||
59 | #define IIN_TXBUF(ep_num) (*(reg32*)(AHB0_UDC+0x80+0x38*((ep_num/3)-1))) | ||
60 | #define IIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x84+0x38*((ep_num/3)-1))) | ||
61 | #define IIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x88+0x38*((ep_num/3)-1))) | ||
62 | |||
63 | #ifdef LOGF_ENABLE | ||
64 | #define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT") | ||
65 | #endif | ||
66 | |||
67 | struct endpoint_t { | ||
68 | const int type; /* EP type */ | ||
69 | const int dir; /* DIR_IN/DIR_OUT */ | ||
70 | bool allocated; /* flag to mark EPs taken */ | ||
71 | volatile void *buf; /* tx/rx buffer address */ | ||
72 | volatile int len; /* size of the transfer (bytes) */ | ||
73 | volatile int cnt; /* number of bytes transfered/received */ | ||
74 | volatile bool block; /* flag indicating that transfer is blocking */ | ||
75 | struct semaphore complete; /* semaphore for blocking transfers */ | ||
76 | }; | ||
77 | |||
78 | static struct endpoint_t ctrlep[2] = { | ||
79 | {USB_ENDPOINT_XFER_CONTROL, DIR_OUT, true, NULL, 0, 0, true}, | ||
80 | {USB_ENDPOINT_XFER_CONTROL, DIR_IN, true, NULL, 0, 0, true} | ||
81 | }; | ||
82 | |||
83 | static struct endpoint_t endpoints[16] = { | ||
84 | {USB_ENDPOINT_XFER_CONTROL, 3, true, NULL, 0, 0, true}, /* stub */ | ||
85 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false}, /* BOUT1 */ | ||
86 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false}, /* BIN2 */ | ||
87 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false}, /* IIN3 */ | ||
88 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false}, /* BOUT4 */ | ||
89 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false}, /* BIN5 */ | ||
90 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false}, /* IIN6 */ | ||
91 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false}, /* BOUT7 */ | ||
92 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false}, /* BIN8 */ | ||
93 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false}, /* IIN9 */ | ||
94 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false}, /* BOUT10 */ | ||
95 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false}, /* BIN11 */ | ||
96 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false}, /* IIN12 */ | ||
97 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false}, /* BOUT13 */ | ||
98 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false}, /* BIN14 */ | ||
99 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false}, /* IIN15 */ | ||
100 | }; | ||
101 | |||
102 | static void setup_received(void) | ||
103 | { | ||
104 | static uint32_t setup_data[2]; | ||
105 | |||
106 | /* copy setup data from packet */ | ||
107 | setup_data[0] = SETUP1; | ||
108 | setup_data[1] = SETUP2; | ||
109 | |||
110 | /* clear all pending control transfers | ||
111 | * do we need this here? | ||
112 | */ | ||
113 | |||
114 | /* pass setup data to the upper layer */ | ||
115 | usb_core_control_request((struct usb_ctrlrequest*)setup_data); | ||
116 | } | ||
117 | |||
118 | /* service ep0 IN transaction */ | ||
119 | static void ctr_write(void) | ||
120 | { | ||
121 | int xfer_size = (ctrlep[DIR_IN].cnt > 64) ? 64 : ctrlep[DIR_IN].cnt; | ||
122 | unsigned int timeout = current_tick + HZ/10; | ||
123 | |||
124 | while (TX0BUF & (1<<0)) /* TX0FULL flag */ | ||
125 | { | ||
126 | if(TIME_AFTER(current_tick, timeout)) | ||
127 | break; | ||
128 | } | ||
129 | |||
130 | TX0STAT = xfer_size; /* size of the transfer */ | ||
131 | TX0DMALM_IADDR = (uint32_t)ctrlep[DIR_IN].buf; /* local buffer address */ | ||
132 | TX0DMAINCTL = (1<<1); /* start DMA */ | ||
133 | TX0CON &= ~(1<<2); /* clear NAK */ | ||
134 | |||
135 | /* Decrement by max packet size is intentional. | ||
136 | * This way if we have final packet short one we will get negative len | ||
137 | * after transfer, which in turn indicates we *don't* need to send | ||
138 | * zero length packet. If the final packet is max sized packet we will | ||
139 | * get zero len after transfer which indicates we need to send | ||
140 | * zero length packet to signal host end of the transfer. | ||
141 | */ | ||
142 | ctrlep[DIR_IN].cnt -= 64; | ||
143 | ctrlep[DIR_IN].buf += xfer_size; | ||
144 | } | ||
145 | |||
146 | static void ctr_read(void) | ||
147 | { | ||
148 | int xfer_size = RX0STAT & 0xffff; | ||
149 | |||
150 | /* clear NAK bit */ | ||
151 | RX0CON &= ~(1<<3); | ||
152 | |||
153 | ctrlep[DIR_OUT].cnt -= xfer_size; | ||
154 | ctrlep[DIR_OUT].buf += xfer_size; | ||
155 | |||
156 | RX0DMAOUTLMADDR = (uint32_t)ctrlep[DIR_OUT].buf; | ||
157 | RX0DMACTLO = (1<<0); | ||
158 | } | ||
159 | |||
160 | static void blk_write(int ep) | ||
161 | { | ||
162 | int ep_num = EP_NUM(ep); | ||
163 | int max = usb_drv_port_speed() ? 512 : 64; | ||
164 | int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; | ||
165 | unsigned int timeout = current_tick + HZ/10; | ||
166 | |||
167 | while (BIN_TXBUF(ep_num) & (1<<0)) /* TXFULL flag */ | ||
168 | { | ||
169 | if(TIME_AFTER(current_tick, timeout)) | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | BIN_TXSTAT(ep_num) = xfer_size; /* size of the transfer */ | ||
174 | BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ | ||
175 | BIN_DMAINCTL(ep_num) = (1<<0); /* start DMA */ | ||
176 | BIN_TXCON(ep_num) &= ~(1<<2); /* clear NAK */ | ||
177 | |||
178 | /* Decrement by max packet size is intentional. | ||
179 | * This way if we have final packet short one we will get negative len | ||
180 | * after transfer, which in turn indicates we *don't* need to send | ||
181 | * zero length packet. If the final packet is max sized packet we will | ||
182 | * get zero len after transfer which indicates we need to send | ||
183 | * zero length packet to signal host end of the transfer. | ||
184 | */ | ||
185 | endpoints[ep_num].cnt -= max; | ||
186 | endpoints[ep_num].buf += xfer_size; | ||
187 | } | ||
188 | |||
189 | static void blk_read(int ep) | ||
190 | { | ||
191 | int ep_num = EP_NUM(ep); | ||
192 | int xfer_size = BOUT_RXSTAT(ep_num) & 0xffff; | ||
193 | |||
194 | /* clear NAK bit */ | ||
195 | BOUT_RXCON(ep_num) &= ~(1<<3); | ||
196 | |||
197 | endpoints[ep_num].cnt -= xfer_size; | ||
198 | endpoints[ep_num].buf += xfer_size; | ||
199 | |||
200 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; | ||
201 | BOUT_DMAOUTCTL(ep_num) = (1<<1); | ||
202 | } | ||
203 | |||
204 | static void int_write(int ep) | ||
205 | { | ||
206 | int ep_num = EP_NUM(ep); | ||
207 | int max = usb_drv_port_speed() ? 1024 : 64; | ||
208 | int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; | ||
209 | unsigned int timeout = current_tick + HZ/10; | ||
210 | |||
211 | while (IIN_TXBUF(ep_num) & (1<<0)) /* TXFULL flag */ | ||
212 | { | ||
213 | if(TIME_AFTER(current_tick, timeout)) | ||
214 | break; | ||
215 | } | ||
216 | |||
217 | IIN_TXSTAT(ep_num) = xfer_size; /* size of the transfer */ | ||
218 | IIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ | ||
219 | IIN_DMAINCTL(ep_num) = (1<<0); /* start DMA */ | ||
220 | IIN_TXCON(ep_num) &= ~(1<<2); /* clear NAK */ | ||
221 | |||
222 | /* Decrement by max packet size is intentional. | ||
223 | * This way if we have final packet short one we will get negative len | ||
224 | * after transfer, which in turn indicates we *don't* need to send | ||
225 | * zero length packet. If the final packet is max sized packet we will | ||
226 | * get zero len after transfer which indicates we need to send | ||
227 | * zero length packet to signal host end of the transfer. | ||
228 | */ | ||
229 | endpoints[ep_num].cnt -= max; | ||
230 | endpoints[ep_num].buf += xfer_size; | ||
231 | } | ||
232 | |||
233 | /* UDC ISR function */ | ||
234 | void INT_UDC(void) | ||
235 | { | ||
236 | uint32_t txstat, rxstat; | ||
237 | int tmp, ep_num; | ||
238 | |||
239 | /* read what caused UDC irq */ | ||
240 | uint32_t intsrc = INT2FLAG & 0x7fffff; | ||
241 | |||
242 | if (intsrc & (1<<1)) /* setup interrupt */ | ||
243 | { | ||
244 | setup_received(); | ||
245 | } | ||
246 | else if (intsrc & (1<<2)) /* ep0 in interrupt */ | ||
247 | { | ||
248 | txstat = TX0STAT; /* read clears flags */ | ||
249 | |||
250 | /* TODO handle errors */ | ||
251 | if (txstat & (1<<18)) /* check TxACK flag */ | ||
252 | { | ||
253 | if (ctrlep[DIR_IN].cnt >= 0) | ||
254 | { | ||
255 | /* we still have data to send (or ZLP) */ | ||
256 | ctr_write(); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | /* final ack received */ | ||
261 | usb_core_transfer_complete(0, /* ep */ | ||
262 | USB_DIR_IN, /* dir */ | ||
263 | 0, /* status */ | ||
264 | ctrlep[DIR_IN].len); /* length */ | ||
265 | |||
266 | /* release semaphore for blocking transfer */ | ||
267 | if (ctrlep[DIR_IN].block) | ||
268 | semaphore_release(&ctrlep[DIR_IN].complete); | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | else if (intsrc & (1<<3)) /* ep0 out interrupt */ | ||
273 | { | ||
274 | rxstat = RX0STAT; | ||
275 | |||
276 | /* TODO handle errors */ | ||
277 | if (rxstat & (1<<18)) /* RxACK */ | ||
278 | { | ||
279 | if (ctrlep[DIR_OUT].cnt > 0) | ||
280 | ctr_read(); | ||
281 | else | ||
282 | usb_core_transfer_complete(0, /* ep */ | ||
283 | USB_DIR_OUT, /* dir */ | ||
284 | 0, /* status */ | ||
285 | ctrlep[DIR_OUT].len); /* length */ | ||
286 | } | ||
287 | } | ||
288 | else if (intsrc & (1<<4)) /* usb reset */ | ||
289 | { | ||
290 | usb_drv_init(); | ||
291 | } | ||
292 | else if (intsrc & (1<<5)) /* usb resume */ | ||
293 | { | ||
294 | TX0CON |= (1<<0); /* TxClr */ | ||
295 | TX0CON &= ~(1<<0); | ||
296 | RX0CON |= (1<<1); /* RxClr */ | ||
297 | RX0CON &= (1<<1); | ||
298 | } | ||
299 | else if (intsrc & (1<<6)) /* usb suspend */ | ||
300 | { | ||
301 | } | ||
302 | else if (intsrc & (1<<7)) /* usb connect */ | ||
303 | { | ||
304 | } | ||
305 | else | ||
306 | { | ||
307 | /* lets figure out which ep generated irq */ | ||
308 | tmp = intsrc >> 7; | ||
309 | for (ep_num=1; ep_num < 15; ep_num++) | ||
310 | { | ||
311 | tmp >>= ep_num; | ||
312 | if (tmp & 0x01) | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | if (intsrc & ((1<<8)|(1<<11)|(1<<14)|(1<<17)|(1<<20))) | ||
317 | { | ||
318 | /* bulk out */ | ||
319 | rxstat = BOUT_RXSTAT(ep_num); | ||
320 | |||
321 | /* TODO handle errors */ | ||
322 | if (rxstat & (1<<18)) /* RxACK */ | ||
323 | { | ||
324 | if (endpoints[ep_num].cnt > 0) | ||
325 | blk_read(ep_num); | ||
326 | else | ||
327 | usb_core_transfer_complete(ep_num, /* ep */ | ||
328 | USB_DIR_OUT, /* dir */ | ||
329 | 0, /* status */ | ||
330 | endpoints[ep_num].len); /* length */ | ||
331 | } | ||
332 | } | ||
333 | else if (intsrc & ((1<<9)|(1<<12)|(1<<15)|(1<<18)|(1<<21))) | ||
334 | { | ||
335 | /* bulk in */ | ||
336 | txstat = BIN_TXSTAT(ep_num); | ||
337 | |||
338 | /* TODO handle errors */ | ||
339 | if (txstat & (1<<18)) /* check TxACK flag */ | ||
340 | { | ||
341 | if (endpoints[ep_num].cnt >= 0) | ||
342 | { | ||
343 | /* we still have data to send (or ZLP) */ | ||
344 | blk_write(ep_num); | ||
345 | } | ||
346 | else | ||
347 | { | ||
348 | /* final ack received */ | ||
349 | usb_core_transfer_complete(ep_num, /* ep */ | ||
350 | USB_DIR_IN, /* dir */ | ||
351 | 0, /* status */ | ||
352 | endpoints[ep_num].len); /* length */ | ||
353 | |||
354 | /* release semaphore for blocking transfer */ | ||
355 | if (endpoints[ep_num].block) | ||
356 | semaphore_release(&endpoints[ep_num].complete); | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | else if (intsrc & ((1<<10)|(1<13)|(1<<16)|(1<<19)|(1<<22))) | ||
361 | { | ||
362 | /* int in */ | ||
363 | txstat = IIN_TXSTAT(ep_num); | ||
364 | |||
365 | /* TODO handle errors */ | ||
366 | if (txstat & (1<<18)) /* check TxACK flag */ | ||
367 | { | ||
368 | if (endpoints[ep_num].cnt >= 0) | ||
369 | { | ||
370 | /* we still have data to send (or ZLP) */ | ||
371 | int_write(ep_num); | ||
372 | } | ||
373 | else | ||
374 | { | ||
375 | /* final ack received */ | ||
376 | usb_core_transfer_complete(ep_num, /* ep */ | ||
377 | USB_DIR_IN, /* dir */ | ||
378 | 0, /* status */ | ||
379 | endpoints[ep_num].len); /* length */ | ||
380 | |||
381 | /* release semaphore for blocking transfer */ | ||
382 | if (endpoints[ep_num].block) | ||
383 | semaphore_release(&endpoints[ep_num].complete); | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | /* return port speed FS=0, HS=1 */ | ||
391 | int usb_drv_port_speed(void) | ||
392 | { | ||
393 | return ((DEV_INFO & (3<<21)) == 0) ? 0 : 1; | ||
394 | } | ||
395 | |||
396 | /* Reserve endpoint */ | ||
397 | int usb_drv_request_endpoint(int type, int dir) | ||
398 | { | ||
399 | int ep_num, ep_dir; | ||
400 | int ep_type; | ||
401 | |||
402 | /* Safety */ | ||
403 | ep_dir = EP_DIR(dir); | ||
404 | ep_type = type & USB_ENDPOINT_XFERTYPE_MASK; | ||
405 | |||
406 | logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type)); | ||
407 | |||
408 | /* Find an available ep/dir pair */ | ||
409 | for (ep_num=1;ep_num<USB_NUM_ENDPOINTS;ep_num++) | ||
410 | { | ||
411 | struct endpoint_t* endpoint = &endpoints[ep_num]; | ||
412 | |||
413 | if (endpoint->type == ep_type && | ||
414 | endpoint->dir == ep_dir && | ||
415 | !endpoint->allocated) | ||
416 | { | ||
417 | /* mark endpoint as taken */ | ||
418 | endpoint->allocated = true; | ||
419 | |||
420 | /* enable interrupt from this endpoint */ | ||
421 | EN_INT |= (1<<(ep_num+7)); | ||
422 | |||
423 | logf("add: ep%d %s", ep_num, XFER_DIR_STR(ep_dir)); | ||
424 | return (ep_num | (dir & USB_ENDPOINT_DIR_MASK)); | ||
425 | } | ||
426 | } | ||
427 | return -1; | ||
428 | } | ||
429 | |||
430 | /* Free endpoint */ | ||
431 | void usb_drv_release_endpoint(int ep) | ||
432 | { | ||
433 | int ep_num = EP_NUM(ep); | ||
434 | int ep_dir = EP_DIR(ep); | ||
435 | |||
436 | logf("rel: ep%d %s", ep_num, XFER_DIR_STR(ep_dir)); | ||
437 | endpoints[ep_num].allocated = false; | ||
438 | |||
439 | /* disable interrupt from this endpoint */ | ||
440 | EN_INT &= ~(1<<(ep_num+7)); | ||
441 | } | ||
442 | |||
443 | /* Set the address (usually it's in a register). | ||
444 | * There is a problem here: some controller want the address to be set between | ||
445 | * control out and ack and some want to wait for the end of the transaction. | ||
446 | * In the first case, you need to write some code special code when getting | ||
447 | * setup packets and ignore this function (have a look at other drives) | ||
448 | */ | ||
449 | void usb_drv_set_address(int address) | ||
450 | { | ||
451 | (void)address; | ||
452 | /* UDC seems to set this automaticaly */ | ||
453 | } | ||
454 | |||
455 | static int _usb_drv_send(int endpoint, void *ptr, int length, bool block) | ||
456 | { | ||
457 | struct endpoint_t *ep; | ||
458 | int ep_num = EP_NUM(endpoint); | ||
459 | |||
460 | if (ep_num == 0) | ||
461 | ep = &ctrlep[DIR_IN]; | ||
462 | else | ||
463 | ep = &endpoints[ep_num]; | ||
464 | |||
465 | ep->buf = ptr; | ||
466 | ep->len = ep->cnt = length; | ||
467 | |||
468 | if (block) | ||
469 | ep->block = true; | ||
470 | else | ||
471 | ep->block = false; | ||
472 | |||
473 | switch (ep->type) | ||
474 | { | ||
475 | case USB_ENDPOINT_XFER_CONTROL: | ||
476 | ctr_write(); | ||
477 | break; | ||
478 | |||
479 | case USB_ENDPOINT_XFER_BULK: | ||
480 | blk_write(ep_num); | ||
481 | break; | ||
482 | |||
483 | case USB_ENDPOINT_XFER_INT: | ||
484 | int_write(ep_num); | ||
485 | break; | ||
486 | } | ||
487 | |||
488 | if (block) | ||
489 | /* wait for transfer to end */ | ||
490 | semaphore_wait(&ep->complete, TIMEOUT_BLOCK); | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | /* Setup a send transfer. (blocking) */ | ||
496 | int usb_drv_send(int endpoint, void *ptr, int length) | ||
497 | { | ||
498 | return _usb_drv_send(endpoint, ptr, length, true); | ||
499 | } | ||
500 | |||
501 | /* Setup a send transfer. (non blocking) */ | ||
502 | int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) | ||
503 | { | ||
504 | return _usb_drv_send(endpoint, ptr, length, false); | ||
505 | } | ||
506 | |||
507 | /* Setup a receive transfer. (non blocking) */ | ||
508 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
509 | { | ||
510 | struct endpoint_t *ep; | ||
511 | int ep_num = EP_NUM(endpoint); | ||
512 | |||
513 | if (ep_num == 0) | ||
514 | { | ||
515 | ep = &ctrlep[DIR_OUT]; | ||
516 | |||
517 | ctr_read(); | ||
518 | } | ||
519 | else | ||
520 | { | ||
521 | ep = &endpoints[ep_num]; | ||
522 | |||
523 | /* clear NAK bit */ | ||
524 | BOUT_RXCON(ep_num) &= ~(1<<3); | ||
525 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr; | ||
526 | BOUT_DMAOUTCTL(ep_num) = (1<<1); | ||
527 | } | ||
528 | |||
529 | ep->buf = ptr; | ||
530 | ep->len = ep->cnt = length; | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | /* Kill all transfers. Usually you need to set a bit for each endpoint | ||
536 | * and flush fifos. You should also call the completion handler with | ||
537 | * error status for everything | ||
538 | */ | ||
539 | void usb_drv_cancel_all_transfers(void) | ||
540 | { | ||
541 | } | ||
542 | |||
543 | /* Set test mode, you can forget that for now, usually it's sufficient | ||
544 | * to bit copy the argument into some register of the controller | ||
545 | */ | ||
546 | void usb_drv_set_test_mode(int mode) | ||
547 | { | ||
548 | (void)mode; | ||
549 | } | ||
550 | |||
551 | /* Check if endpoint is in stall state */ | ||
552 | bool usb_drv_stalled(int endpoint, bool in) | ||
553 | { | ||
554 | int ep_num = EP_NUM(endpoint); | ||
555 | |||
556 | switch (endpoints[ep_num].type) | ||
557 | { | ||
558 | case USB_ENDPOINT_XFER_CONTROL: | ||
559 | if (in) | ||
560 | return (TX0CON & (1<<1)) ? true : false; | ||
561 | else | ||
562 | return (RX0CON & (1<<2)) ? true : false; | ||
563 | |||
564 | break; | ||
565 | |||
566 | case USB_ENDPOINT_XFER_BULK: | ||
567 | if (in) | ||
568 | return (BIN_TXCON(ep_num) & (1<<1)) ? true : false; | ||
569 | else | ||
570 | return (BOUT_RXCON(ep_num) & (1<<2)) ? true : false; | ||
571 | |||
572 | break; | ||
573 | |||
574 | case USB_ENDPOINT_XFER_INT: | ||
575 | if (in) | ||
576 | return (IIN_TXCON(ep_num) & (1<<1)) ? true : false; | ||
577 | else | ||
578 | return false; /* we don't have such endpoint anyway */ | ||
579 | |||
580 | break; | ||
581 | } | ||
582 | |||
583 | return false; | ||
584 | } | ||
585 | |||
586 | /* Stall the endpoint. Usually set a flag in the controller */ | ||
587 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
588 | { | ||
589 | int ep_num = EP_NUM(endpoint); | ||
590 | |||
591 | switch (endpoints[ep_num].type) | ||
592 | { | ||
593 | case USB_ENDPOINT_XFER_CONTROL: | ||
594 | if (in) | ||
595 | { | ||
596 | if (stall) | ||
597 | TX0CON |= (1<<1); | ||
598 | else | ||
599 | TX0CON &= ~(1<<1); | ||
600 | } | ||
601 | else | ||
602 | { | ||
603 | if (stall) | ||
604 | RX0CON |= (1<<2); | ||
605 | else | ||
606 | RX0CON &= ~(1<<2); /* doc says Auto clear by UDC 2.0 */ | ||
607 | } | ||
608 | break; | ||
609 | |||
610 | case USB_ENDPOINT_XFER_BULK: | ||
611 | if (in) | ||
612 | { | ||
613 | if (stall) | ||
614 | BIN_TXCON(ep_num) |= (1<<1); | ||
615 | else | ||
616 | BIN_TXCON(ep_num) &= ~(1<<1); | ||
617 | } | ||
618 | else | ||
619 | { | ||
620 | if (stall) | ||
621 | BOUT_RXCON(ep_num) |= (1<<2); | ||
622 | else | ||
623 | BOUT_RXCON(ep_num) &= ~(1<<2); | ||
624 | } | ||
625 | break; | ||
626 | |||
627 | case USB_ENDPOINT_XFER_INT: | ||
628 | if (in) | ||
629 | { | ||
630 | if (stall) | ||
631 | IIN_TXCON(ep_num) |= (1<<1); | ||
632 | else | ||
633 | IIN_TXCON(ep_num) &= ~(1<<1); | ||
634 | } | ||
635 | break; | ||
636 | } | ||
637 | } | ||
638 | |||
639 | /* one time init (once per connection) - basicaly enable usb core */ | ||
640 | void usb_drv_init(void) | ||
641 | { | ||
642 | int ep_num; | ||
643 | |||
644 | /* enable USB clock */ | ||
645 | SCU_CLKCFG &= ~(1<<6); | ||
646 | |||
647 | /* 1. do soft disconnect */ | ||
648 | DEV_CTL = (1<<3); /* DEV_SELF_PWR */ | ||
649 | |||
650 | /* 2. do power on reset to PHY */ | ||
651 | DEV_CTL = (1<<3) | /* DEV_SELF_PWR */ | ||
652 | (1<<7); /* SOFT_POR */ | ||
653 | |||
654 | /* 3. wait more than 10ms */ | ||
655 | udelay(20000); | ||
656 | |||
657 | /* 4. clear SOFT_POR bit */ | ||
658 | DEV_CTL &= ~(1<<7); | ||
659 | |||
660 | /* 5. configure minimal EN_INT */ | ||
661 | EN_INT = (1<<6) | /* Enable Suspend Interrupt */ | ||
662 | (1<<5) | /* Enable Resume Interrupt */ | ||
663 | (1<<4) | /* Enable USB Reset Interrupt */ | ||
664 | (1<<3) | /* Enable OUT Token receive Interrupt EP0 */ | ||
665 | (1<<2) | /* Enable IN Token transmits Interrupt EP0 */ | ||
666 | (1<<1); /* Enable SETUP Packet Receive Interrupt */ | ||
667 | |||
668 | /* 6. configure INTCON */ | ||
669 | INTCON = (1<<2) | /* interrupt high active */ | ||
670 | (1<<0); /* enable EP0 interrupts */ | ||
671 | |||
672 | /* 7. configure EP0 control registers */ | ||
673 | TX0CON = (1<<6) | /* Set as one to enable the EP0 tx irq */ | ||
674 | (1<<2); /* Set as one to response NAK handshake */ | ||
675 | |||
676 | RX0CON = (1<<7) | | ||
677 | (1<<4) | /* Endpoint 0 Enable. When cleared the endpoint does | ||
678 | * not respond to an SETUP or OUT token | ||
679 | */ | ||
680 | |||
681 | (1<<3); /* Set as one to response NAK handshake */ | ||
682 | |||
683 | /* 8. write final bits to DEV_CTL */ | ||
684 | DEV_CTL = (1<<8) | /* Configure CSR done */ | ||
685 | (1<<6) | /* 16-bit data path enabled. udc_clk = 30MHz */ | ||
686 | (1<<4) | /* Device soft connect */ | ||
687 | (1<<3); /* Device self power */ | ||
688 | |||
689 | /* init semaphore of ep0 */ | ||
690 | semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0); | ||
691 | semaphore_init(&ctrlep[DIR_IN].complete, 1, 0); | ||
692 | |||
693 | for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) | ||
694 | { | ||
695 | semaphore_init(&endpoints[ep_num].complete, 1, 0); | ||
696 | |||
697 | if (ep_num%3 == 0) /* IIN 3, 6, 9, 12, 15 */ | ||
698 | { | ||
699 | IIN_TXCON(ep_num) |= (ep_num<<8)|(1<<3)|(1<<2); /* ep_num, enable, NAK */ | ||
700 | } | ||
701 | else if (ep_num%3 == 1) /* BOUT 1, 4, 7, 10, 13 */ | ||
702 | { | ||
703 | BOUT_RXCON(ep_num) |= (ep_num<<8)|(1<<4)|(1<<3); /* ep_num, NAK, enable */ | ||
704 | } | ||
705 | else if (ep_num%3 == 2) /* BIN 2, 5, 8, 11, 14 */ | ||
706 | { | ||
707 | BIN_TXCON(ep_num) |= (ep_num<<8)|(1<<3)|(1<<2); /* ep_num, enable, NAK */ | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | /* turn off usb core */ | ||
713 | void usb_drv_exit(void) | ||
714 | { | ||
715 | DEV_CTL = (1<<3); /* DEV_SELF_PWR */ | ||
716 | |||
717 | /* disable USB interrupts in interrupt controller */ | ||
718 | INTC_IMR &= ~(1<<16); | ||
719 | INTC_IECR &= ~(1<<16); | ||
720 | |||
721 | /* we cannot disable UDC clock since this causes data abort | ||
722 | * when reading DEV_INFO in order to check usb connect event | ||
723 | */ | ||
724 | } | ||
725 | |||
726 | int usb_detect(void) | ||
727 | { | ||
728 | if (DEV_INFO & (1<<20)) | ||
729 | return USB_INSERTED; | ||
730 | else | ||
731 | return USB_EXTRACTED; | ||
732 | } | ||
733 | |||