summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-04-10 16:52:15 +0700
committerhathach <[email protected]>2026-04-10 16:52:15 +0700
commitc96ff6b6d978bdcacc7cd49ad66ef00438cfb9f6 (patch)
tree598f2488731826c2d85ff7ebe261b7dcc29dac02
parent8849515a0e1d1179d20a42b7b92c0e0a4d1aa131 (diff)
add dwc2_clock_init() to have nrf54 initial sequence. currently stub for other ports.
add board_uart_read() for nrf
-rw-r--r--hw/bsp/nrf/family.c131
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_at32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_bcm.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h16
-rw-r--r--src/portable/synopsys/dwc2/dwc2_efm32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_gd32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_nrf.h52
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_xmc.h6
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c4
12 files changed, 172 insertions, 77 deletions
diff --git a/hw/bsp/nrf/family.c b/hw/bsp/nrf/family.c
index 10f0fc460..31a6bac9e 100644
--- a/hw/bsp/nrf/family.c
+++ b/hw/bsp/nrf/family.c
@@ -81,39 +81,67 @@ enum {
// Forward USB interrupt events to TinyUSB IRQ Handler
#if defined(NRF54H20_XXAA) || defined(NRF54LM20A_ENGA_XXAA)
-#define USBD_IRQn USBHS_IRQn
+ #define USBD_IRQn USBHS_IRQn
void USBHS_IRQHandler(void) {
tusb_int_handler(0, true);
}
-#if defined(NRF54LM20A_ENGA_XXAA)
+ #if defined(NRF54LM20A_ENGA_XXAA)
static nrfx_uarte_t _uart_id = NRFX_UARTE_INSTANCE(20);
-#else
+ #else
static nrfx_uarte_t _uart_id = NRFX_UARTE_INSTANCE(120);
-#endif
-
-#else
+ #endif
-#ifdef NRF5340_XXAA
-#define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC
-#define VBUSDETECT_Msk USBREG_USBREGSTATUS_VBUSDETECT_Msk
-#define OUTPUTRDY_Msk USBREG_USBREGSTATUS_OUTPUTRDY_Msk
-#define GPIOTE_IRQn GPIOTE1_IRQn
#else
-#define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_RC
-#define VBUSDETECT_Msk POWER_USBREGSTATUS_VBUSDETECT_Msk
-#define OUTPUTRDY_Msk POWER_USBREGSTATUS_OUTPUTRDY_Msk
-#endif
+ #ifdef NRF5340_XXAA
+ #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC
+ #define VBUSDETECT_Msk USBREG_USBREGSTATUS_VBUSDETECT_Msk
+ #define OUTPUTRDY_Msk USBREG_USBREGSTATUS_OUTPUTRDY_Msk
+ #define GPIOTE_IRQn GPIOTE1_IRQn
+ #else
+ #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_RC
+ #define VBUSDETECT_Msk POWER_USBREGSTATUS_VBUSDETECT_Msk
+ #define OUTPUTRDY_Msk POWER_USBREGSTATUS_OUTPUTRDY_Msk
+ #endif
-#if CFG_TUSB_OS != OPT_OS_ZEPHYR
+ #if CFG_TUSB_OS != OPT_OS_ZEPHYR
static nrfx_uarte_t _uart_id = NRFX_UARTE_INSTANCE(0);
-#endif
+ #endif
void USBD_IRQHandler(void) {
- tud_int_handler(0);
+ tusb_int_handler(0, true);
}
#endif
+//--------------------------------------------------------------------+
+// UART RX ring buffer and event handler
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS != OPT_OS_ZEPHYR
+
+#define UART_RX_BUFSIZE 64
+
+static uint8_t rx_fifo[UART_RX_BUFSIZE];
+static volatile uint16_t rx_fifo_head = 0;
+static volatile uint16_t rx_fifo_tail = 0;
+
+static uint8_t rx_dma_buf[1]; // 1-byte DMA target for continuous RX
+
+static void uart_event_handler(nrfx_uarte_event_t const* p_event, void* p_context) {
+ (void) p_context;
+ if (p_event->type == NRFX_UARTE_EVT_RX_DONE) {
+ for (size_t i = 0; i < p_event->data.rx.length; i++) {
+ uint16_t next_head = (rx_fifo_head + 1) & (UART_RX_BUFSIZE - 1);
+ if (next_head != rx_fifo_tail) {
+ rx_fifo[rx_fifo_head] = p_event->data.rx.p_buffer[i];
+ rx_fifo_head = next_head;
+ }
+ }
+ // re-arm 1-byte RX
+ nrfx_uarte_rx(&_uart_id, rx_dma_buf, sizeof(rx_dma_buf));
+ }
+}
+
+#endif
// tinyusb function that handles power event (detected, ready, removed)
// We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled.
@@ -136,7 +164,6 @@ static nrfx_gpiote_t _gpiote = NRFX_GPIOTE_INSTANCE(0);
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
-
void board_init(void) {
#if !defined(NRF54H20_XXAA) && !defined(NRF54LM20A_ENGA_XXAA)
// stop LF clock just in case we jump from application without reset
@@ -172,12 +199,14 @@ void board_init(void) {
#if CFG_TUSB_OS != OPT_OS_ZEPHYR
// UART
+ static uint8_t uart_tx_cache[64];
nrfx_uarte_config_t uart_cfg = {
.txd_pin = UART_TX_PIN,
.rxd_pin = UART_RX_PIN,
.rts_pin = NRF_UARTE_PSEL_DISCONNECTED,
.cts_pin = NRF_UARTE_PSEL_DISCONNECTED,
.p_context = NULL,
+ .tx_cache = { .p_buffer = uart_tx_cache, .length = sizeof(uart_tx_cache) },
.baudrate = NRF_UARTE_BAUDRATE_115200, // CFG_BOARD_UART_BAUDRATE
.interrupt_priority = 7,
.config = {
@@ -186,63 +215,21 @@ void board_init(void) {
}
};
- nrfx_uarte_init(&_uart_id, &uart_cfg, NULL);
+ nrfx_uarte_init(&_uart_id, &uart_cfg, uart_event_handler);
+ // start continuous 1-byte RX
+ nrfx_uarte_rx(&_uart_id, rx_dma_buf, sizeof(rx_dma_buf));
#endif
//------------- USB -------------//
#if CFG_TUD_ENABLED
#if defined(NRF54LM20A_ENGA_XXAA)
- // Start the USB voltage regulator
- NRF_VREGUSB->TASKS_START = VREGUSB_TASKS_START_TASKS_START_Trigger;
-
// Request HFXO crystal clock for PCLK24M (required by USBHS core)
NRF_CLOCK->TASKS_XO24MSTART = CLOCK_TASKS_XO24MSTART_TASKS_XO24MSTART_Trigger;
while (!NRF_CLOCK->EVENTS_XO24MSTARTED) {}
NRF_CLOCK->EVENTS_XO24MSTARTED = 0;
#endif
-#if defined(NRF54H20_XXAA)
- // Enable the USBHS wrapper (core + PHY) before any DWC2 register access
- NRF_USBHS->ENABLE = (USBHS_ENABLE_PHY_Enabled << USBHS_ENABLE_PHY_Pos) |
- (USBHS_ENABLE_CORE_Enabled << USBHS_ENABLE_CORE_Pos);
- NRF_USBHS->TASKS_START = USBHS_TASKS_START_TASKS_START_Trigger;
- // Brief delay for PHY PLL lock and core power-up
- for (volatile int i = 0; i < 1000; i++) {}
-#endif
-
-#if defined(NRF54LM20A_ENGA_XXAA)
- // 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_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_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
-
// Priorities 0, 1, 4 (nRF52) are reserved for SoftDevice
// 2 is highest for application
NVIC_SetPriority(USBD_IRQn, 2);
@@ -329,11 +316,18 @@ size_t board_get_unique_id(uint8_t id[], size_t max_len) {
}
int board_uart_read(uint8_t* buf, int len) {
+#if CFG_TUSB_OS == OPT_OS_ZEPHYR
(void) buf;
(void) len;
return -1;
-// nrfx_err_t err = nrfx_uarte_rx(&_uart_id, buf, (size_t) len);
-// return NRFX_SUCCESS == err ? len : 0;
+#else
+ int count = 0;
+ while (count < len && rx_fifo_tail != rx_fifo_head) {
+ buf[count++] = rx_fifo[rx_fifo_tail];
+ rx_fifo_tail = (rx_fifo_tail + 1) & (UART_RX_BUFSIZE - 1);
+ }
+ return count;
+#endif
}
int board_uart_write(void const* buf, int len) {
@@ -341,7 +335,10 @@ int board_uart_write(void const* buf, int len) {
(void) buf;
return len;
#else
- nrfx_err_t err = nrfx_uarte_tx(&_uart_id, (uint8_t const*) buf, (size_t) len ,0);
+ if (nrfx_uarte_tx_in_progress(&_uart_id)) {
+ return 0;
+ }
+ nrfx_err_t err = nrfx_uarte_tx(&_uart_id, (uint8_t const*) buf, (size_t) len, 0);
return (NRFX_SUCCESS == err) ? len : 0;
#endif
}
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 <soc/nrfx_coredep.h>
+
+#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));