From c96ff6b6d978bdcacc7cd49ad66ef00438cfb9f6 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 10 Apr 2026 16:52:15 +0700 Subject: add dwc2_clock_init() to have nrf54 initial sequence. currently stub for other ports. add board_uart_read() for nrf --- src/portable/synopsys/dwc2/dcd_dwc2.c | 4 +-- src/portable/synopsys/dwc2/dwc2_at32.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_bcm.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_common.h | 16 +++++----- src/portable/synopsys/dwc2/dwc2_efm32.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_esp32.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_gd32.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_nrf.h | 52 ++++++++++++++++++++++++++++++++ src/portable/synopsys/dwc2/dwc2_stm32.h | 6 ++++ src/portable/synopsys/dwc2/dwc2_xmc.h | 6 ++++ src/portable/synopsys/dwc2/hcd_dwc2.c | 4 ++- 11 files changed, 108 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index bdd3a59ed..da6ee894a 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -442,12 +442,12 @@ bool dcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { } bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rh_init; - dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_clock_init(rhport, rh_init->role); tu_memclr(&_dcd_data, sizeof(_dcd_data)); // Core Initialization + dwc2_regs_t* dwc2 = DWC2_REG(rhport); const bool is_hs_phy = dwc2_core_is_highspeed_phy(dwc2, TUD_OPT_HIGH_SPEED); const bool is_dma = dma_device_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_hs_phy, is_dma)); diff --git a/src/portable/synopsys/dwc2/dwc2_at32.h b/src/portable/synopsys/dwc2/dwc2_at32.h index 594d350b7..85fa9b20b 100644 --- a/src/portable/synopsys/dwc2/dwc2_at32.h +++ b/src/portable/synopsys/dwc2/dwc2_at32.h @@ -79,6 +79,12 @@ static const dwc2_controller_t _dwc2_controller[] = { #endif }; +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) { (void) role; const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum; diff --git a/src/portable/synopsys/dwc2/dwc2_bcm.h b/src/portable/synopsys/dwc2/dwc2_bcm.h index 50f3122e8..91cae821e 100644 --- a/src/portable/synopsys/dwc2/dwc2_bcm.h +++ b/src/portable/synopsys/dwc2/dwc2_bcm.h @@ -46,6 +46,12 @@ static const dwc2_controller_t _dwc2_controller[] = #define dcache_invalidate(_addr, _size) data_invalidate(_addr, _size) #define dcache_clean_invalidate(_addr, _size) data_clean_and_invalidate(_addr, _size) +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 9f28ab2e0..5c4798d21 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -38,13 +38,15 @@ #include "host/hcd.h" #endif -// Following symbols must be defined by port header -// - _dwc2_controller[]: array of controllers -// - DWC2_EP_MAX: largest EP counts of all controllers -// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset -// - dwc2_phy_deinit(dwc2, hs_phy_type): phy deinit to disable PHY power, only deinit the phy used by core -// - dwc2_dcd_int_enable/dwc2_dcd_int_disable -// - dwc2_remote_wakeup_delay +/* Following symbols must be defined by port header + - _dwc2_controller[]: array of controllers + - DWC2_EP_MAX: largest EP counts of all controllers + - dwc2_clock_init(): clock init call before + - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset + - dwc2_phy_deinit(dwc2, hs_phy_type): phy deinit to disable PHY power, only deinit the phy used by core + - dwc2_dcd_int_enable/dwc2_dcd_int_disable + - dwc2_remote_wakeup_delay +*/ #if defined(TUP_USBIP_DWC2_STM32) #include "dwc2_stm32.h" diff --git a/src/portable/synopsys/dwc2/dwc2_efm32.h b/src/portable/synopsys/dwc2/dwc2_efm32.h index 167f5b4cd..063360873 100644 --- a/src/portable/synopsys/dwc2/dwc2_efm32.h +++ b/src/portable/synopsys/dwc2/dwc2_efm32.h @@ -43,6 +43,12 @@ static const dwc2_controller_t _dwc2_controller[] = { .reg_base = DWC2_REG_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = 512 } }; +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 99ef9456f..6a10dc7f8 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -97,6 +97,12 @@ static void dwc2_int_handler_wrap(void* arg) { #endif } +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) { if (enabled) { esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED, diff --git a/src/portable/synopsys/dwc2/dwc2_gd32.h b/src/portable/synopsys/dwc2/dwc2_gd32.h index 396d17e30..28c3fde5e 100644 --- a/src/portable/synopsys/dwc2/dwc2_gd32.h +++ b/src/portable/synopsys/dwc2/dwc2_gd32.h @@ -57,6 +57,12 @@ static inline void __eclic_disable_interrupt (uint32_t irq){ *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 0; } +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { diff --git a/src/portable/synopsys/dwc2/dwc2_nrf.h b/src/portable/synopsys/dwc2/dwc2_nrf.h index fc9cd18bc..a1bf692f8 100644 --- a/src/portable/synopsys/dwc2/dwc2_nrf.h +++ b/src/portable/synopsys/dwc2/dwc2_nrf.h @@ -29,8 +29,20 @@ #ifndef TUSB_DWC2_NRF_H #define TUSB_DWC2_NRF_H +// NRF is device only without OTG support #include "nrf.h" +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" +#endif + +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #define DWC2_EP_MAX 16 // Use the auto-resolving peripheral pointer (respects TrustZone secure/non-secure mapping) @@ -44,6 +56,46 @@ static const dwc2_controller_t _dwc2_controller[] = { {.reg_base = DWC2_REG_BASE, .irqnum = USBHS_IRQn, .ep_count = 16, .otg_dfifo_depth = 3072}, }; +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; + + #if defined(NRF54LM20A_ENGA_XXAA) + // Start the USB voltage regulator + NRF_VREGUSB->TASKS_START = VREGUSB_TASKS_START_TASKS_START_Trigger; + + // Based on Zephyr usbhs_enable_core() in drivers/usb/udc/udc_dwc2_vendor_quirks.h + // Step 1: Power up core only (PHY not yet) + NRF_USBHS->ENABLE = USBHS_ENABLE_CORE_Msk; + + // Step 2: Override ID=Device (bit 31), and temporarily override VBUSVALID + NRF_USBHS->PHY.OVERRIDEVALUES = (USBHS_PHY_OVERRIDEVALUES_ID_Device << USBHS_PHY_OVERRIDEVALUES_ID_Pos); + NRF_USBHS->PHY.INPUTOVERRIDE = USBHS_PHY_INPUTOVERRIDE_ID_Msk | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; + + // Step 3: Release PHY power-on reset by enabling PHY + NRF_USBHS->ENABLE = USBHS_ENABLE_PHY_Msk | USBHS_ENABLE_CORE_Msk; + + // Step 4: Wait 45us for PHY clock to start + nrfx_coredep_delay_us(45); + + // Step 5: Release DWC2 reset + NRF_USBHS->TASKS_START = USBHS_TASKS_START_TASKS_START_Trigger; + + // Step 6: Wait for clock to start to avoid hang on too early register read + nrfx_coredep_delay_us(2); + + // Step 7: Clear VBUSVALID override (keep ID=Device override) + // DWC2 is now in Non-Driving opmode; D+ pull-up will activate when DWC2 clears DCTL SftDiscon + NRF_USBHS->PHY.INPUTOVERRIDE = USBHS_PHY_INPUTOVERRIDE_ID_Msk; + + // Barrier: USBHS wrapper (0x5005A000) and USBHSCORE (0x50020000) are separate + // peripheral blocks. Ensure the ENABLE/TASKS_START writes have propagated from + // the Cortex-M33 write buffer to hardware before anyone reads DWC2 core regs. + __DSB(); + #endif +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) { (void)rhport; (void)role; diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index 5f6e98224..e5379dfe3 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -162,6 +162,12 @@ static const dwc2_controller_t _dwc2_controller[] = { // SystemCoreClock is already included by family header // extern uint32_t SystemCoreClock; +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) { (void) role; const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum; diff --git a/src/portable/synopsys/dwc2/dwc2_xmc.h b/src/portable/synopsys/dwc2/dwc2_xmc.h index b946444ee..85b2a2633 100644 --- a/src/portable/synopsys/dwc2/dwc2_xmc.h +++ b/src/portable/synopsys/dwc2/dwc2_xmc.h @@ -42,6 +42,12 @@ static const dwc2_controller_t _dwc2_controller[] = { .reg_base = USB0_BASE, .irqnum = USB0_0_IRQn, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = 512 } }; +// MCU specific to enable dwc2 clock/power before any access to register +TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) { + (void) rhport; + (void) role; +} + TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index cd2ddf572..6098d6eaa 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -407,10 +407,12 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { // Initialize controller to host mode bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_clock_init(rhport, rh_init->role); + tu_memclr(&_hcd_data, sizeof(_hcd_data)); // Core Initialization + dwc2_regs_t* dwc2 = DWC2_REG(rhport); const bool is_hs_phy = dwc2_core_is_highspeed_phy(dwc2, _tuh_cfg.use_hs_phy); const bool is_dma = dma_host_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_hs_phy, is_dma)); -- cgit v1.3.1