blob: 5bb9906c2600b17b00601840135662a5effac506 [file] [log] [blame]
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -07001/*
2 * Copyright (c) 2008 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
Travis Geiselbrecht7689f352008-10-10 03:18:36 -070024#include <string.h>
25#include <stdlib.h>
26#include <kernel/thread.h>
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -070027#include <dev/usbc.h>
28#include <dev/twl4030.h>
Travis Geiselbrecht7689f352008-10-10 03:18:36 -070029#include <reg.h>
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -070030#include <platform/omap3.h>
Travis Geiselbrecht7689f352008-10-10 03:18:36 -070031#include <platform/interrupts.h>
32#include <hw/usb.h>
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -070033
Travis Geiselbrecht7689f352008-10-10 03:18:36 -070034#define LOCAL_TRACE 0
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -070035
36#define hsusb_reg8(reg) *REG8(USB_HS_BASE + (reg))
37#define hsusb_reg16(reg) *REG16(USB_HS_BASE + (reg))
38#define hsusb_reg32(reg) *REG32(USB_HS_BASE + (reg))
39
40/* registers */
41#define FADDR 0x0
42#define POWER 0x1
43#define INTRTX 0x2
44#define INTRRX 0x4
45#define INTRTXE 0x6
46#define INTRRXE 0x8
47#define INTRUSB 0xa
48#define INTRUSBE 0xb
49#define FRAME 0xc
50#define INDEX 0xe
51#define TESTMODE 0xf
52
53// indexed endpoint regs
54#define IDX_TXMAXP 0x10
55#define IDX_TXCSR 0x12
56#define IDX_TXCSRL 0x12
57#define IDX_TXCSRH 0x13
58#define IDX_RXMAXP 0x14
59#define IDX_RXCSR 0x16
60#define IDX_RXCSRL 0x16
61#define IDX_RXCSRH 0x17
62#define IDX_RXCOUNT 0x18
63#define IDX_FIFOSIZE 0x1f
64
65// if endpoint 0 is selected
66#define IDX_CSR0 0x12
67#define IDX_CONFIGDATA 0x1f
68
69// endpoint FIFOs
70#define FIFOBASE 0x20
71
72#define DEVCTL 0x60
73#define TXFIFOSZ 0x62
74#define RXFIFOSZ 0x63
75#define TXFIFOADD 0x64
76#define RXFIFOADD 0x66
77#define HWVERS 0x6c
78#define EPINFO 0x78
79#define RAMINFO 0x79
80#define LINKINFO 0x7a
81
Travis Geiselbrecht7689f352008-10-10 03:18:36 -070082static void setup_dynamic_fifos(void);
83
84enum usb_state {
85 USB_DEFAULT = 0,
86 USB_ADDRESS,
87 USB_CONFIGURED
88};
89
90struct usbc_ep {
91 bool active;
92 uint width;
93 uint blocksize;
94
95 /* current data buffer */
96 usbc_transfer *transfer;
97
98 /* callback when tx or rx happens on the endpoint */
99 int (*callback)(ep_t endpoint, usbc_callback_op_t op, usbc_transfer *transfer);
100};
101
102struct usbc_stat {
103 bool active;
104 enum usb_state state;
105 uint8_t active_config;
106
107 // callback for device events
108 usb_callback callback;
109
110 // ep0 pending tx
111 const void *ep0_tx_buf;
112 size_t ep0_tx_len;
113 uint ep0_tx_pos;
114
115 struct usbc_ep inep[16]; // IN endpoints (device to host)
116 struct usbc_ep outep[16]; // OUT endpoint (host to device)
117};
118
119static struct usbc_stat *usbc;
120
121struct usbc_callback {
122 struct list_node node;
123 usb_callback callback;
124};
125
126static struct list_node usbc_callback_list;
127
128static void call_all_callbacks(usbc_callback_op_t op, const union usb_callback_args *arg)
129{
130 struct usbc_callback *cb;
131
132 list_for_every_entry(&usbc_callback_list, cb, struct usbc_callback, node) {
133 LTRACEF("calling %p, op %d, arg %p\n", cb->callback, op, arg);
134 cb->callback(op, arg);
135 }
136}
137
138static void print_usb_setup(const struct usb_setup *setup)
139{
140 printf("usb_setup:\n");
141 printf("\ttype 0x%hhx\n", setup->request_type);
142 printf("\trequest 0x%hhx\n", setup->request);
143 printf("\tvalue 0x%hx\n", setup->value);
144 printf("\tindex 0x%hx\n", setup->index);
145 printf("\tlength 0x%hx\n", setup->length);
146}
147
148static void select_ep(uint ep)
149{
150 DEBUG_ASSERT(ep < 16);
151 hsusb_reg8(INDEX) = ep;
152}
153
154static void dump_ep_regs(uint ep)
155{
156#if 0
157 select_ep(ep);
158
159 LTRACEF("%d txmaxp 0x%hx\n", ep, hsusb_reg16(IDX_TXMAXP));
160 LTRACEF("%d rxmaxp 0x%hx\n", ep, hsusb_reg16(IDX_RXMAXP));
161 LTRACEF("%d txfifosz 0x%hhx\n", ep, hsusb_reg8(TXFIFOSZ));
162 LTRACEF("%d rxfifosz 0x%hhx\n", ep, hsusb_reg8(RXFIFOSZ));
163 LTRACEF("%d txfifoadd 0x%hx\n", ep, hsusb_reg16(TXFIFOADD));
164 LTRACEF("%d rxfifoadd 0x%hx\n", ep, hsusb_reg16(RXFIFOADD));
165#endif
166}
167
168#define MULOF4(val) (((uint32_t)(val) & 0x3) == 0)
169
170static int read_ep_fifo(uint ep, void *_buf, size_t maxlen)
171{
172 char *buf = (void *)_buf;
173
174 select_ep(ep);
175
176 uint8_t fifo_reg = FIFOBASE + ep * 4;
177 size_t rxcount = hsusb_reg16(IDX_RXCOUNT);
178
179 if (rxcount > maxlen)
180 rxcount = maxlen;
181
182 if (MULOF4(buf) && MULOF4(rxcount)) {
183 uint i;
184 uint32_t *buf32 = (uint32_t *)_buf;
185 for (i=0; i < rxcount / 4; i++) {
186 buf32[i] = hsusb_reg32(fifo_reg);
187 }
188 } else {
189 /* slow path */
190 uint i;
191 for (i=0; i < rxcount; i++) {
192 buf[i] = hsusb_reg8(fifo_reg);
193 }
194 }
195
196 return rxcount;
197}
198
199static int write_ep_fifo(uint ep, const void *_buf, size_t len)
200{
201 char *buf = (void *)_buf;
202
203 select_ep(ep);
204
205 uint8_t fifo_reg = FIFOBASE + ep * 4;
206
207 if (MULOF4(buf) && MULOF4(len)) {
208 uint i;
209 uint32_t *buf32 = (uint32_t *)_buf;
210 for (i=0; i < len / 4; i++) {
211 hsusb_reg32(fifo_reg) = buf32[i];
212 }
213 } else {
214 /* slow path */
215 uint i;
216 for (i=0; i < len; i++) {
217 hsusb_reg8(fifo_reg) = buf[i];
218 }
219 }
220
221 return len;
222}
223
224#undef MULOF4
225
226void usbc_ep0_send(const void *buf, size_t len, size_t maxlen)
227{
228 LTRACEF("buf %p, len %zu, maxlen %zu\n", buf, len, maxlen);
229
230 // trim the transfer
231 len = MIN(len, maxlen);
232
233 size_t transfer_len = MIN(64, len);
234
235 // write the first 64 bytes
236 write_ep_fifo(0, buf, transfer_len);
237
238 // set txpktready
239 select_ep(0);
240 if (len > 64) {
241 // we have more data to send, don't mark data end
242 hsusb_reg16(IDX_CSR0) |= (1<<1); // TxPktRdy
243
244 // save our position so we can continue
245 usbc->ep0_tx_buf = buf;
246 usbc->ep0_tx_pos = 64;
247 usbc->ep0_tx_len = len;
248 } else {
249 hsusb_reg16(IDX_CSR0) |= (1<<3) | (1<<1); // DataEnd, TxPktRdy
250 usbc->ep0_tx_buf = NULL;
251 }
252}
253
254static void ep0_control_send_resume(void)
255{
256 DEBUG_ASSERT(usbc->ep0_tx_buf != NULL);
257 DEBUG_ASSERT(usbc->ep0_tx_len > usbc->ep0_tx_pos);
258
259 LTRACEF("buf %p pos %d len %d\n", usbc->ep0_tx_buf, usbc->ep0_tx_pos, usbc->ep0_tx_len);
260
261 size_t transfer_len = MIN(64, usbc->ep0_tx_len - usbc->ep0_tx_pos);
262
263 write_ep_fifo(0, (const uint8_t *)usbc->ep0_tx_buf + usbc->ep0_tx_pos, transfer_len);
264
265 usbc->ep0_tx_pos += transfer_len;
266
267 if (usbc->ep0_tx_pos >= usbc->ep0_tx_len) {
268 // completes the transfer
269 hsusb_reg16(IDX_CSR0) |= (1<<3) | (1<<1); // DataEnd, TxPktRdy
270 usbc->ep0_tx_buf = NULL;
271 } else {
272 hsusb_reg16(IDX_CSR0) |= (1<<1); // TxPktRdy
273 }
274}
275
276void usbc_ep0_ack(void)
277{
278 hsusb_reg16(IDX_CSR0) |= (1<<6)|(1<<3); // servicedrxpktrdy & dataend
279}
280
281void usbc_ep0_stall(void)
282{
283 printf("USB STALL\n");
284}
285
286static void usb_shutdown_endpoints(void)
287{
288 // iterate through all the endpoints, cancelling any pending io and shut down the endpoint
289 ep_t i;
290 for (i=1; i < 16; i++) {
291 if (usbc->inep[i].active && usbc->inep[i].transfer) {
292 // pool's closed
293 usbc_transfer *t = usbc->inep[i].transfer;
294 usbc->inep[i].transfer = NULL;
295 t->result = USB_TRANSFER_RESULT_CANCELLED;
296 usbc->inep[i].callback(i, CB_EP_TRANSFER_CANCELLED, t);
297 }
298 if (usbc->outep[i].active && usbc->outep[i].transfer) {
299 // pool's closed
300 usbc_transfer *t = usbc->outep[i].transfer;
301 usbc->outep[i].transfer = NULL;
302 t->result = USB_TRANSFER_RESULT_CANCELLED;
303 usbc->outep[i].callback(i, CB_EP_TRANSFER_CANCELLED, t);
304 }
305 }
306
307 // clear pending ep0 data
308 usbc->ep0_tx_buf = 0;
309}
310
311static void usb_enable_endpoints(void)
312{
313 setup_dynamic_fifos();
314}
315
316static void usb_disconnect(void)
317{
318 // we've been disconnected
319 usbc->state = USB_DEFAULT;
320 usbc->active_config = 0;
321
322 usb_shutdown_endpoints();
323}
324
325static void usb_reset(void)
326{
327 // this wipes out our endpoint interrupt disables
328 hsusb_reg16(INTRTXE) = (1<<0);
329 hsusb_reg16(INTRRXE) = 0;
330
331 usb_shutdown_endpoints();
332}
333
334static int handle_ep_rx(int ep)
335{
336 struct usbc_ep *e = &usbc->outep[ep];
337
338 DEBUG_ASSERT(e->active);
339
340 DEBUG_ASSERT(e->transfer); // can't rx to no transfer
341 usbc_transfer *t = e->transfer;
342
343 uint rxcount = hsusb_reg16(IDX_RXCOUNT);
344 uint readcount = MIN(rxcount, t->buflen - t->bufpos);
345 readcount = MIN(readcount, e->blocksize);
346
347 int len = read_ep_fifo(ep, (uint8_t *)t->buf + t->bufpos, readcount);
348 LTRACEF("read %d bytes from the fifo\n", len);
349
350 // no more packet ready
351 hsusb_reg16(IDX_RXCSRL) &= ~(1<<0); // clear rxpktrdy
352
353 t->bufpos += len;
354
355 if (rxcount < e->blocksize || t->bufpos >= t->buflen) {
356 // we're done with this transfer, clear it and disable the endpoint
357 e->transfer = NULL;
358 hsusb_reg16(INTRRXE) &= ~(1<<ep);
359
360 t->result = USB_TRANSFER_RESULT_OK;
361
362 DEBUG_ASSERT(e->callback);
363 e->callback(ep, CB_EP_RXCOMPLETE, t);
364
365 return 1;
366 }
367
368 return 0;
369}
370
371bool usbc_is_highspeed(void)
372{
373 return (hsusb_reg8(POWER) & (1<<4)) ? true : false;
374}
375
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700376static enum handler_return hsusb_interrupt(void *arg)
377{
378 uint16_t intrtx = hsusb_reg16(INTRTX);
379 uint16_t intrrx = hsusb_reg16(INTRRX);
380 uint8_t intrusb = hsusb_reg8(INTRUSB);
381 enum handler_return ret = INT_NO_RESCHEDULE;
382
383 LTRACEF("intrtx 0x%hx (0x%x), intrrx 0x%hx (0x%x), intrusb 0x%hhx, intrusbe 0x%hhx\n",
384 intrtx, hsusb_reg16(INTRTXE), intrrx, hsusb_reg16(INTRRXE), intrusb, hsusb_reg8(INTRUSBE));
385
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700386 dump_ep_regs(2);
387
388 // look for global usb interrupts
389 intrusb &= hsusb_reg8(INTRUSBE);
390 if (intrusb) {
391 if (intrusb & (1<<0)) {
392 // suspend
393 TRACEF("suspend\n");
394 call_all_callbacks(CB_SUSPEND, 0);
395 ret = INT_RESCHEDULE;
396 }
397 if (intrusb & (1<<1)) {
398 // resume
399 TRACEF("resume\n");
400 call_all_callbacks(CB_RESUME, 0);
401 ret = INT_RESCHEDULE;
402 }
403 if (intrusb & (1<<2)) {
404 // reset
405 TRACEF("reset\n");
406 TRACEF("high speed %d\n", hsusb_reg8(POWER) & (1<<4) ? 1 : 0);
407 call_all_callbacks(CB_RESET, 0);
408 usb_reset();
409 ret = INT_RESCHEDULE;
410 }
411 if (intrusb & (1<<3)) {
412 // SOF
413 TRACEF("sof\n");
414 }
415 if (intrusb & (1<<4)) {
416 // connect (host only)
417 TRACEF("connect\n");
418 }
419 if (intrusb & (1<<5)) {
420 // disconnect
421 TRACEF("disconnect\n");
422 call_all_callbacks(CB_DISCONNECT, 0);
423 usb_disconnect();
424 ret = INT_RESCHEDULE;
425 }
426 if (intrusb & (1<<6)) {
427 // session request (A device only)
428 TRACEF("session request\n");
429 }
430 if (intrusb & (1<<7)) {
431 // vbus error (A device only)
432 TRACEF("vbus error\n");
433 }
434 }
435
436 // look for endpoint 0 interrupt
437 if (intrtx & 1) {
438 select_ep(0);
439 uint16_t csr = hsusb_reg16(IDX_CSR0);
440 LTRACEF("ep0 csr 0x%hhx\n", csr);
441
442 // clear the stall bit
443 if (csr & (1<<2))
444 hsusb_reg16(IDX_CSR0) &= ~(1<<2);
445
446 // do we have any pending tx data?
447 if (usbc->ep0_tx_buf != NULL) {
448 if (csr & (1<<4)) { // setup end
449 // we got an abort on the data transfer
450 usbc->ep0_tx_buf = NULL;
451 } else {
452 // send more data
453 ep0_control_send_resume();
454 }
455 }
456
457 // clear the setup end bit
458 if (csr & (1<<4)) {
459 hsusb_reg16(IDX_CSR0) |= (1<<7); // servicedsetupend
460 }
461
462 if (csr & 0x1) {
463 // rxpktrdy
464 LTRACEF("ep0: rxpktrdy, count %d\n", hsusb_reg16(IDX_RXCOUNT));
465
466 struct usb_setup setup;
467 read_ep_fifo(0, (void *)&setup, sizeof(setup));
468// print_usb_setup(&setup);
469
470 hsusb_reg16(IDX_CSR0) |= (1<<6); // servicedrxpktrdy
471
472 union usb_callback_args args;
473 args.setup = &setup;
474 call_all_callbacks(CB_SETUP_MSG, &args);
475
476 switch (setup.request) {
477 case SET_ADDRESS: {
478 LTRACEF("got SET_ADDRESS: value %d\n", setup.value);
479 dprintf(INFO, "usb: got assigned address %d\n", setup.value);
480 usbc_ep0_ack();
481
482 hsusb_reg8(FADDR) = setup.value;
483 if (setup.value == 0)
484 usbc->state = USB_DEFAULT;
485 else
486 usbc->state = USB_ADDRESS;
487
488 break;
489 }
490 case SET_CONFIGURATION:
491 LTRACEF("got SET_CONFIGURATION, config %d\n", setup.value);
492
493 if (setup.value == 0) {
494 if (usbc->state == USB_CONFIGURED)
495 usbc->state = USB_ADDRESS;
496 call_all_callbacks(CB_OFFLINE, 0);
497 } else {
498 usbc->state = USB_CONFIGURED;
499 call_all_callbacks(CB_ONLINE, 0);
500 }
501 usbc->active_config = setup.value;
502 ret = INT_RESCHEDULE;
503
504 // set up all of the endpoints
505 usb_enable_endpoints();
506 break;
507 }
508 }
509 }
510
511 // handle endpoint interrupts
512
513 // mask out ones we don't want to play with
514 intrtx &= hsusb_reg16(INTRTXE);
515 intrrx &= hsusb_reg16(INTRRXE);
516
517 int i;
518 for (i=1; i < 16; i++) {
519 if (intrtx & (1<<i)) {
520 select_ep(i);
521
522 LTRACEF("txcsr %i: 0x%hx\n", i, hsusb_reg16(IDX_TXCSR));
523
524 // data was sent, see if we have more to send
525 struct usbc_ep *e = &usbc->inep[i];
526
527 DEBUG_ASSERT(e->transfer); // interrupts shouldn't be enabled if there isn't a transfer queued
528 usbc_transfer *t = e->transfer;
529
530 if (t->bufpos < t->buflen) {
531 // cram more stuff in the buffer
532 uint queuelen = MIN(e->blocksize, t->buflen - t->bufpos);
533 LTRACEF("writing more tx data into fifo: len %u, remaining %zu\n", queuelen, t->buflen - t->bufpos);
534 write_ep_fifo(i, (uint8_t *)t->buf + t->bufpos, queuelen);
535 t->bufpos += queuelen;
536
537 // start the transfer
538 hsusb_reg16(IDX_TXCSR) |= (1<<0); // txpktrdy
539 } else {
540 // we're done, callback
541 e->transfer = NULL;
542 hsusb_reg16(INTRTXE) &= ~(1<<i);
543
544 t->result = USB_TRANSFER_RESULT_OK;
545
546 DEBUG_ASSERT(e->callback);
547 e->callback(i, CB_EP_TXCOMPLETE, t);
548 ret = INT_RESCHEDULE;
549 }
550 }
551 if (intrrx & (1<<i)) {
552 select_ep(i);
553
554 uint16_t csr = hsusb_reg16(IDX_RXCSR);
555 LTRACEF("rxcsr %i: 0x%hx\n", i, csr);
556
557 if (csr & 0x1) { // rxpktrdy
558 // see if the endpoint is ready
559 struct usbc_ep *e = &usbc->outep[i];
560 if (!e->active) {
561 // stall it
562 hsusb_reg16(IDX_RXCSR) |= (1<<6); // stall
563 hsusb_reg16(IDX_RXCSR) |= (1<<4); // flush fifo
564 panic("rx on inactive endpoint\n");
565 continue;
566 }
567
568 if (handle_ep_rx(i) > 0)
569 ret = INT_RESCHEDULE;
570 }
571 }
572 }
573
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700574 return ret;
575}
576
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700577static enum handler_return hsusb_dma_interrupt(void *arg)
578{
579 LTRACE;
580
581 return INT_NO_RESCHEDULE;
582}
583
584void usbc_setup_endpoint(ep_t ep, ep_dir_t dir, bool active, ep_callback callback, uint width, uint blocksize)
585{
586 DEBUG_ASSERT(ep != 0);
587 DEBUG_ASSERT(ep < 16);
588 DEBUG_ASSERT(dir == IN || dir == OUT);
589
590 struct usbc_ep *e;
591 if (dir == IN)
592 e = &usbc->inep[ep];
593 else
594 e = &usbc->outep[ep];
595
596 // for now we can only make active
597 e->active = active;
598 e->callback = callback;
599 e->width = width;
600 e->blocksize = blocksize;
601}
602
603int usbc_queue_rx(ep_t ep, usbc_transfer *transfer)
604{
605 LTRACE;
606 struct usbc_ep *e = &usbc->outep[ep];
607
608 DEBUG_ASSERT(ep != 0);
609 DEBUG_ASSERT(ep < 16);
610 DEBUG_ASSERT(e->active);
611
612 DEBUG_ASSERT(transfer);
613 DEBUG_ASSERT(transfer->buf);
614
615 DEBUG_ASSERT(e->transfer == NULL);
616
617 // can only queue up multiples of the endpoint blocksize
618 DEBUG_ASSERT(transfer->buflen >= e->blocksize && (transfer->buflen % e->blocksize) == 0);
619
620 enter_critical_section();
621
622 if (usbc->state != USB_CONFIGURED) {
623 // can't transfer now
624 exit_critical_section();
625 return -1;
626 }
627
628 e->transfer = transfer;
629
630 // make sure the ep is set up right
631// select_ep(ep);
632// hsusb_reg8(IDX_RXCSRH) = 0;
633 dump_ep_regs(ep);
634
635 select_ep(ep);
636 if (hsusb_reg16(IDX_RXCSR) & (1<<0)) {
637 // packet already ready
638 LTRACEF("****packet already ready (%d)\n", hsusb_reg16(IDX_RXCOUNT));
639
640 int rc = handle_ep_rx(ep);
641 if (rc > 0) {
642 // the transfer was completed
643 goto done;
644 }
645 }
646
647 // unmask irqs for this endpoint
648 hsusb_reg16(INTRRXE) |= (1<<ep);
649
650done:
651 exit_critical_section();
652
653 return 0;
654}
655
656int usbc_queue_tx(ep_t ep, usbc_transfer *transfer)
657{
658 LTRACEF("ep %u, transfer %p (buf %p, len %zu)\n", ep, transfer, transfer->buf, transfer->buflen);
659 struct usbc_ep *e = &usbc->inep[ep];
660
661 DEBUG_ASSERT(ep != 0);
662 DEBUG_ASSERT(ep < 16);
663 DEBUG_ASSERT(e->active);
664
665 DEBUG_ASSERT(e->transfer == NULL);
666
667 enter_critical_section();
668
669 if (usbc->state != USB_CONFIGURED) {
670 // can't transfer now
671 exit_critical_section();
672 return -1;
673 }
674
675e->transfer = transfer;
676
677 select_ep(ep);
678
679 // set this endpoint in tx mode
680// hsusb_reg8(IDX_TXCSRH) = (1<<7)|(1<<5); // autoset, tx direction
681 dump_ep_regs(ep);
682
683 // unmask irqs for this endpoint
684 hsusb_reg16(INTRTXE) |= (1<<ep);
685
686 // if the fifo is empty, start the transfer
687 if ((hsusb_reg16(IDX_TXCSR) & (1<<1)) == 0) {
688 // dump the start of the transfer in the fifo
689 uint queuelen = MIN(e->blocksize, transfer->buflen);
690 write_ep_fifo(ep, transfer->buf, queuelen);
691 transfer->bufpos = queuelen;
692
693 // start the transfer
694 hsusb_reg16(IDX_TXCSR) |= (1<<0); // txpktrdy
695 }
696
697 exit_critical_section();
698
699 return 0;
700}
701
702int usbc_set_callback(usb_callback callback)
703{
704 DEBUG_ASSERT(callback != NULL);
705
706 struct usbc_callback *cb = malloc(sizeof(struct usbc_callback));
707
708 enter_critical_section();
709
710 cb->callback = callback;
711 list_add_head(&usbc_callback_list, &cb->node);
712
713 exit_critical_section();
714 return 0;
715}
716
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700717int usbc_set_active(bool active)
718{
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700719 LTRACEF("active %d\n", active);
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700720 if (active) {
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700721 DEBUG_ASSERT(!usbc->active);
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700722
723 hsusb_reg8(POWER) |= (1<<6); // soft conn
724 twl4030_set_usb_pullup(true);
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700725 usbc->active = true;
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700726 } else {
727 hsusb_reg8(POWER) &= ~(1<<6); // soft conn
728 twl4030_set_usb_pullup(false);
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700729 usbc->active = false;
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700730 }
731
732 return 0;
733}
734
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700735static void setup_dynamic_fifos(void)
736{
737// LTRACE;
738
739#if LOCAL_TRACE
740 uint8_t raminfo = hsusb_reg8(RAMINFO);
741 size_t ramsize = (1 << ((raminfo & 0xf) + 2));
742 LTRACEF("%zd bytes of onboard ram\n", ramsize);
743#endif
744
745 uint32_t offset = 128;
746
747 int highspeed = hsusb_reg8(POWER) & (1<<4);
748
749 int i;
750 for (i=1; i < 16; i++) {
751 select_ep(i);
752 if (usbc->inep[i].active) {
753 hsusb_reg8(TXFIFOSZ) = (1<<4)|(0x6); // 512 byte, double buffered
754 hsusb_reg8(RXFIFOSZ) = 0;
755 hsusb_reg16(TXFIFOADD) = offset / 8;
756 hsusb_reg16(RXFIFOADD) = 0;
757 if (highspeed) {
758 hsusb_reg16(IDX_TXMAXP) = usbc->inep[i].width;
759 } else {
760 hsusb_reg16(IDX_TXMAXP) = (((usbc->inep[i].blocksize / 64) - 1) << 11) | 64;
761// hsusb_reg16(IDX_TXMAXP) = 64;
762// usbc->inep[i].blocksize = 64;
763 }
764
765 hsusb_reg16(IDX_RXMAXP) = 0;
766 LTRACEF("%d: txmaxp 0x%hx\n", i, hsusb_reg16(IDX_TXMAXP));
767 hsusb_reg8(IDX_TXCSRH) = (1<<5)|(1<<3);
768 hsusb_reg8(IDX_TXCSRL) = (1<<3);
769 hsusb_reg8(IDX_TXCSRL) = (1<<3);
770 offset += 512*2;
771 } else {
772 hsusb_reg8(TXFIFOSZ) = 0;
773 hsusb_reg16(TXFIFOADD) = 0;
774 hsusb_reg16(IDX_TXMAXP) = 0;
775 }
776 if (usbc->outep[i].active) {
777 hsusb_reg8(TXFIFOSZ) = 0;
778 hsusb_reg8(RXFIFOSZ) = (0<<4)|(0x6); // 512 byte, single buffered
779 hsusb_reg16(TXFIFOADD) = 0;
780 hsusb_reg16(RXFIFOADD) = offset / 8;
781 hsusb_reg16(IDX_TXMAXP) = 0;
782 if (highspeed) {
783 hsusb_reg16(IDX_RXMAXP) = usbc->inep[i].width;
784 } else {
785 hsusb_reg16(IDX_RXMAXP) = (((usbc->outep[i].blocksize / 64) - 1) << 11) | 64;
786// hsusb_reg16(IDX_RXMAXP) = 64;
787// usbc->outep[i].blocksize = 64;
788 }
789 LTRACEF("%d: rxmaxp 0x%hx\n", i, hsusb_reg16(IDX_RXMAXP));
790 offset += 512;
791 hsusb_reg8(IDX_RXCSRH) = (1<<7);
792 hsusb_reg8(IDX_RXCSRL) = (1<<7);
793
794// LTRACEF("rxcsr 0x%hx\n", hsusb_reg16(IDX_RXCSR));
795 } else {
796 hsusb_reg8(RXFIFOSZ) = 0;
797 hsusb_reg16(RXFIFOADD) = 0;
798 hsusb_reg16(IDX_RXMAXP) = 0;
799 }
800// LTRACEF("%d txfifosz 0x%hhx\n", i, hsusb_reg8(TXFIFOSZ));
801// LTRACEF("%d rxfifosz 0x%hhx\n", i, hsusb_reg8(RXFIFOSZ));
802// LTRACEF("%d txfifoadd 0x%hx\n", i, hsusb_reg16(TXFIFOADD));
803// LTRACEF("%d rxfifoadd 0x%hx\n", i, hsusb_reg16(RXFIFOADD));
804 }
805}
806
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700807static void otg_reset(void)
808{
809 /* reset the chip */
810 *REG32(OTG_SYSCONFIG) |= (1<<1);
811 while ((*REG32(OTG_SYSSTATUS) & 1) == 0)
812 ;
813
814 /* power up the controller */
815 *REG32(OTG_FORCESTDBY) = 0; // disable forced standby
816 *REG32(OTG_SYSCONFIG) &= ~(1<<1); // autoidle off
817 *REG32(OTG_SYSCONFIG) = (2<<12) | (2<<3) | (0<<0); // master in smart-standby, periph in smart-idle, autoidle off
818
819 *REG32(OTG_SYSCONFIG) |= 1; // autoidle on
820
821 *REG32(OTG_INTERFSEL) = 1; // 8 bit ULPI
822}
823
824static void hsusb_init(void)
825{
826 LTRACE_ENTRY;
827
828 // select endpoint 0
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700829 dprintf(SPEW, "hwvers 0x%hx\n", hsusb_reg16(HWVERS));
830 dprintf(SPEW, "epinfo 0x%hhx\n", hsusb_reg8(EPINFO));
831 dprintf(SPEW, "raminfo 0x%hhx\n", hsusb_reg8(RAMINFO));
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700832 hsusb_reg8(INDEX) = 0;
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700833 dprintf(SPEW, "config 0x%hhx\n", hsusb_reg8(IDX_CONFIGDATA));
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700834
835 // assert that we have dynamic fifo sizing
836 DEBUG_ASSERT(hsusb_reg8(IDX_CONFIGDATA) & (1<<2));
837
838 // mask all the interrupts for the endpoints (except 0)
839 hsusb_reg16(INTRTXE) = (1<<0);
840 hsusb_reg16(INTRRXE) = 0;
841
842 twl4030_usb_reset();
843 twl4030_init_hs();
844
845 hsusb_reg8(DEVCTL) = 0; // peripheral mode
846// hsusb_reg8(POWER) &= (1<<5); // disable high speed
847 hsusb_reg8(POWER) |= (1<<5); // enable high speed
848
849 hsusb_reg8(INTRUSBE) = (1<<5)|(1<<2)|(1<<1)|(1<<0); // disconnect, reset, resume, suspend
850
851 LTRACE_EXIT;
852}
853
854void usbc_init(void)
855{
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700856 LTRACE_ENTRY;
857
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700858 // enable the clock
859 RMWREG32(CM_ICLKEN1_CORE, 4, 1, 1);
860
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700861 // allocate some ram for the usb struct
862 usbc = malloc(sizeof(struct usbc_stat));
863 memset(usbc, 0, sizeof(struct usbc_stat));
864
865 usbc->state = USB_DEFAULT;
866
867 // initialize the callback list
868 list_initialize(&usbc_callback_list);
869
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700870 // register the interrupt handlers
871 register_int_handler(92, hsusb_interrupt, NULL);
872// register_int_handler(93, hsusb_dma_interrupt, NULL);
873
874 otg_reset();
875 hsusb_init();
876
Travis Geiselbrecht47e75392008-09-13 15:34:26 -0700877 unmask_interrupt(92);
878// unmask_interrupt(93);
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700879
Travis Geiselbrecht7689f352008-10-10 03:18:36 -0700880 LTRACE_EXIT;
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700881}
882