@@ -294,6 +294,40 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
294
294
ehci_data .regs -> command_bm .async_adv_doorbell = 1 ;
295
295
}
296
296
297
+ static void init_periodic_list (uint8_t rhport ) {
298
+ // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
299
+ for ( uint32_t i = 0 ; i < TU_ARRAY_SIZE (ehci_data .period_head_arr ); i ++ ) {
300
+ ehci_data .period_head_arr [i ].int_smask = 1 ; // queue head in period list must have smask non-zero
301
+ ehci_data .period_head_arr [i ].qtd_overlay .halted = 1 ; // dummy node, always inactive
302
+ }
303
+
304
+ ehci_link_t * const framelist = ehci_data .period_framelist ;
305
+ ehci_link_t * const period_1ms = get_period_head (rhport , 1u );
306
+
307
+ // all links --> period_head_arr[0] (1ms)
308
+ // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
309
+ // 1, 5 --> period_head_arr[2] (4ms)
310
+ // 3 --> period_head_arr[3] (8ms)
311
+
312
+ // TODO EHCI_FRAMELIST_SIZE with other size than 8
313
+ for (uint32_t i = 0 ; i < FRAMELIST_SIZE ; i ++ ) {
314
+ framelist [i ].address = (uint32_t ) period_1ms ;
315
+ framelist [i ].type = EHCI_QTYPE_QHD ;
316
+ }
317
+
318
+ for (uint32_t i = 0 ; i < FRAMELIST_SIZE ; i += 2 ) {
319
+ list_insert (framelist + i , get_period_head (rhport , 2u ), EHCI_QTYPE_QHD );
320
+ }
321
+
322
+ for (uint32_t i = 1 ; i < FRAMELIST_SIZE ; i += 4 ) {
323
+ list_insert (framelist + i , get_period_head (rhport , 4u ), EHCI_QTYPE_QHD );
324
+ }
325
+
326
+ list_insert (framelist + 3 , get_period_head (rhport , 8u ), EHCI_QTYPE_QHD );
327
+
328
+ period_1ms -> terminate = 1 ;
329
+ }
330
+
297
331
bool ehci_init (uint8_t rhport , uint32_t capability_reg , uint32_t operatial_reg )
298
332
{
299
333
tu_memclr (& ehci_data , sizeof (ehci_data_t ));
@@ -332,38 +366,8 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
332
366
regs -> async_list_addr = (uint32_t ) async_head ;
333
367
334
368
//------------- Periodic List -------------//
335
- // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
336
- for ( uint32_t i = 0 ; i < TU_ARRAY_SIZE (ehci_data .period_head_arr ); i ++ )
337
- {
338
- ehci_data .period_head_arr [i ].int_smask = 1 ; // queue head in period list must have smask non-zero
339
- ehci_data .period_head_arr [i ].qtd_overlay .halted = 1 ; // dummy node, always inactive
340
- }
341
-
342
- ehci_link_t * const framelist = ehci_data .period_framelist ;
343
- ehci_link_t * const period_1ms = get_period_head (rhport , 1u );
344
-
345
- // all links --> period_head_arr[0] (1ms)
346
- // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
347
- // 1, 5 --> period_head_arr[2] (4ms)
348
- // 3 --> period_head_arr[3] (8ms)
349
-
350
- // TODO EHCI_FRAMELIST_SIZE with other size than 8
351
- for (uint32_t i = 0 ; i < FRAMELIST_SIZE ; i ++ ) {
352
- framelist [i ].address = (uint32_t ) period_1ms ;
353
- framelist [i ].type = EHCI_QTYPE_QHD ;
354
- }
355
-
356
- for (uint32_t i = 0 ; i < FRAMELIST_SIZE ; i += 2 ) {
357
- list_insert (framelist + i , get_period_head (rhport , 2u ), EHCI_QTYPE_QHD );
358
- }
359
-
360
- for (uint32_t i = 1 ; i < FRAMELIST_SIZE ; i += 4 ) {
361
- list_insert (framelist + i , get_period_head (rhport , 4u ), EHCI_QTYPE_QHD );
362
- }
363
- list_insert (framelist + 3 , get_period_head (rhport , 8u ), EHCI_QTYPE_QHD );
364
- period_1ms -> terminate = 1 ;
365
-
366
- regs -> periodic_list_base = (uint32_t ) framelist ;
369
+ init_periodic_list (rhport );
370
+ regs -> periodic_list_base = (uint32_t ) ehci_data .period_framelist ;
367
371
368
372
hcd_dcache_clean (& ehci_data , sizeof (ehci_data_t ));
369
373
@@ -491,8 +495,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
491
495
ehci_qhd_t * qhd ;
492
496
ehci_qtd_t * qtd ;
493
497
494
- if ( epnum == 0 )
495
- {
498
+ if (epnum == 0 ) {
496
499
qhd = qhd_control (dev_addr );
497
500
qtd = qtd_control (dev_addr );
498
501
@@ -501,8 +504,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
501
504
// first first data toggle is always 1 (data & setup stage)
502
505
qtd -> data_toggle = 1 ;
503
506
qtd -> pid = dir ? EHCI_PID_IN : EHCI_PID_OUT ;
504
- }else
505
- {
507
+ } else {
506
508
qhd = qhd_get_from_addr (dev_addr , ep_addr );
507
509
qtd = qtd_find_free ();
508
510
TU_ASSERT (qtd );
@@ -531,10 +533,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
531
533
return true;
532
534
}
533
535
534
- bool hcd_edpt_clear_stall (uint8_t dev_addr , uint8_t ep_addr )
536
+ bool hcd_edpt_clear_stall (uint8_t daddr , uint8_t ep_addr )
535
537
{
536
- ehci_qhd_t * p_qhd = qhd_get_from_addr (dev_addr , ep_addr );
537
- p_qhd -> qtd_overlay .halted = 0 ;
538
+ ehci_qhd_t * qhd = qhd_get_from_addr (daddr , ep_addr );
539
+ qhd -> qtd_overlay .halted = 0 ;
540
+ hcd_dcache_clean_invalidate (qhd , sizeof (ehci_qhd_t ));
538
541
// TODO reset data toggle ?
539
542
return true;
540
543
}
@@ -546,22 +549,22 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
546
549
// async_advance is handshake between usb stack & ehci controller.
547
550
// This isr mean it is safe to modify previously removed queue head from async list.
548
551
// In tinyusb, queue head is only removed when device is unplugged.
549
- static void async_advance_isr (uint8_t rhport )
552
+ TU_ATTR_ALWAYS_INLINE static inline
553
+ void async_advance_isr (uint8_t rhport )
550
554
{
551
555
(void ) rhport ;
552
556
553
- ehci_qhd_t * qhd_pool = ehci_data .qhd_pool ;
554
- for (uint32_t i = 0 ; i < QHD_MAX ; i ++ )
555
- {
556
- if ( qhd_pool [i ].removing )
557
- {
557
+ ehci_qhd_t * qhd_pool = ehci_data .qhd_pool ;
558
+ for (uint32_t i = 0 ; i < QHD_MAX ; i ++ ) {
559
+ if (qhd_pool [i ].removing ) {
558
560
qhd_pool [i ].removing = 0 ;
559
- qhd_pool [i ].used = 0 ;
561
+ qhd_pool [i ].used = 0 ;
560
562
}
561
563
}
562
564
}
563
565
564
- static void port_connect_status_change_isr (uint8_t rhport )
566
+ TU_ATTR_ALWAYS_INLINE static inline
567
+ void port_connect_status_change_isr (uint8_t rhport )
565
568
{
566
569
// NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
567
570
if (ehci_data .regs -> portsc_bm .current_connect_status )
@@ -574,13 +577,13 @@ static void port_connect_status_change_isr(uint8_t rhport)
574
577
}
575
578
}
576
579
577
- static void qhd_xfer_complete_isr (ehci_qhd_t * p_qhd )
580
+ TU_ATTR_ALWAYS_INLINE static inline
581
+ void qhd_xfer_complete_isr (ehci_qhd_t * p_qhd )
578
582
{
579
583
// free all TDs from the head td to the first active TD
580
584
while (p_qhd -> p_qtd_list_head != NULL && !p_qhd -> p_qtd_list_head -> active )
581
585
{
582
586
ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile ) p_qhd -> p_qtd_list_head ;
583
-
584
587
hcd_dcache_invalidate (qtd , sizeof (ehci_qtd_t ));
585
588
586
589
bool const is_ioc = (qtd -> int_on_complete != 0 );
@@ -600,7 +603,8 @@ static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd)
600
603
}
601
604
}
602
605
603
- static void async_list_xfer_complete_isr (ehci_qhd_t * const async_head )
606
+ TU_ATTR_ALWAYS_INLINE static inline
607
+ void async_list_xfer_complete_isr (ehci_qhd_t * const async_head )
604
608
{
605
609
ehci_qhd_t * p_qhd = async_head ;
606
610
do
@@ -611,46 +615,55 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
611
615
if ( !p_qhd -> qtd_overlay .halted ) {
612
616
qhd_xfer_complete_isr (p_qhd );
613
617
}
618
+
614
619
p_qhd = qhd_next (p_qhd );
615
620
}while (p_qhd != async_head ); // async list traversal, stop if loop around
616
621
}
617
622
618
- static void period_list_xfer_complete_isr (uint8_t hostid , uint32_t interval_ms )
623
+ TU_ATTR_ALWAYS_INLINE static inline
624
+ void period_list_xfer_complete_isr (uint8_t rhport , uint32_t interval_ms )
619
625
{
620
- uint16_t max_loop = 0 ;
621
- uint32_t const period_1ms_addr = (uint32_t ) get_period_head (hostid , 1u );
622
- ehci_link_t next_item = * get_period_head (hostid , interval_ms );
626
+ uint32_t const period_1ms_addr = (uint32_t ) get_period_head (rhport , 1u );
627
+ ehci_link_t next_link = * get_period_head (rhport , interval_ms );
623
628
624
- // TODO abstract max loop guard for period
625
- while ( !next_item .terminate &&
626
- !(interval_ms > 1 && period_1ms_addr == tu_align32 (next_item .address )) &&
627
- max_loop < (QHD_MAX + EHCI_MAX_ITD + EHCI_MAX_SITD )* CFG_TUH_DEVICE_MAX )
628
- {
629
- switch ( next_item .type )
630
- {
631
- case EHCI_QTYPE_QHD :
632
- {
633
- ehci_qhd_t * p_qhd_int = (ehci_qhd_t * ) tu_align32 (next_item .address );
634
- if ( !p_qhd_int -> qtd_overlay .halted )
635
- {
636
- qhd_xfer_complete_isr (p_qhd_int );
629
+ while (!next_link .terminate ) {
630
+ if (interval_ms > 1 && period_1ms_addr == tu_align32 (next_link .address )) {
631
+ // 1ms period list is end of list for all larger interval
632
+ break ;
633
+ }
634
+
635
+ uintptr_t const entry_addr = tu_align32 (next_link .address );
636
+
637
+ switch (next_link .type ) {
638
+ case EHCI_QTYPE_QHD : {
639
+ ehci_qhd_t * qhd = (ehci_qhd_t * ) entry_addr ;
640
+ hcd_dcache_invalidate (qhd , sizeof (ehci_qhd_t ));
641
+
642
+ if (!qhd -> qtd_overlay .halted ) {
643
+ qhd_xfer_complete_isr (qhd );
637
644
}
638
645
}
639
- break ;
646
+ break ;
647
+
648
+ case EHCI_QTYPE_ITD :
649
+ // TODO support hs ISO
650
+ break ;
640
651
641
- case EHCI_QTYPE_ITD : // TODO support hs/fs ISO
642
652
case EHCI_QTYPE_SITD :
643
- case EHCI_QTYPE_FSTN :
653
+ // TODO support split ISO
654
+ break ;
644
655
645
- default : break ;
656
+ case EHCI_QTYPE_FSTN :
657
+ default :
658
+ break ;
646
659
}
647
660
648
- next_item = * list_next (& next_item );
649
- max_loop ++ ;
661
+ next_link = * list_next (& next_link );
650
662
}
651
663
}
652
664
653
- static void qhd_xfer_error_isr (ehci_qhd_t * p_qhd )
665
+ TU_ATTR_ALWAYS_INLINE static inline
666
+ void qhd_xfer_error_isr (ehci_qhd_t * p_qhd )
654
667
{
655
668
volatile ehci_qtd_t * qtd_overlay = & p_qhd -> qtd_overlay ;
656
669
@@ -666,22 +679,22 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
666
679
xfer_result = XFER_RESULT_STALLED ;
667
680
}
668
681
669
- p_qhd -> total_xferred_bytes += p_qhd -> p_qtd_list_head -> expected_bytes - p_qhd -> p_qtd_list_head -> total_bytes ;
670
-
671
682
// if (XFER_RESULT_FAILED == xfer_result ) {
672
683
// TU_LOG1(" QHD xfer err count: %d\n", qtd_overlay->err_count);
673
684
// TU_BREAKPOINT(); // TODO skip unplugged device
674
685
// while(1){}
675
686
// }
676
687
677
- // No TD yet, it is probably the probably an signal noise ?
678
- TU_ASSERT (p_qhd -> p_qtd_list_head , );
688
+ ehci_qtd_t * volatile qtd = ( ehci_qtd_t * volatile ) p_qhd -> p_qtd_list_head ;
689
+ TU_ASSERT (qtd , ); // No TD yet, probably a race condition or cache issue !?
679
690
680
- p_qhd -> p_qtd_list_head -> used = 0 ; // free QTD
691
+ hcd_dcache_invalidate (qtd , sizeof (ehci_qtd_t ));
692
+ p_qhd -> total_xferred_bytes += qtd -> expected_bytes - qtd -> total_bytes ;
693
+
694
+ qtd -> used = 0 ; // free QTD
681
695
qtd_remove_1st_from_qhd (p_qhd );
682
696
683
- if ( 0 == p_qhd -> ep_number )
684
- {
697
+ if ( 0 == p_qhd -> ep_number ) {
685
698
// control cannot be halted --> clear all qtd list
686
699
p_qhd -> p_qtd_list_head = NULL ;
687
700
p_qhd -> p_qtd_list_tail = NULL ;
@@ -702,13 +715,15 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
702
715
}
703
716
}
704
717
705
- static void xfer_error_isr (uint8_t hostid )
718
+ TU_ATTR_ALWAYS_INLINE static inline
719
+ void xfer_error_isr (uint8_t hostid )
706
720
{
707
721
//------------- async list -------------//
708
722
ehci_qhd_t * const async_head = qhd_async_head (hostid );
709
723
ehci_qhd_t * p_qhd = async_head ;
710
724
do
711
725
{
726
+ hcd_dcache_invalidate (p_qhd , sizeof (ehci_qhd_t ));
712
727
qhd_xfer_error_isr ( p_qhd );
713
728
p_qhd = qhd_next (p_qhd );
714
729
}while (p_qhd != async_head ); // async list traversal, stop if loop around
@@ -728,6 +743,8 @@ static void xfer_error_isr(uint8_t hostid)
728
743
case EHCI_QTYPE_QHD :
729
744
{
730
745
ehci_qhd_t * p_qhd_int = (ehci_qhd_t * ) tu_align32 (next_item .address );
746
+ hcd_dcache_invalidate (p_qhd_int , sizeof (ehci_qhd_t ));
747
+
731
748
qhd_xfer_error_isr (p_qhd_int );
732
749
}
733
750
break ;
@@ -757,36 +774,31 @@ void hcd_int_handler(uint8_t rhport)
757
774
return ;
758
775
}
759
776
760
- if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER )
761
- {
777
+ if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER ) {
762
778
ehci_data .uframe_number += (FRAMELIST_SIZE << 3 );
763
779
regs -> status = EHCI_INT_MASK_FRAMELIST_ROLLOVER ; // Acknowledge
764
780
}
765
781
766
- if (int_status & EHCI_INT_MASK_PORT_CHANGE )
767
- {
782
+ if (int_status & EHCI_INT_MASK_PORT_CHANGE ) {
768
783
// Including: Force port resume, over-current change, enable/disable change and connect status change.
769
784
uint32_t const port_status = regs -> portsc & EHCI_PORTSC_MASK_W1C ;
770
785
print_portsc (regs );
771
786
772
- if (regs -> portsc_bm .connect_status_change )
773
- {
787
+ if (regs -> portsc_bm .connect_status_change ) {
774
788
port_connect_status_change_isr (rhport );
775
789
}
776
790
777
791
regs -> portsc |= port_status ; // Acknowledge change bits in portsc
778
792
regs -> status = EHCI_INT_MASK_PORT_CHANGE ; // Acknowledge
779
793
}
780
794
781
- if (int_status & EHCI_INT_MASK_ERROR )
782
- {
795
+ if (int_status & EHCI_INT_MASK_ERROR ) {
783
796
xfer_error_isr (rhport );
784
797
regs -> status = EHCI_INT_MASK_ERROR ; // Acknowledge
785
798
}
786
799
787
800
//------------- some QTD/SITD/ITD with IOC set is completed -------------//
788
- if (int_status & EHCI_INT_MASK_NXP_ASYNC )
789
- {
801
+ if (int_status & EHCI_INT_MASK_NXP_ASYNC ) {
790
802
async_list_xfer_complete_isr (qhd_async_head (rhport ));
791
803
regs -> status = EHCI_INT_MASK_NXP_ASYNC ; // Acknowledge
792
804
}
0 commit comments