Skip to content

Commit 49e2aab

Browse files
committed
EHCI more improvement
- more dcache clean/invalidate - extract init_periodic_list() - improve isr list handling
1 parent a0aea52 commit 49e2aab

File tree

2 files changed

+105
-93
lines changed

2 files changed

+105
-93
lines changed

src/host/hcd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
175175
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
176176

177177
// clear stall, data toggle is also reset to DATA0
178-
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
178+
bool hcd_edpt_clear_stall(uint8_t daddr, uint8_t ep_addr);
179179

180180
//--------------------------------------------------------------------+
181181
// USBH implemented API

src/portable/ehci/ehci.c

Lines changed: 104 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,40 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
294294
ehci_data.regs->command_bm.async_adv_doorbell = 1;
295295
}
296296

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+
297331
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
298332
{
299333
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)
332366
regs->async_list_addr = (uint32_t) async_head;
333367

334368
//------------- 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;
367371

368372
hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t));
369373

@@ -491,8 +495,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
491495
ehci_qhd_t* qhd;
492496
ehci_qtd_t* qtd;
493497

494-
if ( epnum == 0 )
495-
{
498+
if (epnum == 0) {
496499
qhd = qhd_control(dev_addr);
497500
qtd = qtd_control(dev_addr);
498501

@@ -501,8 +504,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
501504
// first first data toggle is always 1 (data & setup stage)
502505
qtd->data_toggle = 1;
503506
qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
504-
}else
505-
{
507+
} else {
506508
qhd = qhd_get_from_addr(dev_addr, ep_addr);
507509
qtd = qtd_find_free();
508510
TU_ASSERT(qtd);
@@ -531,10 +533,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
531533
return true;
532534
}
533535

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)
535537
{
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));
538541
// TODO reset data toggle ?
539542
return true;
540543
}
@@ -546,22 +549,22 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
546549
// async_advance is handshake between usb stack & ehci controller.
547550
// This isr mean it is safe to modify previously removed queue head from async list.
548551
// 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)
550554
{
551555
(void) rhport;
552556

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) {
558560
qhd_pool[i].removing = 0;
559-
qhd_pool[i].used = 0;
561+
qhd_pool[i].used = 0;
560562
}
561563
}
562564
}
563565

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)
565568
{
566569
// NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
567570
if (ehci_data.regs->portsc_bm.current_connect_status)
@@ -574,13 +577,13 @@ static void port_connect_status_change_isr(uint8_t rhport)
574577
}
575578
}
576579

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)
578582
{
579583
// free all TDs from the head td to the first active TD
580584
while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active)
581585
{
582586
ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head;
583-
584587
hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
585588

586589
bool const is_ioc = (qtd->int_on_complete != 0);
@@ -600,7 +603,8 @@ static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd)
600603
}
601604
}
602605

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)
604608
{
605609
ehci_qhd_t *p_qhd = async_head;
606610
do
@@ -611,46 +615,55 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
611615
if ( !p_qhd->qtd_overlay.halted ) {
612616
qhd_xfer_complete_isr(p_qhd);
613617
}
618+
614619
p_qhd = qhd_next(p_qhd);
615620
}while(p_qhd != async_head); // async list traversal, stop if loop around
616621
}
617622

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)
619625
{
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);
623628

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);
637644
}
638645
}
639-
break;
646+
break;
647+
648+
case EHCI_QTYPE_ITD:
649+
// TODO support hs ISO
650+
break;
640651

641-
case EHCI_QTYPE_ITD: // TODO support hs/fs ISO
642652
case EHCI_QTYPE_SITD:
643-
case EHCI_QTYPE_FSTN:
653+
// TODO support split ISO
654+
break;
644655

645-
default: break;
656+
case EHCI_QTYPE_FSTN:
657+
default:
658+
break;
646659
}
647660

648-
next_item = *list_next(&next_item);
649-
max_loop++;
661+
next_link = *list_next(&next_link);
650662
}
651663
}
652664

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)
654667
{
655668
volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay;
656669

@@ -666,22 +679,22 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
666679
xfer_result = XFER_RESULT_STALLED;
667680
}
668681

669-
p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
670-
671682
// if (XFER_RESULT_FAILED == xfer_result ) {
672683
// TU_LOG1(" QHD xfer err count: %d\n", qtd_overlay->err_count);
673684
// TU_BREAKPOINT(); // TODO skip unplugged device
674685
// while(1){}
675686
// }
676687

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 !?
679690

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
681695
qtd_remove_1st_from_qhd(p_qhd);
682696

683-
if ( 0 == p_qhd->ep_number )
684-
{
697+
if ( 0 == p_qhd->ep_number ) {
685698
// control cannot be halted --> clear all qtd list
686699
p_qhd->p_qtd_list_head = NULL;
687700
p_qhd->p_qtd_list_tail = NULL;
@@ -702,13 +715,15 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
702715
}
703716
}
704717

705-
static void xfer_error_isr(uint8_t hostid)
718+
TU_ATTR_ALWAYS_INLINE static inline
719+
void xfer_error_isr(uint8_t hostid)
706720
{
707721
//------------- async list -------------//
708722
ehci_qhd_t * const async_head = qhd_async_head(hostid);
709723
ehci_qhd_t *p_qhd = async_head;
710724
do
711725
{
726+
hcd_dcache_invalidate(p_qhd, sizeof(ehci_qhd_t));
712727
qhd_xfer_error_isr( p_qhd );
713728
p_qhd = qhd_next(p_qhd);
714729
}while(p_qhd != async_head); // async list traversal, stop if loop around
@@ -728,6 +743,8 @@ static void xfer_error_isr(uint8_t hostid)
728743
case EHCI_QTYPE_QHD:
729744
{
730745
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+
731748
qhd_xfer_error_isr(p_qhd_int);
732749
}
733750
break;
@@ -757,36 +774,31 @@ void hcd_int_handler(uint8_t rhport)
757774
return;
758775
}
759776

760-
if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER)
761-
{
777+
if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) {
762778
ehci_data.uframe_number += (FRAMELIST_SIZE << 3);
763779
regs->status = EHCI_INT_MASK_FRAMELIST_ROLLOVER; // Acknowledge
764780
}
765781

766-
if (int_status & EHCI_INT_MASK_PORT_CHANGE)
767-
{
782+
if (int_status & EHCI_INT_MASK_PORT_CHANGE) {
768783
// Including: Force port resume, over-current change, enable/disable change and connect status change.
769784
uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_W1C;
770785
print_portsc(regs);
771786

772-
if (regs->portsc_bm.connect_status_change)
773-
{
787+
if (regs->portsc_bm.connect_status_change) {
774788
port_connect_status_change_isr(rhport);
775789
}
776790

777791
regs->portsc |= port_status; // Acknowledge change bits in portsc
778792
regs->status = EHCI_INT_MASK_PORT_CHANGE; // Acknowledge
779793
}
780794

781-
if (int_status & EHCI_INT_MASK_ERROR)
782-
{
795+
if (int_status & EHCI_INT_MASK_ERROR) {
783796
xfer_error_isr(rhport);
784797
regs->status = EHCI_INT_MASK_ERROR; // Acknowledge
785798
}
786799

787800
//------------- 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) {
790802
async_list_xfer_complete_isr(qhd_async_head(rhport));
791803
regs->status = EHCI_INT_MASK_NXP_ASYNC; // Acknowledge
792804
}

0 commit comments

Comments
 (0)