diff options
Diffstat (limited to 'firmware/target/arm/usb-drv-pp502x.c')
-rw-r--r-- | firmware/target/arm/usb-drv-pp502x.c | 184 |
1 files changed, 147 insertions, 37 deletions
diff --git a/firmware/target/arm/usb-drv-pp502x.c b/firmware/target/arm/usb-drv-pp502x.c index 1db3ebd42f..413d905293 100644 --- a/firmware/target/arm/usb-drv-pp502x.c +++ b/firmware/target/arm/usb-drv-pp502x.c | |||
@@ -294,7 +294,7 @@ struct transfer_descriptor { | |||
294 | unsigned int reserved; | 294 | unsigned int reserved; |
295 | } __attribute__ ((packed)); | 295 | } __attribute__ ((packed)); |
296 | 296 | ||
297 | static struct transfer_descriptor _td_array[NUM_ENDPOINTS*2] __attribute((aligned (32))); | 297 | static struct transfer_descriptor _td_array[32] __attribute((aligned (32))); |
298 | static struct transfer_descriptor* td_array; | 298 | static struct transfer_descriptor* td_array; |
299 | 299 | ||
300 | /* manual: 32.13.1 Endpoint Queue Head (dQH) */ | 300 | /* manual: 32.13.1 Endpoint Queue Head (dQH) */ |
@@ -317,9 +317,15 @@ static const unsigned int pipe2mask[NUM_ENDPOINTS*2] = { | |||
317 | 0x04, 0x040000, | 317 | 0x04, 0x040000, |
318 | }; | 318 | }; |
319 | 319 | ||
320 | static struct transfer_descriptor* first_td; | ||
321 | static struct transfer_descriptor* last_td; | ||
322 | |||
320 | /*-------------------------------------------------------------------------*/ | 323 | /*-------------------------------------------------------------------------*/ |
321 | static void transfer_completed(void); | 324 | static void transfer_completed(void); |
322 | static int prime_transfer(int endpoint, void* ptr, int len, bool send); | 325 | static int prime_transfer(int endpoint, void* ptr, int len, bool send); |
326 | static void prepare_td(struct transfer_descriptor* td, | ||
327 | struct transfer_descriptor* previous_td, | ||
328 | void *ptr, int len); | ||
323 | static void bus_reset(void); | 329 | static void bus_reset(void); |
324 | static void init_queue_heads(void); | 330 | static void init_queue_heads(void); |
325 | static void init_endpoints(void); | 331 | static void init_endpoints(void); |
@@ -340,6 +346,10 @@ void usb_drv_init(void) | |||
340 | 346 | ||
341 | REG_USBMODE = USBMODE_CTRL_MODE_DEVICE; | 347 | REG_USBMODE = USBMODE_CTRL_MODE_DEVICE; |
342 | 348 | ||
349 | /* Force device to full speed */ | ||
350 | /* See 32.9.5.9.2 */ | ||
351 | REG_PORTSC1 |= PORTSCX_PORT_FORCE_FULL_SPEED; | ||
352 | |||
343 | td_array = (struct transfer_descriptor*)UNCACHED_ADDR(&_td_array); | 353 | td_array = (struct transfer_descriptor*)UNCACHED_ADDR(&_td_array); |
344 | qh_array = (struct queue_head*)UNCACHED_ADDR(&_qh_array); | 354 | qh_array = (struct queue_head*)UNCACHED_ADDR(&_qh_array); |
345 | init_queue_heads(); | 355 | init_queue_heads(); |
@@ -467,6 +477,10 @@ void usb_drv_wait(int endpoint, bool send) | |||
467 | } | 477 | } |
468 | } | 478 | } |
469 | 479 | ||
480 | int usb_drv_port_speed(void) | ||
481 | { | ||
482 | return (REG_PORTSC1 & 0x08000000) ? 1 : 0; | ||
483 | } | ||
470 | 484 | ||
471 | void usb_drv_set_address(int address) | 485 | void usb_drv_set_address(int address) |
472 | { | 486 | { |
@@ -482,44 +496,87 @@ void usb_drv_reset_endpoint(int endpoint, bool send) | |||
482 | while (REG_ENDPTFLUSH & mask); | 496 | while (REG_ENDPTFLUSH & mask); |
483 | } | 497 | } |
484 | 498 | ||
499 | int usb_drv_get_last_transfer_length(void) | ||
500 | { | ||
501 | struct transfer_descriptor* current_td = first_td; | ||
502 | int length = 0; | ||
503 | |||
504 | while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) { | ||
505 | if ((current_td->size_ioc_sts & 0xff) != 0) | ||
506 | return -1; | ||
507 | |||
508 | length += current_td->reserved - | ||
509 | ((current_td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS); | ||
510 | current_td = (struct transfer_descriptor*)current_td->next_td_ptr; | ||
511 | } | ||
512 | return length; | ||
513 | } | ||
514 | int usb_drv_get_last_transfer_status(void) | ||
515 | { | ||
516 | struct transfer_descriptor* current_td = first_td; | ||
517 | |||
518 | while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) { | ||
519 | if ((current_td->size_ioc_sts & 0xff) != 0) | ||
520 | return current_td->size_ioc_sts & 0xff; | ||
521 | |||
522 | current_td = (struct transfer_descriptor*)current_td->next_td_ptr; | ||
523 | } | ||
524 | return 0; | ||
525 | } | ||
526 | |||
485 | /*-------------------------------------------------------------------------*/ | 527 | /*-------------------------------------------------------------------------*/ |
486 | 528 | ||
487 | /* manual: 32.14.5.2 */ | 529 | /* manual: 32.14.5.2 */ |
488 | static int prime_transfer(int endpoint, void* ptr, int len, bool send) | 530 | static int prime_transfer(int endpoint, void* ptr, int len, bool send) |
489 | { | 531 | { |
490 | int timeout; | ||
491 | int pipe = endpoint * 2 + (send ? 1 : 0); | 532 | int pipe = endpoint * 2 + (send ? 1 : 0); |
492 | unsigned int mask = pipe2mask[pipe]; | 533 | unsigned int mask = pipe2mask[pipe]; |
493 | struct transfer_descriptor* td = &td_array[pipe]; | 534 | last_td = 0; |
494 | struct queue_head* qh = &qh_array[pipe]; | 535 | struct queue_head* qh = &qh_array[pipe]; |
536 | static long last_tick; | ||
495 | 537 | ||
538 | /* | ||
496 | if (send && endpoint > EP_CONTROL) { | 539 | if (send && endpoint > EP_CONTROL) { |
497 | logf("usb: sent %d bytes", len); | 540 | logf("usb: sent %d bytes", len); |
498 | } | 541 | } |
542 | */ | ||
499 | 543 | ||
500 | memset(td, 0, sizeof(struct transfer_descriptor)); | 544 | if (len==0) { |
501 | td->next_td_ptr = DTD_NEXT_TERMINATE; | 545 | struct transfer_descriptor* new_td = &td_array[0]; |
502 | td->size_ioc_sts = (len << DTD_LENGTH_BIT_POS) | | 546 | prepare_td(new_td, 0, ptr, 0); |
503 | DTD_STATUS_ACTIVE | DTD_IOC; | 547 | |
504 | td->buff_ptr0 = (unsigned int)ptr; | 548 | last_td = new_td; |
505 | td->buff_ptr1 = (unsigned int)ptr + 0x1000; | 549 | first_td = new_td; |
506 | td->buff_ptr2 = (unsigned int)ptr + 0x2000; | 550 | } |
507 | td->buff_ptr3 = (unsigned int)ptr + 0x3000; | 551 | else { |
508 | td->buff_ptr4 = (unsigned int)ptr + 0x4000; | 552 | int td_idx = 0; |
509 | td->reserved = len; | 553 | while (len > 0) { |
510 | qh->dtd.next_td_ptr = (unsigned int)td; | 554 | int current_transfer_length = MIN(16384,len); |
555 | struct transfer_descriptor* new_td = &td_array[td_idx]; | ||
556 | prepare_td(new_td, last_td, ptr, current_transfer_length); | ||
557 | |||
558 | last_td = new_td; | ||
559 | len -= current_transfer_length; | ||
560 | td_idx++; | ||
561 | ptr += current_transfer_length; | ||
562 | } | ||
563 | first_td = &td_array[0]; | ||
564 | } | ||
565 | |||
566 | qh->dtd.next_td_ptr = (unsigned int)first_td; | ||
511 | qh->dtd.size_ioc_sts &= ~(QH_STATUS_HALT | QH_STATUS_ACTIVE); | 567 | qh->dtd.size_ioc_sts &= ~(QH_STATUS_HALT | QH_STATUS_ACTIVE); |
512 | 568 | ||
513 | REG_ENDPTPRIME |= mask; | 569 | REG_ENDPTPRIME |= mask; |
514 | 570 | ||
515 | timeout = 10000; | 571 | last_tick = current_tick; |
516 | while ((REG_ENDPTPRIME & mask) && --timeout) { | 572 | while ((REG_ENDPTPRIME & mask)) { |
517 | if (REG_USBSTS & USBSTS_RESET) | 573 | if (REG_USBSTS & USBSTS_RESET) |
518 | return -1; | 574 | return -1; |
519 | } | 575 | |
520 | if (!timeout) { | 576 | if (TIME_AFTER(current_tick, last_tick + HZ/4)) { |
521 | logf("prime timeout"); | 577 | logf("prime timeout"); |
522 | return -2; | 578 | return -2; |
579 | } | ||
523 | } | 580 | } |
524 | 581 | ||
525 | if (!(REG_ENDPTSTATUS & mask)) { | 582 | if (!(REG_ENDPTSTATUS & mask)) { |
@@ -529,23 +586,54 @@ static int prime_transfer(int endpoint, void* ptr, int len, bool send) | |||
529 | 586 | ||
530 | if (send) { | 587 | if (send) { |
531 | /* wait for transfer to finish */ | 588 | /* wait for transfer to finish */ |
532 | timeout = 100000; | 589 | struct transfer_descriptor* current_td = first_td; |
533 | while ((td->size_ioc_sts & DTD_STATUS_ACTIVE) && --timeout) { | 590 | |
534 | if (REG_ENDPTCOMPLETE & mask) | 591 | while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) { |
535 | REG_ENDPTCOMPLETE |= mask; | 592 | while ((current_td->size_ioc_sts & 0xff) == DTD_STATUS_ACTIVE) { |
536 | 593 | if (REG_ENDPTCOMPLETE & mask) | |
537 | if (REG_USBSTS & USBSTS_RESET) | 594 | REG_ENDPTCOMPLETE |= mask; |
538 | return -4; | 595 | |
539 | } | 596 | /* let the host handle timeouts */ |
540 | if (!timeout) { | 597 | if (REG_USBSTS & USBSTS_RESET) { |
541 | logf("td never finished"); | 598 | logf("td interrupted by reset"); |
542 | return -5; | 599 | return -4; |
600 | } | ||
601 | } | ||
602 | if ((current_td->size_ioc_sts & 0xff) != 0) { | ||
603 | logf("td failed with error %X",(current_td->size_ioc_sts & 0xff)); | ||
604 | return -6; | ||
605 | } | ||
606 | //logf("td finished : %X",current_td->size_ioc_sts & 0xff); | ||
607 | current_td=(struct transfer_descriptor*)current_td->next_td_ptr; | ||
543 | } | 608 | } |
609 | //logf("all tds done"); | ||
544 | } | 610 | } |
545 | 611 | ||
546 | return 0; | 612 | return 0; |
547 | } | 613 | } |
548 | 614 | ||
615 | static void prepare_td(struct transfer_descriptor* td, | ||
616 | struct transfer_descriptor* previous_td, | ||
617 | void *ptr, int len) | ||
618 | { | ||
619 | //logf("adding a td : %d",len); | ||
620 | memset(td, 0, sizeof(struct transfer_descriptor)); | ||
621 | td->next_td_ptr = DTD_NEXT_TERMINATE; | ||
622 | td->size_ioc_sts = (len<< DTD_LENGTH_BIT_POS) | | ||
623 | DTD_STATUS_ACTIVE | DTD_IOC; | ||
624 | td->buff_ptr0 = (unsigned int)ptr; | ||
625 | td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000; | ||
626 | td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000; | ||
627 | td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000; | ||
628 | td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000; | ||
629 | td->reserved = len; | ||
630 | |||
631 | if (previous_td != 0) { | ||
632 | previous_td->next_td_ptr=(unsigned int)td; | ||
633 | previous_td->size_ioc_sts&=~DTD_IOC;// Only an interrupt on the last one | ||
634 | } | ||
635 | } | ||
636 | |||
549 | static void transfer_completed(void) | 637 | static void transfer_completed(void) |
550 | { | 638 | { |
551 | int i; | 639 | int i; |
@@ -557,13 +645,16 @@ static void transfer_completed(void) | |||
557 | for (i=0; i<NUM_ENDPOINTS; i++) { | 645 | for (i=0; i<NUM_ENDPOINTS; i++) { |
558 | int x; | 646 | int x; |
559 | for (x=0; x<2; x++) { | 647 | for (x=0; x<2; x++) { |
648 | unsigned int status; | ||
560 | int pipe = i * 2 + x; | 649 | int pipe = i * 2 + x; |
650 | |||
561 | if (mask & pipe2mask[pipe]) | 651 | if (mask & pipe2mask[pipe]) |
562 | usb_core_transfer_complete(i, x ? true : false); | 652 | usb_core_transfer_complete(i, x ? true : false); |
563 | 653 | ||
654 | status = usb_drv_get_last_transfer_status(); | ||
564 | if ((mask & pipe2mask[pipe]) && | 655 | if ((mask & pipe2mask[pipe]) && |
565 | (td_array[pipe].size_ioc_sts & DTD_ERROR_MASK)) { | 656 | status & DTD_ERROR_MASK) { |
566 | logf("pipe %d err %x", pipe, td_array[pipe].size_ioc_sts & DTD_ERROR_MASK); | 657 | logf("pipe %d err %x", pipe, status & DTD_ERROR_MASK); |
567 | } | 658 | } |
568 | } | 659 | } |
569 | } | 660 | } |
@@ -600,23 +691,42 @@ static void bus_reset(void) | |||
600 | if (!(REG_PORTSC1 & PORTSCX_PORT_RESET)) { | 691 | if (!(REG_PORTSC1 & PORTSCX_PORT_RESET)) { |
601 | logf("usb: slow reset!"); | 692 | logf("usb: slow reset!"); |
602 | } | 693 | } |
694 | |||
695 | logf("PTS : %X",(REG_PORTSC1 & 0xC0000000)>>30); | ||
696 | logf("STS : %X",(REG_PORTSC1 & 0x20000000)>>29); | ||
697 | logf("PTW : %X",(REG_PORTSC1 & 0x10000000)>>28); | ||
698 | logf("PSPD : %X",(REG_PORTSC1 & 0x0C000000)>>26); | ||
699 | logf("PFSC : %X",(REG_PORTSC1 & 0x01000000)>>24); | ||
700 | logf("PTC : %X",(REG_PORTSC1 & 0x000F0000)>>16); | ||
701 | logf("PO : %X",(REG_PORTSC1 & 0x00002000)>>13); | ||
603 | } | 702 | } |
604 | 703 | ||
605 | /* manual: 32.14.4.1 Queue Head Initialization */ | 704 | /* manual: 32.14.4.1 Queue Head Initialization */ |
606 | static void init_queue_heads(void) | 705 | static void init_queue_heads(void) |
607 | { | 706 | { |
707 | int tx_packetsize; | ||
708 | int rx_packetsize; | ||
709 | |||
710 | if (usb_drv_port_speed()) { | ||
711 | rx_packetsize = 512; | ||
712 | tx_packetsize = 512; | ||
713 | } | ||
714 | else { | ||
715 | rx_packetsize = 16; | ||
716 | tx_packetsize = 16; | ||
717 | } | ||
608 | memset(qh_array, 0, sizeof _qh_array); | 718 | memset(qh_array, 0, sizeof _qh_array); |
609 | 719 | ||
610 | /*** control ***/ | 720 | /*** control ***/ |
611 | qh_array[EP_CONTROL].max_pkt_length = 512 << QH_MAX_PKT_LEN_POS | QH_IOS; | 721 | qh_array[EP_CONTROL].max_pkt_length = 64 << QH_MAX_PKT_LEN_POS | QH_IOS; |
612 | qh_array[EP_CONTROL].dtd.next_td_ptr = QH_NEXT_TERMINATE; | 722 | qh_array[EP_CONTROL].dtd.next_td_ptr = QH_NEXT_TERMINATE; |
613 | qh_array[EP_CONTROL+1].max_pkt_length = 512 << QH_MAX_PKT_LEN_POS; | 723 | qh_array[EP_CONTROL+1].max_pkt_length = 64 << QH_MAX_PKT_LEN_POS; |
614 | qh_array[EP_CONTROL+1].dtd.next_td_ptr = QH_NEXT_TERMINATE; | 724 | qh_array[EP_CONTROL+1].dtd.next_td_ptr = QH_NEXT_TERMINATE; |
615 | 725 | ||
616 | /*** bulk ***/ | 726 | /*** bulk ***/ |
617 | qh_array[EP_RX*2].max_pkt_length = 512 << QH_MAX_PKT_LEN_POS; | 727 | qh_array[EP_RX*2].max_pkt_length = rx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; |
618 | qh_array[EP_RX*2].dtd.next_td_ptr = QH_NEXT_TERMINATE; | 728 | qh_array[EP_RX*2].dtd.next_td_ptr = QH_NEXT_TERMINATE; |
619 | qh_array[EP_TX*2+1].max_pkt_length = 512 << QH_MAX_PKT_LEN_POS; | 729 | qh_array[EP_TX*2+1].max_pkt_length = tx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; |
620 | qh_array[EP_TX*2+1].dtd.next_td_ptr = QH_NEXT_TERMINATE; | 730 | qh_array[EP_TX*2+1].dtd.next_td_ptr = QH_NEXT_TERMINATE; |
621 | } | 731 | } |
622 | 732 | ||