Skip to content

Commit 73afca1

Browse files
authored
Merge pull request hathach#1491 from Ryzee119/ohci_fixes
OHCI usbh, tweaks and improvements
2 parents b66c2d5 + eca96c6 commit 73afca1

File tree

6 files changed

+153
-56
lines changed

6 files changed

+153
-56
lines changed

src/common/tusb_common.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@
7575

7676
#include "tusb_timeout.h" // TODO remove
7777

78+
//--------------------------------------------------------------------+
79+
// Optional API implemented by application if needed
80+
// TODO move to a more ovious place/file
81+
//--------------------------------------------------------------------+
82+
83+
// flush data cache
84+
TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);
85+
86+
// invalidate data cache
87+
TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);
88+
89+
// Optional physical <-> virtual address translation
90+
TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr);
91+
TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr);
92+
7893
//--------------------------------------------------------------------+
7994
// Internal Inline Functions
8095
//--------------------------------------------------------------------+

src/common/tusb_mcu.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
#define TUSB_MCU_H_
2929

3030
//--------------------------------------------------------------------+
31-
// Port Specific
32-
// TUP stand for TinyUSB Port (can be renamed)
31+
// Port/Platform Specific
32+
// TUP stand for TinyUSB Port/Platform (can be renamed)
3333
//--------------------------------------------------------------------+
3434

3535
//------------- Unaligned Memory Access -------------//
@@ -55,6 +55,7 @@
5555
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
5656
#define TUP_DCD_ENDPOINT_MAX 16
5757
#define TUP_USBIP_OHCI
58+
#define TUP_OHCI_RHPORTS 2
5859

5960
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
6061
// TODO USB0 has 6, USB1 has 4

src/host/hcd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ typedef struct
106106
// Controller API
107107
//--------------------------------------------------------------------+
108108

109-
// optional hcd configuration, called by tuh_config()
109+
// optional hcd configuration, called by tuh_configure()
110110
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_ATTR_WEAK;
111111

112112
// Initialize controller to host mode

src/osal/osal_none.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
// TASK API
3636
//--------------------------------------------------------------------+
3737

38+
#if CFG_TUH_ENABLED
39+
// currently only needed/available in host mode
40+
void osal_task_delay(uint32_t msec);
41+
#endif
3842

3943
//--------------------------------------------------------------------+
4044
// Binary Semaphore API

src/portable/ohci/ohci.c

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828

2929
#if CFG_TUH_ENABLED && defined(TUP_USBIP_OHCI)
3030

31+
#ifndef TUP_OHCI_RHPORTS
32+
#error OHCI is enabled, but TUP_OHCI_RHPORTS is not defined.
33+
#endif
34+
3135
//--------------------------------------------------------------------+
3236
// INCLUDE
3337
//--------------------------------------------------------------------+
@@ -157,6 +161,21 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
157161
//--------------------------------------------------------------------+
158162
// USBH-HCD API
159163
//--------------------------------------------------------------------+
164+
165+
// If your system requires separation of virtual and physical memory, implement
166+
// tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application.
167+
TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address)
168+
{
169+
if (tusb_app_virt_to_phys) return tusb_app_virt_to_phys(virtual_address);
170+
return virtual_address;
171+
}
172+
173+
TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address)
174+
{
175+
if (tusb_app_phys_to_virt) return tusb_app_phys_to_virt(physical_address);
176+
return physical_address;
177+
}
178+
160179
// Initialization according to 5.1.1.4
161180
bool hcd_init(uint8_t rhport)
162181
{
@@ -166,37 +185,65 @@ bool hcd_init(uint8_t rhport)
166185
tu_memclr(&ohci_data, sizeof(ohci_data_t));
167186
for(uint8_t i=0; i<32; i++)
168187
{ // assign all interrupt pointers to period head ed
169-
ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed;
188+
ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed);
170189
}
171190

172191
ohci_data.control[0].ed.skip = 1;
173192
ohci_data.bulk_head_ed.skip = 1;
174193
ohci_data.period_head_ed.skip = 1;
175194

195+
//If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
196+
if (OHCI_REG->control_bit.interrupt_routing == 1)
197+
{
198+
OHCI_REG->command_status_bit.ownership_change_request = 1;
199+
while (OHCI_REG->control_bit.interrupt_routing == 1) {}
200+
}
201+
202+
//If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
203+
else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
204+
OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL)
205+
{
206+
//Wait 20 ms. (Ref Usb spec 7.1.7.7)
207+
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME;
208+
209+
#if CFG_TUSB_OS != OPT_OS_NONE
210+
// os_none implement task delay using usb frame counter which is not started yet
211+
// therefore cause infinite delay.
212+
// TODO find a way to delay in case of os none e.g __nop
213+
osal_task_delay(20);
214+
#endif
215+
}
216+
176217
// reset controller
177218
OHCI_REG->command_status_bit.controller_reset = 1;
178219
while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us
179220

180221
//------------- init ohci registers -------------//
181-
OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed;
182-
OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed;
183-
OHCI_REG->hcca = (uint32_t) &ohci_data.hcca;
222+
OHCI_REG->control_head_ed = (uint32_t) _phys_addr(&ohci_data.control[0].ed);
223+
OHCI_REG->bulk_head_ed = (uint32_t) _phys_addr(&ohci_data.bulk_head_ed);
224+
OHCI_REG->hcca = (uint32_t) _phys_addr(&ohci_data.hcca);
184225

185226
OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts
186227
OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits
187228
OHCI_REG->interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK |
188229
OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
189230
OHCI_INT_MASTER_ENABLE_MASK;
190231

191-
OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
232+
OHCI_REG->control = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
192233
OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous
193234

194235
OHCI_REG->frame_interval = (OHCI_FMINTERVAL_FSMPS << 16) | OHCI_FMINTERVAL_FI;
236+
OHCI_REG->frame_interval ^= (1 << 31); //Must toggle when frame_interval is updated.
195237
OHCI_REG->periodic_start = (OHCI_FMINTERVAL_FI * 9) / 10; // Periodic start is 90% of frame interval
196238

197239
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power)
198240
OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports
199241

242+
#if CFG_TUSB_OS != OPT_OS_NONE
243+
// TODO as above delay
244+
osal_task_delay(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up
245+
#endif
246+
200247
return true;
201248
}
202249

@@ -212,8 +259,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
212259
//--------------------------------------------------------------------+
213260
void hcd_port_reset(uint8_t hostid)
214261
{
215-
(void) hostid;
216-
OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
262+
OHCI_REG->rhport_status[hostid] = RHPORT_PORT_RESET_STATUS_MASK;
217263
}
218264

219265
void hcd_port_reset_end(uint8_t rhport)
@@ -223,14 +269,12 @@ void hcd_port_reset_end(uint8_t rhport)
223269

224270
bool hcd_port_connect_status(uint8_t hostid)
225271
{
226-
(void) hostid;
227-
return OHCI_REG->rhport_status_bit[0].current_connect_status;
272+
return OHCI_REG->rhport_status_bit[hostid].current_connect_status;
228273
}
229274

230275
tusb_speed_t hcd_port_speed_get(uint8_t hostid)
231276
{
232-
(void) hostid;
233-
return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
277+
return OHCI_REG->rhport_status_bit[hostid].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
234278
}
235279

236280
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
@@ -308,8 +352,8 @@ static void gtd_init(ohci_gtd_t* p_td, uint8_t* data_ptr, uint16_t total_bytes)
308352
p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
309353
p_td->condition_code = OHCI_CCODE_NOT_ACCESSED;
310354

311-
p_td->current_buffer_pointer = data_ptr;
312-
p_td->buffer_end = total_bytes ? (data_ptr + total_bytes-1) : data_ptr;
355+
p_td->current_buffer_pointer = _phys_addr(data_ptr);
356+
p_td->buffer_end = total_bytes ? (_phys_addr(data_ptr + total_bytes - 1)) : (uint8_t *)p_td->current_buffer_pointer;
313357
}
314358

315359
static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
@@ -345,7 +389,7 @@ static ohci_ed_t * ed_find_free(void)
345389
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
346390
{
347391
p_ed->next = p_pre->next;
348-
p_pre->next = (uint32_t) p_ed;
392+
p_pre->next = (uint32_t) _phys_addr(p_ed);
349393
}
350394

351395
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
@@ -354,20 +398,24 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
354398

355399
while( p_prev->next )
356400
{
357-
ohci_ed_t* ed = (ohci_ed_t*) p_prev->next;
401+
ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
358402

359403
if (ed->dev_addr == dev_addr)
360404
{
361-
// unlink ed
405+
// Prevent Host Controller from processing this ED while we remove it
406+
ed->skip = 1;
407+
408+
// unlink ed, will also move up p_prev
362409
p_prev->next = ed->next;
363410

364411
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
365-
ed->next = (uint32_t) p_head;
412+
ed->next = (uint32_t) _phys_addr(p_head);
366413
ed->used = 0;
414+
ed->skip = 0;
415+
}else
416+
{
417+
p_prev = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
367418
}
368-
369-
// check next valid since we could remove it
370-
if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next;
371419
}
372420
}
373421

@@ -386,11 +434,11 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
386434
// tail is always NULL
387435
if ( tu_align16(p_ed->td_head.address) == 0 )
388436
{ // TD queue is empty --> head = TD
389-
p_ed->td_head.address |= (uint32_t) p_gtd;
437+
p_ed->td_head.address |= (uint32_t) _phys_addr(p_gtd);
390438
}
391439
else
392440
{ // TODO currently only support queue up to 2 TD each endpoint at a time
393-
((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd;
441+
((ohci_gtd_t*) tu_align16((uint32_t)_virt_addr((void *)p_ed->td_head.address)))->next = (uint32_t) _phys_addr(p_gtd);
394442
}
395443
}
396444

@@ -443,10 +491,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
443491
qtd->index = dev_addr;
444492
qtd->pid = PID_SETUP;
445493
qtd->data_toggle = GTD_DT_DATA0;
446-
qtd->delay_interrupt = 0;
494+
qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
447495

448496
//------------- Attach TDs list to Control Endpoint -------------//
449-
ed->td_head.address = (uint32_t) qtd;
497+
ed->td_head.address = (uint32_t) _phys_addr(qtd);
450498

451499
OHCI_REG->command_status_bit.control_list_filled = 1;
452500

@@ -470,9 +518,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
470518
gtd->index = dev_addr;
471519
gtd->pid = dir ? PID_IN : PID_OUT;
472520
gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1
473-
gtd->delay_interrupt = 0;
521+
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
474522

475-
ed->td_head.address = (uint32_t) gtd;
523+
ed->td_head.address = (uint32_t) _phys_addr(gtd);
476524

477525
OHCI_REG->command_status_bit.control_list_filled = 1;
478526
}else
@@ -484,7 +532,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
484532

485533
gtd_init(gtd, buffer, buflen);
486534
gtd->index = ed-ohci_data.ed_pool;
487-
gtd->delay_interrupt = 0;
535+
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
488536

489537
td_insert_to_ed(ed, gtd);
490538

@@ -520,16 +568,17 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
520568

521569
while(td_head != NULL)
522570
{
571+
td_head = _virt_addr(td_head);
523572
uint32_t next = td_head->next;
524573

525574
// make current's item become reverse's first item
526575
td_head->next = (uint32_t) td_reverse_head;
527-
td_reverse_head = td_head;
576+
td_reverse_head = _phys_addr(td_head);
528577

529578
td_head = (ohci_td_item_t*) next; // advance to next item
530579
}
531580

532-
return td_reverse_head;
581+
return _virt_addr(td_reverse_head);
533582
}
534583

535584
static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
@@ -565,6 +614,7 @@ static void done_queue_isr(uint8_t hostid)
565614

566615
// done head is written in reversed order of completion --> need to reverse the done queue first
567616
ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
617+
ohci_data.hcca.done_head = 0;
568618

569619
while( td_head != NULL )
570620
{
@@ -600,7 +650,7 @@ static void done_queue_isr(uint8_t hostid)
600650
hcd_event_xfer_complete(ed->dev_addr, tu_edpt_addr(ed->ep_number, dir), xferred_bytes, event, true);
601651
}
602652

603-
td_head = (ohci_td_item_t*) td_head->next;
653+
td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next);
604654
}
605655
}
606656

@@ -611,6 +661,9 @@ void hcd_int_handler(uint8_t hostid)
611661

612662
if (int_status == 0) return;
613663

664+
// Disable MIE as per OHCI spec 5.3
665+
OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK;
666+
614667
// Frame number overflow
615668
if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
616669
{
@@ -620,29 +673,30 @@ void hcd_int_handler(uint8_t hostid)
620673
//------------- RootHub status -------------//
621674
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
622675
{
623-
uint32_t const rhport_status = OHCI_REG->rhport_status[0] & RHPORT_ALL_CHANGE_MASK;
624-
625-
// TODO dual port is not yet supported
626-
if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
676+
for (int i = 0; i < TUP_OHCI_RHPORTS; i++)
627677
{
628-
// TODO check if remote wake-up
629-
if ( OHCI_REG->rhport_status_bit[0].current_connect_status )
678+
uint32_t const rhport_status = OHCI_REG->rhport_status[i] & RHPORT_ALL_CHANGE_MASK;
679+
if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
630680
{
631-
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
632-
OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
633-
hcd_event_device_attach(hostid, true);
634-
}else
635-
{
636-
hcd_event_device_remove(hostid, true);
681+
// TODO check if remote wake-up
682+
if ( OHCI_REG->rhport_status_bit[i].current_connect_status )
683+
{
684+
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
685+
OHCI_REG->rhport_status[i] = RHPORT_PORT_RESET_STATUS_MASK;
686+
hcd_event_device_attach(i, true);
687+
}else
688+
{
689+
hcd_event_device_remove(i, true);
690+
}
637691
}
638-
}
639692

640-
if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
641-
{
693+
if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
694+
{
642695

643-
}
696+
}
644697

645-
OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt
698+
OHCI_REG->rhport_status[i] = rhport_status; // acknowledge all interrupt
699+
}
646700
}
647701

648702
//------------- Transfer Complete -------------//
@@ -652,6 +706,8 @@ void hcd_int_handler(uint8_t hostid)
652706
}
653707

654708
OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt
709+
710+
OHCI_REG->interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK; // Enable MIE
655711
}
656712
//--------------------------------------------------------------------+
657713
// HELPER

0 commit comments

Comments
 (0)