/* * SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org) * SPDX-License-Identifier: MIT * * This file is part of the TinyUSB stack. */ #ifndef TUSB_OHCI_H_ #define TUSB_OHCI_H_ #ifdef __cplusplus extern "C" { #endif //--------------------------------------------------------------------+ // OHCI CONFIGURATION & CONSTANTS //--------------------------------------------------------------------+ #define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enabled #define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS) // TODO merge OHCI with EHCI enum { OHCI_MAX_ITD = 4 }; #define ED_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX) #define GTD_MAX ED_MAX // tinyUSB's OHCI implementation caps number of EDs to 8 bits TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX"); #define GTD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16) #define ED_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16) #define ITD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 32) //--------------------------------------------------------------------+ // OHCI Data Structure //--------------------------------------------------------------------+ typedef struct { uint32_t interrupt_table[32]; volatile uint16_t frame_number; volatile uint16_t frame_pad; volatile uint32_t done_head; uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256 }ohci_hcca_t; // TU_ATTR_ALIGNED(256) TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" ); // An OHCI host controller is controlled using data structures placed in memory (RAM). // It needs to both read and write these data structures (as defined by the OHCI specification), // and this can be mentally conceptualized similar to two software threads running on // two different CPUs. In order to prevent a _data race_ where data gets corrupted, // the CPU and the OHCI host controller need to agree on how the memory should be accessed. // In this driver, we do this by transferring logical ownership of transfer descriptors (TDs) // between the CPU and the OHCI host controller. Only the device which holds the logical ownership // is allowed to read or write the TD. This ownership is not visible anywhere in the code, // but it instead must be inferred based on the logical state of the transfer. // // If dcache-supporting mode is enabled, we need to do additional manual cache operations // in order to correctly transfer this logical ownership and prevent data corruption. // In order to do this, we also choose to align each OHCI TD so that it doesn't // share CPU cache lines with other TDs. This is because manual cache operations // can only be performed on cache line granularity. In other words, one cache line is // the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs // in the same cache line, they would be required to always have the same logical ownership. // This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely. // common link item for gtd and itd for list travel typedef struct TU_ATTR_ALIGNED(16) { uint32_t reserved[2]; volatile uint32_t next; uint32_t reserved2; }ohci_td_item_t; typedef struct TU_ATTR_ALIGNED(GTD_ALIGN_SIZE) { // Word 0 uint32_t used : 1; uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer uint32_t : 9; // can be used uint32_t buffer_rounding : 1; uint32_t pid : 2; uint32_t delay_interrupt : 3; volatile uint32_t data_toggle : 2; volatile uint32_t error_count : 2; volatile uint32_t condition_code : 4; // Word 1 uint8_t* volatile current_buffer_pointer; // Word 2 : next TD volatile uint32_t next; // Word 3 uint8_t* buffer_end; } ohci_gtd_t; TU_VERIFY_STATIC(sizeof(ohci_gtd_t) == GTD_ALIGN_SIZE, "size is not correct" ); typedef union { struct { uint32_t dev_addr : 7; uint32_t ep_number : 4; uint32_t pid : 2; uint32_t speed : 1; uint32_t skip : 1; uint32_t is_iso : 1; uint32_t max_packet_size : 11; // HCD: make use of 5 reserved bits uint32_t used : 1; uint32_t is_interrupt_xfer : 1; uint32_t : 3; }; uint32_t value; } ohci_ed_word0_t; TU_VERIFY_STATIC(sizeof(ohci_ed_word0_t) == 4, "size is not correct" ); typedef union { uint32_t address; struct { uint32_t halted : 1; uint32_t toggle : 1; uint32_t : 30; }; } ohci_ed_word2_t; TU_VERIFY_STATIC(sizeof(ohci_ed_word2_t) == 4, "size is not correct" ); typedef struct TU_ATTR_ALIGNED(ED_ALIGN_SIZE) { ohci_ed_word0_t w0; // Word 0 uint32_t td_tail; // Word 1 volatile ohci_ed_word2_t td_head; // Word 2 uint32_t next; // Word 3 } ohci_ed_t; TU_VERIFY_STATIC(sizeof(ohci_ed_t) == ED_ALIGN_SIZE, "size is not correct" ); typedef struct TU_ATTR_ALIGNED(ITD_ALIGN_SIZE) { /*---------- Word 1 ----------*/ uint32_t starting_frame : 16; uint32_t : 5; // can be used uint32_t delay_interrupt : 3; uint32_t frame_count : 3; uint32_t : 1; // can be used volatile uint32_t condition_code : 4; /*---------- Word 2 ----------*/ uint32_t buffer_page0; // 12 lsb bits can be used /*---------- Word 3 ----------*/ volatile uint32_t next; /*---------- Word 4 ----------*/ uint32_t buffer_end; /*---------- Word 5-8 ----------*/ volatile uint16_t offset_packetstatus[8]; } ohci_itd_t; TU_VERIFY_STATIC(sizeof(ohci_itd_t) == ITD_ALIGN_SIZE, "size is not correct" ); typedef struct { uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits } gtd_extra_data_t; TU_VERIFY_STATIC(sizeof(gtd_extra_data_t) == 2, "size is not correct" ); // structure with member alignment required from large to small typedef struct TU_ATTR_ALIGNED(256) { ohci_hcca_t hcca; ohci_ed_t bulk_head_ed; // static bulk head (dummy) ohci_ed_t period_head_ed; // static periodic list head (dummy) // control endpoints has reserved resources struct { ohci_ed_t ed; ohci_gtd_t gtd; } control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1]; // ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32 ohci_ed_t ed_pool[ED_MAX]; ohci_gtd_t gtd_pool[GTD_MAX]; // extra data needed by TDs that can't fit in the TD struct gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1]; gtd_extra_data_t gtd_extra[GTD_MAX]; volatile uint16_t frame_number_hi; } ohci_data_t; //--------------------------------------------------------------------+ // OHCI Operational Register //--------------------------------------------------------------------+ //--------------------------------------------------------------------+ // OHCI Data Organization //--------------------------------------------------------------------+ typedef volatile struct { uint32_t revision; // 0x00 union { uint32_t control; // 0x04 struct { uint32_t control_bulk_service_ratio : 2; uint32_t periodic_list_enable : 1; uint32_t isochronous_enable : 1; uint32_t control_list_enable : 1; uint32_t bulk_list_enable : 1; uint32_t hc_functional_state : 2; uint32_t interrupt_routing : 1; uint32_t remote_wakeup_connected : 1; uint32_t remote_wakeup_enale : 1; uint32_t TU_RESERVED : 21; }control_bit; }; union { uint32_t command_status; // 0x08 struct { uint32_t controller_reset : 1; uint32_t control_list_filled : 1; uint32_t bulk_list_filled : 1; uint32_t ownership_change_request : 1; uint32_t : 12; uint32_t scheduling_overrun_count : 2; }command_status_bit; }; uint32_t interrupt_status; // 0x0C uint32_t interrupt_enable; // 0x10 uint32_t interrupt_disable; // 0x14 uint32_t hcca; // 0x18 uint32_t period_current_ed; // 0x1C uint32_t control_head_ed; // 0x20 uint32_t control_current_ed; // 0x24 uint32_t bulk_head_ed; // 0x28 uint32_t bulk_current_ed; // 0x2C uint32_t done_head; // 0x30 uint32_t frame_interval; // 0x34 uint32_t frame_remaining; // 0x38 uint32_t frame_number; // 0x3C uint32_t periodic_start; // 0x40 uint32_t lowspeed_threshold; // 0x44 union { uint32_t rh_descriptorA; // 0x48 struct { uint32_t number_downstream_ports : 8; uint32_t power_switching_mode : 1; uint32_t no_power_switching : 1; uint32_t device_type : 1; uint32_t overcurrent_protection_mode : 1; uint32_t no_over_current_protection : 1; uint32_t reserved : 11; uint32_t power_on_to_good_time : 8; } rh_descriptorA_bit; }; union { uint32_t rh_descriptorB; // 0x4C struct { uint32_t device_removable : 16; uint32_t port_power_control_mask : 16; } rh_descriptorB_bit; }; union { uint32_t rh_status; // 0x50 struct { uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power uint32_t over_current_indicator : 1; uint32_t : 13; uint32_t device_remote_wakeup_enable : 1; uint32_t local_power_status_change : 1; uint32_t over_current_indicator_change : 1; uint32_t : 13; uint32_t clear_remote_wakeup_enable : 1; }rh_status_bit; }; union { uint32_t rhport_status[TUP_OHCI_RHPORTS]; // 0x54 struct { uint32_t current_connect_status : 1; uint32_t port_enable_status : 1; uint32_t port_suspend_status : 1; uint32_t port_over_current_indicator : 1; uint32_t port_reset_status : 1; uint32_t : 3; uint32_t port_power_status : 1; uint32_t low_speed_device_attached : 1; uint32_t : 6; uint32_t connect_status_change : 1; uint32_t port_enable_status_change : 1; uint32_t port_suspend_status_change : 1; uint32_t port_over_current_indicator_change : 1; uint32_t port_reset_status_change : 1; uint32_t TU_RESERVED : 11; }rhport_status_bit[TUP_OHCI_RHPORTS]; }; }ohci_registers_t; TU_VERIFY_STATIC( sizeof(ohci_registers_t) == (0x54 + (4 * TUP_OHCI_RHPORTS)), "size is not correct"); #ifdef __cplusplus } #endif #endif /* TUSB_OHCI_H_ */