| /* |
| * 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_ */ |