Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OHCI usbh, tweaks and improvements #1491

Merged
merged 16 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/common/tusb_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@

#include "tusb_timeout.h" // TODO remove

//--------------------------------------------------------------------+
// Optional API implemented by application if needed
// TODO move to a more ovious place/file
//--------------------------------------------------------------------+

// flush data cache
TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);

// invalidate data cache
TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);

// Optional physical <-> virtual address translation
TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr);
TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr);

//--------------------------------------------------------------------+
// Internal Inline Functions
//--------------------------------------------------------------------+
Expand Down
5 changes: 3 additions & 2 deletions src/common/tusb_mcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
#define TUSB_MCU_H_

//--------------------------------------------------------------------+
// Port Specific
// TUP stand for TinyUSB Port (can be renamed)
// Port/Platform Specific
// TUP stand for TinyUSB Port/Platform (can be renamed)
//--------------------------------------------------------------------+

//------------- Unaligned Memory Access -------------//
Expand All @@ -55,6 +55,7 @@
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_USBIP_OHCI
#define TUP_OHCI_RHPORTS 2

#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
// TODO USB0 has 6, USB1 has 4
Expand Down
2 changes: 1 addition & 1 deletion src/host/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ typedef struct
// Controller API
//--------------------------------------------------------------------+

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

// Initialize controller to host mode
Expand Down
4 changes: 4 additions & 0 deletions src/osal/osal_none.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
// TASK API
//--------------------------------------------------------------------+

#if CFG_TUH_ENABLED
// currently only needed/available in host mode
void osal_task_delay(uint32_t msec);
#endif

//--------------------------------------------------------------------+
// Binary Semaphore API
Expand Down
150 changes: 103 additions & 47 deletions src/portable/ohci/ohci.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@

#if CFG_TUH_ENABLED && defined(TUP_USBIP_OHCI)

#ifndef TUP_OHCI_RHPORTS
#error OHCI is enabled, but TUP_OHCI_RHPORTS is not defined.
#endif

//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -157,6 +161,21 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
//--------------------------------------------------------------------+
// USBH-HCD API
//--------------------------------------------------------------------+

// If your system requires separation of virtual and physical memory, implement
// tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application.
TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address)
{
if (tusb_app_virt_to_phys) return tusb_app_virt_to_phys(virtual_address);
return virtual_address;
}

TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address)
{
if (tusb_app_phys_to_virt) return tusb_app_phys_to_virt(physical_address);
return physical_address;
}

// Initialization according to 5.1.1.4
bool hcd_init(uint8_t rhport)
{
Expand All @@ -166,37 +185,65 @@ bool hcd_init(uint8_t rhport)
tu_memclr(&ohci_data, sizeof(ohci_data_t));
for(uint8_t i=0; i<32; i++)
{ // assign all interrupt pointers to period head ed
ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed;
ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed);
}

ohci_data.control[0].ed.skip = 1;
ohci_data.bulk_head_ed.skip = 1;
ohci_data.period_head_ed.skip = 1;

//If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
if (OHCI_REG->control_bit.interrupt_routing == 1)
{
OHCI_REG->command_status_bit.ownership_change_request = 1;
while (OHCI_REG->control_bit.interrupt_routing == 1) {}
}

//If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL)
{
//Wait 20 ms. (Ref Usb spec 7.1.7.7)
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME;

#if CFG_TUSB_OS != OPT_OS_NONE
// os_none implement task delay using usb frame counter which is not started yet
// therefore cause infinite delay.
// TODO find a way to delay in case of os none e.g __nop
osal_task_delay(20);
#endif
}

// reset controller
OHCI_REG->command_status_bit.controller_reset = 1;
while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us

//------------- init ohci registers -------------//
OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed;
OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed;
OHCI_REG->hcca = (uint32_t) &ohci_data.hcca;
OHCI_REG->control_head_ed = (uint32_t) _phys_addr(&ohci_data.control[0].ed);
OHCI_REG->bulk_head_ed = (uint32_t) _phys_addr(&ohci_data.bulk_head_ed);
OHCI_REG->hcca = (uint32_t) _phys_addr(&ohci_data.hcca);

OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts
OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits
OHCI_REG->interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK |
OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
OHCI_INT_MASTER_ENABLE_MASK;

OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
OHCI_REG->control = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous

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

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)
OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports

#if CFG_TUSB_OS != OPT_OS_NONE
// TODO as above delay
osal_task_delay(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up
#endif

return true;
}

Expand All @@ -212,8 +259,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
//--------------------------------------------------------------------+
void hcd_port_reset(uint8_t hostid)
{
(void) hostid;
OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
OHCI_REG->rhport_status[hostid] = RHPORT_PORT_RESET_STATUS_MASK;
}

void hcd_port_reset_end(uint8_t rhport)
Expand All @@ -223,14 +269,12 @@ void hcd_port_reset_end(uint8_t rhport)

bool hcd_port_connect_status(uint8_t hostid)
{
(void) hostid;
return OHCI_REG->rhport_status_bit[0].current_connect_status;
return OHCI_REG->rhport_status_bit[hostid].current_connect_status;
}

tusb_speed_t hcd_port_speed_get(uint8_t hostid)
{
(void) hostid;
return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
return OHCI_REG->rhport_status_bit[hostid].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
}

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

p_td->current_buffer_pointer = data_ptr;
p_td->buffer_end = total_bytes ? (data_ptr + total_bytes-1) : data_ptr;
p_td->current_buffer_pointer = _phys_addr(data_ptr);
p_td->buffer_end = total_bytes ? (_phys_addr(data_ptr + total_bytes - 1)) : (uint8_t *)p_td->current_buffer_pointer;
}

static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
Expand Down Expand Up @@ -345,7 +389,7 @@ static ohci_ed_t * ed_find_free(void)
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
{
p_ed->next = p_pre->next;
p_pre->next = (uint32_t) p_ed;
p_pre->next = (uint32_t) _phys_addr(p_ed);
}

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

while( p_prev->next )
{
ohci_ed_t* ed = (ohci_ed_t*) p_prev->next;
ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)p_prev->next);

if (ed->dev_addr == dev_addr)
{
// unlink ed
// Prevent Host Controller from processing this ED while we remove it
ed->skip = 1;

// unlink ed, will also move up p_prev
p_prev->next = ed->next;

// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
ed->next = (uint32_t) p_head;
ed->next = (uint32_t) _phys_addr(p_head);
ed->used = 0;
ed->skip = 0;
}else
{
p_prev = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
}

// check next valid since we could remove it
if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next;
}
}

Expand All @@ -386,11 +434,11 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
// tail is always NULL
if ( tu_align16(p_ed->td_head.address) == 0 )
{ // TD queue is empty --> head = TD
p_ed->td_head.address |= (uint32_t) p_gtd;
p_ed->td_head.address |= (uint32_t) _phys_addr(p_gtd);
}
else
{ // TODO currently only support queue up to 2 TD each endpoint at a time
((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd;
((ohci_gtd_t*) tu_align16((uint32_t)_virt_addr((void *)p_ed->td_head.address)))->next = (uint32_t) _phys_addr(p_gtd);
}
}

Expand Down Expand Up @@ -443,10 +491,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
qtd->index = dev_addr;
qtd->pid = PID_SETUP;
qtd->data_toggle = GTD_DT_DATA0;
qtd->delay_interrupt = 0;
qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;

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

OHCI_REG->command_status_bit.control_list_filled = 1;

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

ed->td_head.address = (uint32_t) gtd;
ed->td_head.address = (uint32_t) _phys_addr(gtd);

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

gtd_init(gtd, buffer, buflen);
gtd->index = ed-ohci_data.ed_pool;
gtd->delay_interrupt = 0;
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;

td_insert_to_ed(ed, gtd);

Expand Down Expand Up @@ -520,16 +568,17 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)

while(td_head != NULL)
{
td_head = _virt_addr(td_head);
uint32_t next = td_head->next;

// make current's item become reverse's first item
td_head->next = (uint32_t) td_reverse_head;
td_reverse_head = td_head;
td_reverse_head = _phys_addr(td_head);

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

return td_reverse_head;
return _virt_addr(td_reverse_head);
}

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

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

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

td_head = (ohci_td_item_t*) td_head->next;
td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next);
}
}

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

if (int_status == 0) return;

// Disable MIE as per OHCI spec 5.3
OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK;

// Frame number overflow
if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
{
Expand All @@ -620,29 +673,30 @@ void hcd_int_handler(uint8_t hostid)
//------------- RootHub status -------------//
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
{
uint32_t const rhport_status = OHCI_REG->rhport_status[0] & RHPORT_ALL_CHANGE_MASK;

// TODO dual port is not yet supported
if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
for (int i = 0; i < TUP_OHCI_RHPORTS; i++)
{
// TODO check if remote wake-up
if ( OHCI_REG->rhport_status_bit[0].current_connect_status )
uint32_t const rhport_status = OHCI_REG->rhport_status[i] & RHPORT_ALL_CHANGE_MASK;
if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
{
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
hcd_event_device_attach(hostid, true);
}else
{
hcd_event_device_remove(hostid, true);
// TODO check if remote wake-up
if ( OHCI_REG->rhport_status_bit[i].current_connect_status )
{
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
OHCI_REG->rhport_status[i] = RHPORT_PORT_RESET_STATUS_MASK;
hcd_event_device_attach(i, true);
}else
{
hcd_event_device_remove(i, true);
}
}
}

if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
{
if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
{

}
}

OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt
OHCI_REG->rhport_status[i] = rhport_status; // acknowledge all interrupt
}
}

//------------- Transfer Complete -------------//
Expand All @@ -652,6 +706,8 @@ void hcd_int_handler(uint8_t hostid)
}

OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt

OHCI_REG->interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK; // Enable MIE
}
//--------------------------------------------------------------------+
// HELPER
Expand Down
Loading