From 6886cb896d480d053405d093f9a269177d87bab0 Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Thu, 9 Apr 2026 16:26:02 +0200 Subject: [PATCH 1/6] feat(device): Internal clock gating --- src/portable/synopsys/dwc2/dcd_dwc2.c | 101 ++++++++++++++++++++++- src/portable/synopsys/dwc2/dwc2_common.h | 15 ++++ src/portable/synopsys/dwc2/dwc2_esp32.h | 2 +- 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index e9fb847aa7..ebc20aa4c2 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -72,6 +72,14 @@ typedef struct { static dcd_data_t _dcd_data; +static volatile dcd_dwc2_link_state_t _dcd_link_state[DWC2_CONTROLLER_COUNT]; + +TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set(uint8_t rhport, dcd_dwc2_link_state_t state) { + if (rhport < DWC2_CONTROLLER_COUNT) { + _dcd_link_state[rhport] = state; + } +} + CFG_TUD_MEM_SECTION static struct { TUD_EPBUF_DEF(setup_packet, 8); } _dcd_usbbuf; @@ -390,6 +398,46 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin } } +//-------------------------------------------------------------------- +// Power / clock management +//-------------------------------------------------------------------- + +// Power / clock gating (PCGCCTL): Synopsys DWC2 programming guide — on suspend stop PHY +// clock then gate HCLK; on resume ungate HCLK first, then restore PHY clock. +// The clock gating sequence is following DWC2 programming guide: +// - 14.2.2.2 Internal Clock Gating when the DWC_otg Core is in Device Mode + +TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_is_clock_gated(dwc2_regs_t* dwc2) { + const uint32_t pcgcctl = dwc2->pcgcctl; + return (pcgcctl & (PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK)) != 0; +} + +// Put controller in internal clock gating (device suspend, PG §14.2.2.2 step 2). +TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_enter_clock_gating(dwc2_regs_t* dwc2) { + dwc2->pcgcctl |= PCGCCTL_STOPPCLK; + dwc2->pcgcctl |= PCGCCTL_GATEHCLK; + + if ((dwc2->pcgcctl & (PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK)) == (PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK)) { + return true; // Successfully gated + } else { + return false; // Failed to gate + } + // TODO for me: Handle return value +} + +// Exit controller from device clock gating. +TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_exit_clock_gating(dwc2_regs_t* dwc2) { + dwc2->pcgcctl &= ~PCGCCTL_GATEHCLK; + dwc2->pcgcctl &= ~PCGCCTL_STOPPCLK; + + if ((dwc2->pcgcctl & (PCGCCTL_GATEHCLK | PCGCCTL_STOPPCLK)) == 0) { + return true; // Successfully ungated + } else { + return false; // Failed to ungate + } + // TODO for me: Handle return value +} + //-------------------------------------------------------------------- // Controller API //-------------------------------------------------------------------- @@ -398,6 +446,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); tu_memclr(&_dcd_data, sizeof(_dcd_data)); + _dcd_link_state[rhport] = DCD_DWC2_LNK_OFF; // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); @@ -438,7 +487,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { #endif // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM | GINTMSK_RSTDEM; // TX FIFO empty level for interrupt is complete empty uint32_t gahbcfg = dwc2->gahbcfg; @@ -471,7 +520,8 @@ void dcd_remote_wakeup(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - // set remote wakeup + dwc2_dcd_exit_clock_gating(dwc2); + dwc2->dctl |= DCTL_RWUSIG; // enable SOF to detect bus resume @@ -488,6 +538,8 @@ void dcd_connect(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_IDLE); + #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 0; @@ -505,6 +557,9 @@ void dcd_disconnect(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_dcd_exit_clock_gating(dwc2); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); + #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 1; @@ -678,6 +733,11 @@ static void handle_bus_reset(uint8_t rhport) { dwc2_regs_t *dwc2 = DWC2_REG(rhport); const uint8_t ep_count = dwc2_ep_count(dwc2); + // Ungate clocks before accessing registers (PG §14.2.3.3 host-initiated reset step 3) + dwc2_dcd_exit_clock_gating(dwc2); + + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_DEFAULT); + tu_memclr(xfer_status, sizeof(xfer_status)); _dcd_data.sof_en = false; @@ -856,6 +916,7 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe if (epin0->diepctl & DIEPCTL_EPENA) { edpt_disable(rhport, 0x80, false); } + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); return; } @@ -941,6 +1002,7 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi } dma_setup_prepare(rhport); dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); return; } @@ -1078,7 +1140,7 @@ static void handle_incomplete_iso_in(uint8_t rhport) { DAINT / \ / \ - GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL + GINTSTS: OEPInt IEPInt | RstDet | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk are combined to generate dedicated interrupt line for each endpoint. @@ -1088,6 +1150,14 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t gintmask = dwc2->gintmsk; const uint32_t gintsts = dwc2->gintsts & gintmask; + if (gintsts & GINTSTS_RSTDET) { + // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST (PG §14.2.3.3). + dwc2->gintsts = GINTSTS_RSTDET; + if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { + dwc2_dcd_exit_clock_gating(dwc2); + } + } + if (gintsts & GINTSTS_USBRST) { // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; @@ -1107,10 +1177,22 @@ void dcd_int_handler(uint8_t rhport) { if (gintsts & GINTSTS_USBSUSP) { dwc2->gintsts = GINTSTS_USBSUSP; dwc2->gintmsk &= ~GINTMSK_USBSUSPM; + // Plug/unplug noise can look like bus idle (suspend). Only gate when link is ACTIVE (SETUP seen) + // and the core reports suspend status (DSTS.SuspSts). + if ((_dcd_link_state[rhport] == DCD_DWC2_LNK_ACTIVE) && + (dwc2->dsts & DSTS_SUSPSTS)) { + dwc2_dcd_enter_clock_gating(dwc2); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_SUSPENDED); + } dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); } if (gintsts & GINTSTS_WKUINT) { + // PG §14.2.2.2: resume detected; core deasserts suspend_n, then app clears GateHclk/StopPclk. + dwc2_dcd_exit_clock_gating(dwc2); + if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); + } dwc2->gintsts = GINTSTS_WKUINT; dwc2->gintmsk |= GINTMSK_USBSUSPM; dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); @@ -1124,6 +1206,8 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t otg_int = dwc2->gotgint; if (otg_int & GOTGINT_SEDET) { + dwc2_dcd_exit_clock_gating(dwc2); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); } @@ -1133,6 +1217,10 @@ void dcd_int_handler(uint8_t rhport) { if(gintsts & GINTSTS_SOF) { dwc2->gintsts = GINTSTS_SOF; dwc2->gintmsk |= GINTMSK_USBSUSPM; + if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { + dwc2_dcd_exit_clock_gating(dwc2); + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); + } const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection @@ -1185,4 +1273,11 @@ void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) } #endif +dcd_dwc2_link_state_t dcd_dwc2_link_state_get(uint8_t rhport) { + if (rhport >= DWC2_CONTROLLER_COUNT) { + return DCD_DWC2_LNK_OFF; + } + return _dcd_link_state[rhport]; +} + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 33219f786c..d33d3f81f6 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -100,4 +100,19 @@ void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, // DMA //--------------------------------------------------------------------+ +//--------------------------------------------------------------------+ +// Device link state (dcd_dwc2) +//--------------------------------------------------------------------+ +#if CFG_TUD_ENABLED +typedef enum { + DCD_DWC2_LNK_OFF = 0, // Soft-disconnected (DCTL_SDIS) or session end (e.g. OTG SEDET) + DCD_DWC2_LNK_IDLE, // Pull-up enabled, waiting for USB bus reset + DCD_DWC2_LNK_DEFAULT, // After USB reset; default device state (address 0) + DCD_DWC2_LNK_ACTIVE, // SETUP received from host; bus suspend can be trusted (not plug noise) + DCD_DWC2_LNK_SUSPENDED, // USB suspend (PHY/HCLK may be clock-gated) +} dcd_dwc2_link_state_t; + +dcd_dwc2_link_state_t dcd_dwc2_link_state_get(uint8_t rhport); +#endif + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 66f99c377e..7622946cfb 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -122,7 +122,7 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_ #define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false) TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { - vTaskDelay(pdMS_TO_TICKS(1)); + vTaskDelay(pdMS_TO_TICKS(10)); } // MCU specific PHY init, called BEFORE core reset From d643044e74da98ed8f497b4e066ec6165f93650c Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Tue, 16 Jun 2026 14:16:55 +0200 Subject: [PATCH 2/6] fix(usb_device): esp32p4 ECO4 fix with resuming WIP --- src/portable/synopsys/dwc2/dcd_dwc2.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index ebc20aa4c2..63c21af0f1 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -414,6 +414,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_is_clock_gated(dwc2_regs_t* dw // Put controller in internal clock gating (device suspend, PG §14.2.2.2 step 2). TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_enter_clock_gating(dwc2_regs_t* dwc2) { + esp_rom_printf("enter\n"); dwc2->pcgcctl |= PCGCCTL_STOPPCLK; dwc2->pcgcctl |= PCGCCTL_GATEHCLK; @@ -422,11 +423,11 @@ TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_enter_clock_gating(dwc2_regs_t } else { return false; // Failed to gate } - // TODO for me: Handle return value } -// Exit controller from device clock gating. +// Exit controller from device clock gating (PG §14.2.2.2: ungate HCLK, then restore PHY clock). TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_exit_clock_gating(dwc2_regs_t* dwc2) { + esp_rom_printf("exit\n"); dwc2->pcgcctl &= ~PCGCCTL_GATEHCLK; dwc2->pcgcctl &= ~PCGCCTL_STOPPCLK; @@ -435,7 +436,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_exit_clock_gating(dwc2_regs_t* } else { return false; // Failed to ungate } - // TODO for me: Handle return value } //-------------------------------------------------------------------- @@ -478,8 +478,9 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; // No overrides - dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); - + //dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + //dwc2->gotgctl |= (GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + dwc2->gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; #if CFG_TUSB_MCU == OPT_MCU_STM32N6 // No hardware detection of Vbus B-session is available on the STM32N6 @@ -487,7 +488,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { #endif // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM | GINTMSK_RSTDEM; + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM | GINTMSK_RSTDEM | GINTSTS_WKUINT; // TX FIFO empty level for interrupt is complete empty uint32_t gahbcfg = dwc2->gahbcfg; @@ -520,6 +521,7 @@ void dcd_remote_wakeup(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); + esp_rom_printf("dcd_remote_wakeup\n"); dwc2_dcd_exit_clock_gating(dwc2); dwc2->dctl |= DCTL_RWUSIG; @@ -541,6 +543,8 @@ void dcd_connect(uint8_t rhport) { dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_IDLE); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) + // USB_WRAP controls the internal FSLS PHY only (P4 rhport 0). HS UTMI (rhport 1) uses UTMI/HP_SYSTEM. +#if !TU_CHECK_MCU(OPT_MCU_ESP32P4) || (rhport == 0) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 0; conf.dp_pullup = 0; @@ -548,6 +552,7 @@ void dcd_connect(uint8_t rhport) { conf.dm_pullup = 0; conf.dm_pulldown = 0; USB_WRAP.otg_conf = conf; +#endif #endif dwc2->dctl &= ~DCTL_SDIS; @@ -557,10 +562,12 @@ void dcd_disconnect(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + esp_rom_printf("dcd_disconnect\n"); dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) +#if !TU_CHECK_MCU(OPT_MCU_ESP32P4) || (rhport == 0) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 1; conf.dp_pullup = 0; @@ -568,6 +575,7 @@ void dcd_disconnect(uint8_t rhport) { conf.dm_pullup = 0; conf.dm_pulldown = 1; USB_WRAP.otg_conf = conf; +#endif #endif dwc2->dctl |= DCTL_SDIS; @@ -734,6 +742,7 @@ static void handle_bus_reset(uint8_t rhport) { const uint8_t ep_count = dwc2_ep_count(dwc2); // Ungate clocks before accessing registers (PG §14.2.3.3 host-initiated reset step 3) + esp_rom_printf("handle_bus_reset\n"); dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_DEFAULT); @@ -1151,14 +1160,17 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t gintsts = dwc2->gintsts & gintmask; if (gintsts & GINTSTS_RSTDET) { + esp_rom_printf("RSTDET trig\n"); // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST (PG §14.2.3.3). dwc2->gintsts = GINTSTS_RSTDET; if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { + esp_rom_printf("RSTDET\n"); dwc2_dcd_exit_clock_gating(dwc2); } } if (gintsts & GINTSTS_USBRST) { + esp_rom_printf("USBRST\n"); // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; @@ -1189,6 +1201,7 @@ void dcd_int_handler(uint8_t rhport) { if (gintsts & GINTSTS_WKUINT) { // PG §14.2.2.2: resume detected; core deasserts suspend_n, then app clears GateHclk/StopPclk. + esp_rom_printf("WKUINT\n"); dwc2_dcd_exit_clock_gating(dwc2); if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); @@ -1206,6 +1219,7 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t otg_int = dwc2->gotgint; if (otg_int & GOTGINT_SEDET) { + esp_rom_printf("OTGINT\n"); dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); @@ -1218,6 +1232,7 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintsts = GINTSTS_SOF; dwc2->gintmsk |= GINTMSK_USBSUSPM; if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { + esp_rom_printf("SOF\n"); dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); } From 83c5be9c9a4417126f50f22508d9ce478fe4602d Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Tue, 16 Jun 2026 17:33:04 +0200 Subject: [PATCH 3/6] fix(usb_device): Fix dconn test for other targets --- src/portable/synopsys/dwc2/dcd_dwc2.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 63c21af0f1..90bdd9cc5b 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -477,10 +477,17 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Force device mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; - // No overrides - //dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); - //dwc2->gotgctl |= (GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); - dwc2->gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + // Default for all targets and for ESP32-P4 ECO5+ + dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + + #if TU_CHECK_MCU(OPT_MCU_ESP32P4) + if (dwc2->gsnpsid <= DWC2_CORE_REV_4_00a) { + // ESP32-P4 ECO4 and earlier: + // override BVAL signal, otherwise the core may not generate + // WKUPINT when DWC2 clocks are gated. + dwc2->gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + } + #endif #if CFG_TUSB_MCU == OPT_MCU_STM32N6 // No hardware detection of Vbus B-session is available on the STM32N6 @@ -488,7 +495,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { #endif // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM | GINTMSK_RSTDEM | GINTSTS_WKUINT; + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_RSTDEM | GINTMSK_WUIM; // TX FIFO empty level for interrupt is complete empty uint32_t gahbcfg = dwc2->gahbcfg; @@ -543,8 +550,6 @@ void dcd_connect(uint8_t rhport) { dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_IDLE); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) - // USB_WRAP controls the internal FSLS PHY only (P4 rhport 0). HS UTMI (rhport 1) uses UTMI/HP_SYSTEM. -#if !TU_CHECK_MCU(OPT_MCU_ESP32P4) || (rhport == 0) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 0; conf.dp_pullup = 0; @@ -552,7 +557,6 @@ void dcd_connect(uint8_t rhport) { conf.dm_pullup = 0; conf.dm_pulldown = 0; USB_WRAP.otg_conf = conf; -#endif #endif dwc2->dctl &= ~DCTL_SDIS; @@ -563,11 +567,11 @@ void dcd_disconnect(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); esp_rom_printf("dcd_disconnect\n"); + // Ungate DWC clock before accessing registers dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) -#if !TU_CHECK_MCU(OPT_MCU_ESP32P4) || (rhport == 0) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 1; conf.dp_pullup = 0; @@ -575,7 +579,6 @@ void dcd_disconnect(uint8_t rhport) { conf.dm_pullup = 0; conf.dm_pulldown = 1; USB_WRAP.otg_conf = conf; -#endif #endif dwc2->dctl |= DCTL_SDIS; @@ -744,7 +747,6 @@ static void handle_bus_reset(uint8_t rhport) { // Ungate clocks before accessing registers (PG §14.2.3.3 host-initiated reset step 3) esp_rom_printf("handle_bus_reset\n"); dwc2_dcd_exit_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_DEFAULT); tu_memclr(xfer_status, sizeof(xfer_status)); From 4235ea1dbfc57ce54cab8e2782988eb7db35d8af Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Wed, 17 Jun 2026 15:22:37 +0200 Subject: [PATCH 4/6] fix(usb_device): WIP --- src/portable/synopsys/dwc2/dcd_dwc2.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 90bdd9cc5b..8f67ce395a 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -1162,17 +1162,14 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t gintsts = dwc2->gintsts & gintmask; if (gintsts & GINTSTS_RSTDET) { - esp_rom_printf("RSTDET trig\n"); - // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST (PG §14.2.3.3). + // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST dwc2->gintsts = GINTSTS_RSTDET; if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { - esp_rom_printf("RSTDET\n"); dwc2_dcd_exit_clock_gating(dwc2); } } if (gintsts & GINTSTS_USBRST) { - esp_rom_printf("USBRST\n"); // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; @@ -1193,8 +1190,7 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintmsk &= ~GINTMSK_USBSUSPM; // Plug/unplug noise can look like bus idle (suspend). Only gate when link is ACTIVE (SETUP seen) // and the core reports suspend status (DSTS.SuspSts). - if ((_dcd_link_state[rhport] == DCD_DWC2_LNK_ACTIVE) && - (dwc2->dsts & DSTS_SUSPSTS)) { + if ((_dcd_link_state[rhport] == DCD_DWC2_LNK_ACTIVE) && (dwc2->dsts & DSTS_SUSPSTS)) { dwc2_dcd_enter_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_SUSPENDED); } @@ -1202,14 +1198,11 @@ void dcd_int_handler(uint8_t rhport) { } if (gintsts & GINTSTS_WKUINT) { - // PG §14.2.2.2: resume detected; core deasserts suspend_n, then app clears GateHclk/StopPclk. - esp_rom_printf("WKUINT\n"); + // Exit clock gating before accessing any registers dwc2_dcd_exit_clock_gating(dwc2); - if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); - } dwc2->gintsts = GINTSTS_WKUINT; dwc2->gintmsk |= GINTMSK_USBSUSPM; + dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); } @@ -1221,7 +1214,6 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t otg_int = dwc2->gotgint; if (otg_int & GOTGINT_SEDET) { - esp_rom_printf("OTGINT\n"); dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); @@ -1231,13 +1223,13 @@ void dcd_int_handler(uint8_t rhport) { } if(gintsts & GINTSTS_SOF) { - dwc2->gintsts = GINTSTS_SOF; - dwc2->gintmsk |= GINTMSK_USBSUSPM; if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { - esp_rom_printf("SOF\n"); + // Exit clock gating before accessing any registers dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); } + dwc2->gintsts = GINTSTS_SOF; + dwc2->gintmsk |= GINTMSK_USBSUSPM; const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection From c0be5b4982e1ec4976539859c00cecbdb95cdc2d Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Wed, 17 Jun 2026 16:30:31 +0200 Subject: [PATCH 5/6] fix(usb_device): WIP WIP --- src/portable/synopsys/dwc2/dcd_dwc2.c | 69 ++++++++++++++---------- src/portable/synopsys/dwc2/dwc2_common.h | 15 ------ 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 8f67ce395a..c60152a743 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -58,6 +58,22 @@ typedef struct { static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; #define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir]) +//--------------------------------------------------------------------+ +// Device link state (dcd_dwc2) +//--------------------------------------------------------------------+ +typedef enum { + DCD_DWC2_LNK_OFF = 0, // Soft-disconnected (DCTL_SDIS) or session end (e.g. OTG SEDET) + DCD_DWC2_LNK_IDLE, // Pull-up enabled, waiting for USB bus reset + DCD_DWC2_LNK_DEFAULT, // After USB reset; default device state (address 0) + DCD_DWC2_LNK_ACTIVE, // SETUP received from host + DCD_DWC2_LNK_SUSPENDED, // Suspended with DWC2 clock gated +} link_state_t; + +typedef struct { + link_state_t current_state; + link_state_t last_state; +} dcd_dwc2_link_t; + typedef struct { // EP0 transfers are limited to 1 packet - larger sizes has to be split uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type @@ -72,12 +88,15 @@ typedef struct { static dcd_data_t _dcd_data; -static volatile dcd_dwc2_link_state_t _dcd_link_state[DWC2_CONTROLLER_COUNT]; +static volatile dcd_dwc2_link_t _dcd_link; -TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set(uint8_t rhport, dcd_dwc2_link_state_t state) { - if (rhport < DWC2_CONTROLLER_COUNT) { - _dcd_link_state[rhport] = state; - } +TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set_state(link_state_t state) { + _dcd_link.last_state = _dcd_link.current_state; + _dcd_link.current_state = state; +} + +TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set_last_state(void) { + _dcd_link.current_state = _dcd_link.last_state; } CFG_TUD_MEM_SECTION static struct { @@ -446,7 +465,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); tu_memclr(&_dcd_data, sizeof(_dcd_data)); - _dcd_link_state[rhport] = DCD_DWC2_LNK_OFF; + _dcd_link.current_state = DCD_DWC2_LNK_OFF; + _dcd_link.last_state = DCD_DWC2_LNK_OFF; // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); @@ -547,7 +567,7 @@ void dcd_connect(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_IDLE); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_IDLE); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; @@ -569,7 +589,7 @@ void dcd_disconnect(uint8_t rhport) { esp_rom_printf("dcd_disconnect\n"); // Ungate DWC clock before accessing registers dwc2_dcd_exit_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_OFF); #if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; @@ -744,11 +764,6 @@ static void handle_bus_reset(uint8_t rhport) { dwc2_regs_t *dwc2 = DWC2_REG(rhport); const uint8_t ep_count = dwc2_ep_count(dwc2); - // Ungate clocks before accessing registers (PG §14.2.3.3 host-initiated reset step 3) - esp_rom_printf("handle_bus_reset\n"); - dwc2_dcd_exit_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_DEFAULT); - tu_memclr(xfer_status, sizeof(xfer_status)); _dcd_data.sof_en = false; @@ -927,7 +942,6 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe if (epin0->diepctl & DIEPCTL_EPENA) { edpt_disable(rhport, 0x80, false); } - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); return; } @@ -1013,7 +1027,6 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi } dma_setup_prepare(rhport); dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); return; } @@ -1164,7 +1177,7 @@ void dcd_int_handler(uint8_t rhport) { if (gintsts & GINTSTS_RSTDET) { // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST dwc2->gintsts = GINTSTS_RSTDET; - if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { + if (_dcd_link.current_state == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { dwc2_dcd_exit_clock_gating(dwc2); } } @@ -1173,9 +1186,11 @@ void dcd_int_handler(uint8_t rhport) { // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; + // DWC2 clock is already ungated by GINTSTS_RSTDET interrupt handler usbd_spin_lock(true); handle_bus_reset(rhport); usbd_spin_unlock(true); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_DEFAULT); } if (gintsts & GINTSTS_ENUMDNE) { @@ -1183,6 +1198,7 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintsts = GINTSTS_ENUMDNE; dwc2->gintmsk |= GINTMSK_USBSUSPM; handle_enum_done(rhport); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_ACTIVE); } if (gintsts & GINTSTS_USBSUSP) { @@ -1190,9 +1206,9 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintmsk &= ~GINTMSK_USBSUSPM; // Plug/unplug noise can look like bus idle (suspend). Only gate when link is ACTIVE (SETUP seen) // and the core reports suspend status (DSTS.SuspSts). - if ((_dcd_link_state[rhport] == DCD_DWC2_LNK_ACTIVE) && (dwc2->dsts & DSTS_SUSPSTS)) { + if ((_dcd_link.current_state == DCD_DWC2_LNK_ACTIVE) && (dwc2->dsts & DSTS_SUSPSTS)) { dwc2_dcd_enter_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_SUSPENDED); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_SUSPENDED); } dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); } @@ -1202,7 +1218,8 @@ void dcd_int_handler(uint8_t rhport) { dwc2_dcd_exit_clock_gating(dwc2); dwc2->gintsts = GINTSTS_WKUINT; dwc2->gintmsk |= GINTMSK_USBSUSPM; - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); + // Set last state, which was current before suspending + dcd_dwc2_link_set_last_state(); dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); } @@ -1215,7 +1232,7 @@ void dcd_int_handler(uint8_t rhport) { if (otg_int & GOTGINT_SEDET) { dwc2_dcd_exit_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_OFF); + dcd_dwc2_link_set_state(DCD_DWC2_LNK_OFF); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); } @@ -1223,10 +1240,11 @@ void dcd_int_handler(uint8_t rhport) { } if(gintsts & GINTSTS_SOF) { - if (_dcd_link_state[rhport] == DCD_DWC2_LNK_SUSPENDED) { + if (_dcd_link.current_state == DCD_DWC2_LNK_SUSPENDED) { // Exit clock gating before accessing any registers dwc2_dcd_exit_clock_gating(dwc2); - dcd_dwc2_link_set(rhport, DCD_DWC2_LNK_ACTIVE); + // Set last state, which was current before suspending + dcd_dwc2_link_set_last_state(); } dwc2->gintsts = GINTSTS_SOF; dwc2->gintmsk |= GINTMSK_USBSUSPM; @@ -1282,11 +1300,4 @@ void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) } #endif -dcd_dwc2_link_state_t dcd_dwc2_link_state_get(uint8_t rhport) { - if (rhport >= DWC2_CONTROLLER_COUNT) { - return DCD_DWC2_LNK_OFF; - } - return _dcd_link_state[rhport]; -} - #endif diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index d33d3f81f6..33219f786c 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -100,19 +100,4 @@ void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, // DMA //--------------------------------------------------------------------+ -//--------------------------------------------------------------------+ -// Device link state (dcd_dwc2) -//--------------------------------------------------------------------+ -#if CFG_TUD_ENABLED -typedef enum { - DCD_DWC2_LNK_OFF = 0, // Soft-disconnected (DCTL_SDIS) or session end (e.g. OTG SEDET) - DCD_DWC2_LNK_IDLE, // Pull-up enabled, waiting for USB bus reset - DCD_DWC2_LNK_DEFAULT, // After USB reset; default device state (address 0) - DCD_DWC2_LNK_ACTIVE, // SETUP received from host; bus suspend can be trusted (not plug noise) - DCD_DWC2_LNK_SUSPENDED, // USB suspend (PHY/HCLK may be clock-gated) -} dcd_dwc2_link_state_t; - -dcd_dwc2_link_state_t dcd_dwc2_link_state_get(uint8_t rhport); -#endif - #endif From 60bab5c3e07ca2db4ad02d7af1090e8597f9f0f1 Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Thu, 18 Jun 2026 17:31:16 +0200 Subject: [PATCH 6/6] feat(usb_device): cleanup WIP --- src/portable/synopsys/dwc2/dcd_dwc2.c | 47 +++++++++++---------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index c60152a743..a882758e02 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -69,11 +69,6 @@ typedef enum { DCD_DWC2_LNK_SUSPENDED, // Suspended with DWC2 clock gated } link_state_t; -typedef struct { - link_state_t current_state; - link_state_t last_state; -} dcd_dwc2_link_t; - typedef struct { // EP0 transfers are limited to 1 packet - larger sizes has to be split uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type @@ -84,19 +79,21 @@ typedef struct { // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by bool sof_en; + + // Device link FSM + link_state_t link_state_current; // Current state + link_state_t link_state_last; // Last state, used to set current state after resuming } dcd_data_t; static dcd_data_t _dcd_data; -static volatile dcd_dwc2_link_t _dcd_link; - TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set_state(link_state_t state) { - _dcd_link.last_state = _dcd_link.current_state; - _dcd_link.current_state = state; + _dcd_data.link_state_last = _dcd_data.link_state_current; + _dcd_data.link_state_current = state; } TU_ATTR_ALWAYS_INLINE static inline void dcd_dwc2_link_set_last_state(void) { - _dcd_link.current_state = _dcd_link.last_state; + _dcd_data.link_state_current = _dcd_data.link_state_last; } CFG_TUD_MEM_SECTION static struct { @@ -433,27 +430,27 @@ TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_is_clock_gated(dwc2_regs_t* dw // Put controller in internal clock gating (device suspend, PG §14.2.2.2 step 2). TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_enter_clock_gating(dwc2_regs_t* dwc2) { - esp_rom_printf("enter\n"); dwc2->pcgcctl |= PCGCCTL_STOPPCLK; dwc2->pcgcctl |= PCGCCTL_GATEHCLK; if ((dwc2->pcgcctl & (PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK)) == (PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK)) { - return true; // Successfully gated + return true; } else { - return false; // Failed to gate + TU_LOG1("Failed to gate DWC2 clock\n"); + return false; } } // Exit controller from device clock gating (PG §14.2.2.2: ungate HCLK, then restore PHY clock). TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcd_exit_clock_gating(dwc2_regs_t* dwc2) { - esp_rom_printf("exit\n"); dwc2->pcgcctl &= ~PCGCCTL_GATEHCLK; dwc2->pcgcctl &= ~PCGCCTL_STOPPCLK; if ((dwc2->pcgcctl & (PCGCCTL_GATEHCLK | PCGCCTL_STOPPCLK)) == 0) { - return true; // Successfully ungated + return true; } else { - return false; // Failed to ungate + TU_LOG1("Failed to un-gate DWC2 clock\n"); + return false; } } @@ -465,8 +462,6 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); tu_memclr(&_dcd_data, sizeof(_dcd_data)); - _dcd_link.current_state = DCD_DWC2_LNK_OFF; - _dcd_link.last_state = DCD_DWC2_LNK_OFF; // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); @@ -497,14 +492,13 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Force device mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; - // Default for all targets and for ESP32-P4 ECO5+ + // Default for all targets except for ESP32-P4 ECO4 dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); #if TU_CHECK_MCU(OPT_MCU_ESP32P4) if (dwc2->gsnpsid <= DWC2_CORE_REV_4_00a) { - // ESP32-P4 ECO4 and earlier: - // override BVAL signal, otherwise the core may not generate - // WKUPINT when DWC2 clocks are gated. + // ESP32-P4 ECO4: + // override BVAL signal, otherwise the core would not generate WKUPINT when DWC2 clocks are gated. dwc2->gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; } #endif @@ -547,8 +541,6 @@ void dcd_remote_wakeup(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - - esp_rom_printf("dcd_remote_wakeup\n"); dwc2_dcd_exit_clock_gating(dwc2); dwc2->dctl |= DCTL_RWUSIG; @@ -586,7 +578,6 @@ void dcd_disconnect(uint8_t rhport) { (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - esp_rom_printf("dcd_disconnect\n"); // Ungate DWC clock before accessing registers dwc2_dcd_exit_clock_gating(dwc2); dcd_dwc2_link_set_state(DCD_DWC2_LNK_OFF); @@ -1177,7 +1168,7 @@ void dcd_int_handler(uint8_t rhport) { if (gintsts & GINTSTS_RSTDET) { // RSTDET reset detected while suspended with clock gated, RSTDET fires before USBRST dwc2->gintsts = GINTSTS_RSTDET; - if (_dcd_link.current_state == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { + if (_dcd_data.link_state_current == DCD_DWC2_LNK_SUSPENDED || dwc2_dcd_is_clock_gated(dwc2)) { dwc2_dcd_exit_clock_gating(dwc2); } } @@ -1206,7 +1197,7 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintmsk &= ~GINTMSK_USBSUSPM; // Plug/unplug noise can look like bus idle (suspend). Only gate when link is ACTIVE (SETUP seen) // and the core reports suspend status (DSTS.SuspSts). - if ((_dcd_link.current_state == DCD_DWC2_LNK_ACTIVE) && (dwc2->dsts & DSTS_SUSPSTS)) { + if ((_dcd_data.link_state_current == DCD_DWC2_LNK_ACTIVE) && (dwc2->dsts & DSTS_SUSPSTS)) { dwc2_dcd_enter_clock_gating(dwc2); dcd_dwc2_link_set_state(DCD_DWC2_LNK_SUSPENDED); } @@ -1240,7 +1231,7 @@ void dcd_int_handler(uint8_t rhport) { } if(gintsts & GINTSTS_SOF) { - if (_dcd_link.current_state == DCD_DWC2_LNK_SUSPENDED) { + if (_dcd_data.link_state_current == DCD_DWC2_LNK_SUSPENDED) { // Exit clock gating before accessing any registers dwc2_dcd_exit_clock_gating(dwc2); // Set last state, which was current before suspending