/* * SPDX-FileCopyrightText: Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * SPDX-FileCopyrightText: Copyright (c) 2020 Ha Thach (tinyusb.org) * SPDX-License-Identifier: MIT * * This file is part of the TinyUSB stack. */ #ifndef RP2040_COMMON_H_ #define RP2040_COMMON_H_ #include "pico.h" #include "hardware/structs/usb.h" #include "hardware/irq.h" #include "hardware/resets.h" #include "hardware/timer.h" #include "pico/critical_section.h" #include "common/tusb_common.h" #include "osal/osal.h" #include "common/tusb_fifo.h" #if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE) #error TinyUSB device and host mode not supported at the same time #endif #if defined(PICO_RP2040) && PICO_RP2040 == 1 // RP2040-E2 USB device endpoint abort is not cleared. #define CFG_TUSB_RP2_ERRATA_E2 1 // RP2040-E4: USB host writes to upper half of buffer status in single buffered mode. #define CFG_TUSB_RP2_ERRATA_E4 1 // RP2040-E5: USB device fails to exit RESET state on busy USB bus. #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX) #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX #endif // RP2040-E15: USB Device controller will hang if certain bus errors occur during an IN transfer. #ifndef CFG_TUSB_RP2_ERRATA_E15 #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && PICO_RP2040_USB_DEVICE_UFRAME_FIX) #elif defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) #endif #endif #endif #ifndef CFG_TUSB_RP2_ERRATA_E2 #define CFG_TUSB_RP2_ERRATA_E2 0 #endif #ifndef CFG_TUSB_RP2_ERRATA_E4 #define CFG_TUSB_RP2_ERRATA_E4 0 #endif #ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX 0 #endif #ifndef CFG_TUSB_RP2_ERRATA_E15 #define CFG_TUSB_RP2_ERRATA_E15 0 #endif #if CFG_TUSB_RP2_ERRATA_E15 #undef PICO_RP2040_USB_FAST_IRQ #define PICO_RP2040_USB_FAST_IRQ 1 #endif #ifndef PICO_RP2040_USB_FAST_IRQ #define PICO_RP2040_USB_FAST_IRQ 0 #endif #if PICO_RP2040_USB_FAST_IRQ #define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x) #else #define __tusb_irq_path_func(x) x #endif // Flags we set by default in sie_ctrl (we add other bits on top) enum { SIE_CTRL_BASE = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS, SIE_CTRL_BASE_MASK = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS | USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS }; //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ #define usb_hw_set ((usb_hw_t *) hw_set_alias_untyped(usb_hw)) #define usb_hw_clear ((usb_hw_t *) hw_clear_alias_untyped(usb_hw)) #define pico_info(...) TU_LOG(2, __VA_ARGS__) #define pico_trace(...) TU_LOG(3, __VA_ARGS__) enum { EPSTATE_IDLE = 0, EPSTATE_ACTIVE, EPSTATE_PENDING, EPSTATE_PENDING_SETUP }; // Hardware information per endpoint typedef struct hw_endpoint { uint8_t ep_addr; uint8_t next_pid; uint8_t state; #if CFG_TUD_EDPT_DEDICATED_HWFIFO bool is_xfer_fifo; // transfer using fifo #endif #if CFG_TUD_ENABLED uint8_t future_bufid; // which buffer holds next data uint8_t future_len; // next data len #endif #if CFG_TUSB_RP2_ERRATA_E15 bool e15_bulk_in; // Errata15 device bulk in #endif #if CFG_TUH_ENABLED uint8_t dev_addr; uint8_t interrupt_num; // 1-15 for interrupt endpoints struct TU_ATTR_PACKED { uint8_t transfer_type : 2; uint8_t need_pre : 1; // preamble for low-speed device behind full speed hub }; #endif uint16_t max_packet_size; // max packet size also indicates configured uint8_t *dpram_buf; // Buffer pointer in usb dpram // transfer info union { uint8_t *user_buf; // User buffer in main memory tu_fifo_t *user_fifo; }; uint16_t remaining_len; uint16_t xferred_len; } hw_endpoint_t; #if CFG_TUSB_RP2_ERRATA_E15 extern volatile uint32_t e15_last_sof; #endif void rp2usb_init(void); // if usb hardware is in host mode TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; } extern critical_section_t rp2usb_lock; TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_enter(void) { critical_section_enter_blocking(&rp2usb_lock); } TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_exit(void) { critical_section_exit(&rp2usb_lock); } //--------------------------------------------------------------------+ // Hardware Endpoint //--------------------------------------------------------------------+ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id, bool is_rx); void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx); void rp2usb_reset_transfer(hw_endpoint_t *ep); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint *ep, __unused int delta) { // todo add critsec as necessary to prevent issues between worker and IRQ... // note that this is perhaps as simple as disabling IRQs because it would make // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. } //--------------------------------------------------------------------+ // Hardware Buffer //--------------------------------------------------------------------+ void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value); void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value); uint16_t bufctrl_prepare16(hw_endpoint_t *ep, uint8_t *dpram_buf, bool is_rx); TU_ATTR_ALWAYS_INLINE static inline uintptr_t hw_data_offset(uint8_t *buf) { // Remove usb base from buffer pointer return (uintptr_t)buf ^ (uintptr_t)usb_dpram; } #endif