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