Skip to content

Commit 8ead4e4

Browse files
committed
ohci: Use uncached alias to access EDs
This code is written very carefully to always use an uncached view of memory to read/write EDs. An uncached view must *always* be used, or else cache behavior can corrupt the ED. As part of this change, combine access into as few word-sized accesses as possible. This makes the code perform better. Doing this involves giving type names to the bitfields that make up the ED's data words.
1 parent 915d212 commit 8ead4e4

File tree

2 files changed

+78
-63
lines changed

2 files changed

+78
-63
lines changed

src/portable/ohci/ohci.c

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
199199
ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed);
200200
}
201201

202-
ohci_data.control[0].ed.skip = 1;
203-
ohci_data.bulk_head_ed.skip = 1;
204-
ohci_data.period_head_ed.skip = 1;
202+
ohci_data.control[0].ed.w0.skip = 1;
203+
ohci_data.bulk_head_ed.w0.skip = 1;
204+
ohci_data.period_head_ed.w0.skip = 1;
205205

206206
//If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
207207
if (OHCI_REG->control_bit.interrupt_routing == 1)
@@ -300,7 +300,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
300300
// addr0 serves as static head --> only set skip bit
301301
if ( dev_addr == 0 )
302302
{
303-
ohci_data.control[0].ed.skip = 1;
303+
hcd_dcache_uncached(ohci_data.control[0].ed.w0).skip = 1;
304304
}else
305305
{
306306
// remove control
@@ -323,11 +323,11 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
323323
//--------------------------------------------------------------------+
324324
// List Helper
325325
//--------------------------------------------------------------------+
326-
static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed)
326+
static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_word0 w0)
327327
{
328-
return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL :
329-
(p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS :
330-
(p_ed->is_interrupt_xfer) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
328+
return (w0.ep_number == 0 ) ? TUSB_XFER_CONTROL :
329+
(w0.is_iso ) ? TUSB_XFER_ISOCHRONOUS :
330+
(w0.is_interrupt_xfer) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
331331
}
332332

333333
static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t ep_addr, uint8_t xfer_type, uint8_t interval)
@@ -337,21 +337,25 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t
337337
// address 0 is used as async head, which always on the list --> cannot be cleared
338338
if (dev_addr != 0)
339339
{
340-
tu_memclr(p_ed, sizeof(ohci_ed_t));
340+
hcd_dcache_uncached(p_ed->td_tail) = 0;
341+
hcd_dcache_uncached(p_ed->td_head).address = 0;
342+
hcd_dcache_uncached(p_ed->next) = 0;
341343
}
342344

343345
tuh_bus_info_t bus_info;
344346
tuh_bus_info_get(dev_addr, &bus_info);
345347

346-
p_ed->dev_addr = dev_addr;
347-
p_ed->ep_number = ep_addr & 0x0F;
348-
p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
349-
p_ed->speed = bus_info.speed;
350-
p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
351-
p_ed->max_packet_size = ep_size;
352-
353-
p_ed->used = 1;
354-
p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
348+
ohci_ed_word0 w0 = {.u = 0};
349+
w0.dev_addr = dev_addr;
350+
w0.ep_number = ep_addr & 0x0F;
351+
w0.pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
352+
w0.speed = bus_info.speed;
353+
w0.is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
354+
w0.max_packet_size = ep_size;
355+
356+
w0.used = 1;
357+
w0.is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
358+
hcd_dcache_uncached(p_ed->w0) = w0;
355359
}
356360

357361
static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) {
@@ -381,8 +385,9 @@ static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
381385

382386
for(uint32_t i=0; i<ED_MAX; i++)
383387
{
384-
if ( (ed_pool[i].dev_addr == dev_addr) &&
385-
ep_addr == tu_edpt_addr(ed_pool[i].ep_number, ed_pool[i].pid == PID_IN) )
388+
ohci_ed_word0 w0 = hcd_dcache_uncached(ed_pool[i].w0);
389+
if ( (w0.dev_addr == dev_addr) &&
390+
ep_addr == tu_edpt_addr(w0.ep_number, w0.pid == PID_IN) )
386391
{
387392
return &ed_pool[i];
388393
}
@@ -397,41 +402,44 @@ static ohci_ed_t * ed_find_free(void)
397402

398403
for(uint8_t i = 0; i < ED_MAX; i++)
399404
{
400-
if ( !ed_pool[i].used ) return &ed_pool[i];
405+
if ( !hcd_dcache_uncached(ed_pool[i].w0).used ) return &ed_pool[i];
401406
}
402407

403408
return NULL;
404409
}
405410

406411
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
407412
{
408-
p_ed->next = p_pre->next;
409-
p_pre->next = (uint32_t) _phys_addr(p_ed);
413+
hcd_dcache_uncached(p_ed->next) = hcd_dcache_uncached(p_pre->next);
414+
hcd_dcache_uncached(p_pre->next) = (uint32_t) _phys_addr(p_ed);
410415
}
411416

412417
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
413418
{
414419
ohci_ed_t* p_prev = p_head;
415420

416-
while( p_prev->next )
421+
uint32_t ed_pa;
422+
while( (ed_pa = hcd_dcache_uncached(p_prev->next)) )
417423
{
418-
ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
424+
ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)ed_pa);
419425

420-
if (ed->dev_addr == dev_addr)
426+
if (hcd_dcache_uncached(ed->w0).dev_addr == dev_addr)
421427
{
422428
// Prevent Host Controller from processing this ED while we remove it
423-
ed->skip = 1;
429+
hcd_dcache_uncached(ed->w0).skip = 1;
424430

425431
// unlink ed, will also move up p_prev
426-
p_prev->next = ed->next;
432+
hcd_dcache_uncached(p_prev->next) = hcd_dcache_uncached(ed->next);
427433

428434
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
429-
ed->next = (uint32_t) _phys_addr(p_head);
430-
ed->used = 0;
431-
ed->skip = 0;
435+
hcd_dcache_uncached(ed->next) = (uint32_t) _phys_addr(p_head);
436+
ohci_ed_word0 w0 = hcd_dcache_uncached(ed->w0);
437+
w0.used = 0;
438+
w0.skip = 0;
439+
hcd_dcache_uncached(ed->w0) = w0;
432440
}else
433441
{
434-
p_prev = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
442+
p_prev = (ohci_ed_t*) _virt_addr((void *)ed_pa);
435443
}
436444
}
437445
}
@@ -477,7 +485,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
477485
// control of dev0 is used as static async head
478486
if ( dev_addr == 0 )
479487
{
480-
p_ed->skip = 0; // only need to clear skip bit
488+
hcd_dcache_uncached(p_ed->w0).skip = 0; // only need to clear skip bit
481489
return true;
482490
}
483491

@@ -519,7 +527,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
519527
hcd_dcache_clean(qtd, sizeof(ohci_gtd_t));
520528

521529
//------------- Attach TDs list to Control Endpoint -------------//
522-
ed->td_head.address = (uint32_t) _phys_addr(qtd);
530+
hcd_dcache_uncached(ed->td_head.address) = (uint32_t) _phys_addr(qtd);
523531

524532
OHCI_REG->command_status_bit.control_list_filled = 1;
525533

@@ -554,12 +562,13 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
554562
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
555563
hcd_dcache_clean(gtd, sizeof(ohci_gtd_t));
556564

557-
ed->td_head.address = (uint32_t) _phys_addr(gtd);
565+
hcd_dcache_uncached(ed->td_head).address = (uint32_t) _phys_addr(gtd);
558566

559567
OHCI_REG->command_status_bit.control_list_filled = 1;
560568
}else
561569
{
562570
ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr);
571+
tusb_xfer_type_t xfer_type = ed_get_xfer_type( hcd_dcache_uncached(ed->w0) );
563572
ohci_gtd_t *gtd = (ohci_gtd_t *)_virt_addr((void *)hcd_dcache_uncached(ed->td_tail));
564573

565574
gtd_init(gtd, buffer, buflen);
@@ -576,7 +585,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
576585

577586
hcd_dcache_uncached(ed->td_tail) = (uint32_t)_phys_addr(new_gtd);
578587

579-
tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
580588
if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
581589
}
582590

@@ -595,13 +603,12 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
595603
(void) rhport;
596604
ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);
597605

598-
p_ed->is_stalled = 0;
599-
p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL
600-
601-
p_ed->td_head.toggle = 0; // reset data toggle
602-
p_ed->td_head.halted = 0;
606+
ohci_ed_td_head td_head = hcd_dcache_uncached(p_ed->td_head);
607+
td_head.toggle = 0; // reset data toggle
608+
td_head.halted = 0;
609+
hcd_dcache_uncached(p_ed->td_head) = td_head;
603610

604-
if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1;
611+
if ( TUSB_XFER_BULK == ed_get_xfer_type(hcd_dcache_uncached(p_ed->w0)) ) OHCI_REG->command_status_bit.bulk_list_filled = 1;
605612

606613
return true;
607614
}
@@ -665,7 +672,7 @@ static void done_queue_isr(uint8_t hostid)
665672
(void) hostid;
666673

667674
// done head is written in reversed order of completion --> need to reverse the done queue first
668-
ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
675+
ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(hcd_dcache_uncached(ohci_data.hcca).done_head) );
669676
ohci_data.hcca.done_head = 0;
670677

671678
while( td_head != NULL )

src/portable/ohci/ohci.h

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -117,34 +117,42 @@ typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LI
117117

118118
TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16, "size is not correct" );
119119

120+
typedef union {
121+
struct {
122+
uint32_t dev_addr : 7;
123+
uint32_t ep_number : 4;
124+
uint32_t pid : 2;
125+
uint32_t speed : 1;
126+
uint32_t skip : 1;
127+
uint32_t is_iso : 1;
128+
uint32_t max_packet_size : 11;
129+
// HCD: make use of 5 reserved bits
130+
uint32_t used : 1;
131+
uint32_t is_interrupt_xfer : 1;
132+
uint32_t : 3;
133+
};
134+
uint32_t u;
135+
} ohci_ed_word0;
136+
137+
typedef union {
138+
uint32_t address;
139+
struct {
140+
uint32_t halted : 1;
141+
uint32_t toggle : 1;
142+
uint32_t : 30;
143+
};
144+
} ohci_ed_td_head;
145+
120146
typedef struct TU_ATTR_ALIGNED(16)
121147
{
122148
// Word 0
123-
uint32_t dev_addr : 7;
124-
uint32_t ep_number : 4;
125-
uint32_t pid : 2;
126-
uint32_t speed : 1;
127-
uint32_t skip : 1;
128-
uint32_t is_iso : 1;
129-
uint32_t max_packet_size : 11;
130-
// HCD: make use of 5 reserved bits
131-
uint32_t used : 1;
132-
uint32_t is_interrupt_xfer : 1;
133-
uint32_t is_stalled : 1;
134-
uint32_t : 2;
149+
ohci_ed_word0 w0;
135150

136151
// Word 1
137152
uint32_t td_tail;
138153

139154
// Word 2
140-
volatile union {
141-
uint32_t address;
142-
struct {
143-
uint32_t halted : 1;
144-
uint32_t toggle : 1;
145-
uint32_t : 30;
146-
};
147-
}td_head;
155+
volatile ohci_ed_td_head td_head;
148156

149157
// Word 3: next ED
150158
uint32_t next;

0 commit comments

Comments
 (0)