summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDang Fan <[email protected]>2026-04-14 13:58:02 +0800
committerGitHub <[email protected]>2026-04-14 13:58:02 +0800
commit41462d2b79d5c7f82ce9e8fa8268b342f3f89a67 (patch)
tree23131631cfd865c9486883f6292ba809125cb87d /src
parentce9864a0bcad0b22b1494466b7ef379e1b688319 (diff)
parent3cfe7a0ed1c8c0ca7b1ad747cfce502a5312bf1c (diff)
Merge branch 'hathach:master' into big-endian-for-setup-packets
Diffstat (limited to 'src')
-rw-r--r--src/common/tusb_mcu.h1
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c31
-rw-r--r--src/portable/synopsys/dwc2/dwc2_at32.h26
-rw-r--r--src/portable/synopsys/dwc2/dwc2_bcm.h8
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h16
-rw-r--r--src/portable/synopsys/dwc2/dwc2_efm32.h8
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h14
-rw-r--r--src/portable/synopsys/dwc2/dwc2_gd32.h8
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md2
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_nrf.h95
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h48
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h3
-rw-r--r--src/portable/synopsys/dwc2/dwc2_xmc.h8
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c25
15 files changed, 219 insertions, 80 deletions
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index b12a3177e..77a0bbf1d 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -166,6 +166,7 @@
#define TUP_USBIP_DWC2
#define TUP_USBIP_DWC2_NRF
#define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_RHPORT_HIGHSPEED 1
#define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
//--------------------------------------------------------------------+
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index e32dedd1b..9a9c734a0 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -148,22 +148,23 @@ static void dma_setup_prepare(uint8_t rhport) {
/* Device Data FIFO scheme
+ The controller has a single SPRAM of otg_dfifo_depth 32-bit words shared between all FIFOs and optional DMA metadata.
+ otg_dfifo_depth = ghwcfg3.dfifo_depth + EP_LOC_CNT. It is split up into:
- The FIFO is split up into
- - EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called
- EP_LOC_CNT = ep_fifo_size - ghwcfg3.dfifo_depth. For value less than EP_LOC_CNT, gdfifocfg must be configured before
- gahbcfg.dmaen is set
- - Buffer mode: 1 word per endpoint direction
- - Scatter/Gather DMA: 4 words per endpoint direction
+ - EPInfo: for storing DMA address registers (DxEPDMAn), only required when DMA is used.
+ gdfifocfg.EPINFOBASE and gdfifocfg.GDFIFOCfg must be configured before gahbcfg.dmaen is set.
+ The number of words needed per endpoint direction depends on the DMA mode used at runtime:
+ - Buffer DMA mode: 1 word per endpoint direction
+ - Scatter/Gather DMA mode: 4 words per endpoint direction
- TX FIFO: one fifo for each IN endpoint. Size is dynamic depending on packet size, starting from top with EP0 IN.
- Shared RX FIFO: a shared fifo for all OUT endpoints. Typically, can hold up to 2 packets of the largest EP size.
- We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
+ We allocate TX FIFOs from top to bottom (using a top pointer), this to allow the RX FIFO to grow dynamically, which is
possible since the free space is located between the RX and TX FIFOs.
- ---------------- ep_fifo_size
- | DxEPIDMAn |
- |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ --------------- otg_dfifo_depth
+ | EPInfo | DxEPDMAn (DMA only, sized per runtime DMA mode)
+ |-------------|-- gdfifocfg.EPINFOBASE (start of EPInfo; FIFO space sized by GDFIFOCFG)
| IN FIFO 0 | control EP
|-------------|
| IN FIFO 1 |
@@ -184,9 +185,9 @@ static void dma_setup_prepare(uint8_t rhport) {
- 13 for setup packets + control words (up to 3 setup packets).
- 1 for global NAK (not required/used here).
- Largest-EPsize/4 + 1. (FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
- - 2 for each used OUT endpoint
+ - 2 for each used OUT endpoint.
- Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
+ Therefore, GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
*/
TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
@@ -249,7 +250,7 @@ static void dfifo_device_init(uint8_t rhport) {
// Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
const bool is_dma = dma_device_enabled(dwc2);
- _dcd_data.dfifo_top = dwc2_controller->ep_fifo_size/4;
+ _dcd_data.dfifo_top = dwc2_controller->otg_dfifo_depth;
if (is_dma) {
_dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count;
}
@@ -441,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 fa6d10c12..85fa9b20b 100644
--- a/src/portable/synopsys/dwc2/dwc2_at32.h
+++ b/src/portable/synopsys/dwc2/dwc2_at32.h
@@ -32,38 +32,38 @@
#if CFG_TUSB_MCU == OPT_MCU_AT32F415
#include <at32f415.h>
- #define OTG1_FIFO_SIZE 1280
+ #define OTG1_DFIFO_DEPTH 320
#define OTG1_IRQn OTGFS1_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#elif CFG_TUSB_MCU == OPT_MCU_AT32F435_437
#include <at32f435_437.h>
- #define OTG1_FIFO_SIZE 1280
- #define OTG2_FIFO_SIZE 1280
+ #define OTG1_DFIFO_DEPTH 320
+ #define OTG2_DFIFO_DEPTH 320
#define OTG1_IRQn OTGFS1_IRQn
#define OTG2_IRQn OTGFS2_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#define DWC2_OTG2_REG_BASE 0x40040000UL
#elif CFG_TUSB_MCU == OPT_MCU_AT32F423
#include <at32f423.h>
- #define OTG1_FIFO_SIZE 1280
+ #define OTG1_DFIFO_DEPTH 320
#define OTG1_IRQn OTGFS1_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#elif CFG_TUSB_MCU == OPT_MCU_AT32F402_405
#include <at32f402_405.h>
- #define OTG1_FIFO_SIZE 1280
- #define OTG2_FIFO_SIZE 4096
+ #define OTG1_DFIFO_DEPTH 320
+ #define OTG2_DFIFO_DEPTH 1024
#define OTG1_IRQn OTGFS1_IRQn
#define OTG2_IRQn OTGHS_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#define DWC2_OTG2_REG_BASE 0x40040000UL //OTGHS
#elif CFG_TUSB_MCU == OPT_MCU_AT32F425
#include <at32f425.h>
- #define OTG1_FIFO_SIZE 1280
+ #define OTG1_DFIFO_DEPTH 320
#define OTG1_IRQn OTGFS1_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#elif CFG_TUSB_MCU == OPT_MCU_AT32F45X
#include <at32f45x.h>
- #define OTG1_FIFO_SIZE 1280
+ #define OTG1_DFIFO_DEPTH 320
#define OTG1_IRQn OTGFS1_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
#endif
@@ -73,12 +73,18 @@ extern "C" {
#endif
static const dwc2_controller_t _dwc2_controller[] = {
- {.reg_base = DWC2_OTG1_REG_BASE, .irqnum = OTG1_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = OTG1_FIFO_SIZE},
+ {.reg_base = DWC2_OTG1_REG_BASE, .irqnum = OTG1_IRQn, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = OTG1_DFIFO_DEPTH},
#if defined DWC2_OTG2_REG_BASE
- {.reg_base = DWC2_OTG2_REG_BASE, .irqnum = OTG2_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = OTG2_FIFO_SIZE}
+ {.reg_base = DWC2_OTG2_REG_BASE, .irqnum = OTG2_IRQn, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = OTG2_DFIFO_DEPTH}
#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 00842bba2..91cae821e 100644
--- a/src/portable/synopsys/dwc2/dwc2_bcm.h
+++ b/src/portable/synopsys/dwc2/dwc2_bcm.h
@@ -39,13 +39,19 @@
static const dwc2_controller_t _dwc2_controller[] =
{
- { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 16384 }
+ { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = 4096 }
};
#define dcache_clean(_addr, _size) data_clean(_addr, _size)
#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 e1cb7c769..063360873 100644
--- a/src/portable/synopsys/dwc2/dwc2_efm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_efm32.h
@@ -40,9 +40,15 @@
static const dwc2_controller_t _dwc2_controller[] =
{
- { .reg_base = DWC2_REG_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 }
+ { .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 ff9f216bd..6a10dc7f8 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -44,7 +44,7 @@
#define DWC2_EP_MAX 7
static const dwc2_controller_t _dwc2_controller[] = {
- { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 }
};
#elif TU_CHECK_MCU(OPT_MCU_ESP32H4)
@@ -61,7 +61,7 @@ static const dwc2_controller_t _dwc2_controller[] = {
#define DWC2_EP_MAX 7
static const dwc2_controller_t _dwc2_controller[] = {
- { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 }
};
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
@@ -72,8 +72,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
- { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
- { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 },
+ { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 }
};
#endif
@@ -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 ccbf93a76..28c3fde5e 100644
--- a/src/portable/synopsys/dwc2/dwc2_gd32.h
+++ b/src/portable/synopsys/dwc2/dwc2_gd32.h
@@ -37,7 +37,7 @@
static const dwc2_controller_t _dwc2_controller[] =
{
- { .reg_base = DWC2_REG_BASE, .irqnum = 86, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1280 }
+ { .reg_base = DWC2_REG_BASE, .irqnum = 86, .ep_count = DWC2_EP_MAX, .otg_dfifo_depth = 320 }
};
extern uint32_t SystemCoreClock;
@@ -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_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index 051c3ab1f..205684e4b 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,4 +1,4 @@
-| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | nRF54 | nRF54LM20 | ST F407/429 HS | ST F207/F407/411/429 FS | ST L476 FS | ST F412/76x FS | ST F76x HS | ST H743/H750 | ST F723/L4P5 FS | ST F723 HS | ST H7RS FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | nRF54H20 | nRF54LM20 | ST F407/429 HS | ST F207/F407/411/429 FS | ST L476 FS | ST F412/76x FS | ST F76x HS | ST H743/H750 | ST F723/L4P5 FS | ST F723 HS | ST H7RS FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:-------------|:-------------|:-----------------|:--------------------------|:-------------|:-----------------|:-------------|:---------------|:------------------|:-------------|:-------------|:---------------------|:-------------|:------------|
| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001100 | 0x00001200 | 0x00002000 | 0x00002000 | 0x00002100 | 0x00002300 | 0x00003000 | 0x00003100 | 0x00004000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54430A | 0x4F54500B | 0x4F54281A | 0x4F54281A | 0x4F54310A | 0x4F54320A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54330A | 0x4F54411A | 0x4F54411A | 0x4F54292A | 0x00000000 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index bdf63d590..8ee04e179 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -9,14 +9,14 @@ import pandas as pd
dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4']
dwc2_reg_value = {
'AT32 F405 FS': [0x00002000, 0x4F54400A, 0x00000000, 0x228FDD00, 0x020004E8, 0x1FF0A020],
- 'AT32 F405 HS': [0x00000000, 0x4F54400A, 0x00000000, 0x229FDDD0, 0x03F006E8, 0x1FF0A020],
+ 'AT32 F405 HS': [0, 0x4F54400A, 0x00000000, 0x229FDDD0, 0x03F006E8, 0x1FF0A020],
'AT32 F415': [0x00001000, 0x4F54400A, 0x00000000, 0x228DCD00, 0x020004E8, 0x0F],
'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020],
'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030],
'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030],
'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
- 'nRF54': [0, 0x4F54430A, 0xAA555000, 0x228BFC72, 0x0BEAC0E8, 0x1E10AA60],
- 'nRF54LM20': [0x00000000, 0x4F54500B, 0x00000000, 0x22AFFC52, 0x0BE0C0E8, 0x3E10AA60],
+ 'nRF54H20': [0, 0x4F54430A, 0xAA555000, 0x228BFC72, 0x0BEAC0E8, 0x1E10AA60], # base on Preliminary Datasheet v0.7
+ 'nRF54LM20': [0, 0x4F54500B, 0x00000000, 0x22AFFC52, 0x0BE0C0E8, 0x3E10AA60],
# ST sort by GUID
'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030],
'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
diff --git a/src/portable/synopsys/dwc2/dwc2_nrf.h b/src/portable/synopsys/dwc2/dwc2_nrf.h
index 51f2d684f..a1bf692f8 100644
--- a/src/portable/synopsys/dwc2/dwc2_nrf.h
+++ b/src/portable/synopsys/dwc2/dwc2_nrf.h
@@ -22,22 +22,88 @@
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
+ *
+ * Modification
+ * - Gabriel Koppenstein add nRF54LM20 support
*/
#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)
+#if defined(NRF54LM20A_ENGA_XXAA)
+ #define DWC2_REG_BASE ((uintptr_t)NRF_USBHSCORE)
+#else
+ #define DWC2_REG_BASE ((uintptr_t)NRF_USBHSCORE0)
+#endif
+
static const dwc2_controller_t _dwc2_controller[] = {
- { .reg_base = NRF_USBHSCORE0_NS_BASE, .irqnum = USBHS_IRQn, .ep_count = 16, .ep_fifo_size = 12288 },
+ {.reg_base = DWC2_REG_BASE, .irqnum = USBHS_IRQn, .ep_count = 16, .otg_dfifo_depth = 3072},
};
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+// 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;
- (void) enabled;
+
+ #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;
+ if (enabled) {
+ NVIC_EnableIRQ(USBHS_IRQn);
+ } else {
+ NVIC_DisableIRQ(USBHS_IRQn);
+ }
}
#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
@@ -47,21 +113,38 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
}
// MCU specific PHY init, called BEFORE core reset
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
}
// MCU specific PHY deinit, disable PHY power
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
}
// MCU specific PHY update, it is called AFTER init() and core reset
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
}
+// nRF54 Cortex-M33 has no D-cache, provide no-op stubs for DMA mode
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(const void *addr, uint32_t data_size) {
+ (void)addr;
+ (void)data_size;
+ return true;
+}
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(const void *addr, uint32_t data_size) {
+ (void)addr;
+ (void)data_size;
+ return true;
+}
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(const void *addr, uint32_t data_size) {
+ (void)addr;
+ (void)data_size;
+ return true;
+}
+
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index 259ad21b9..126632fec 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -31,36 +31,36 @@
extern "C" {
#endif
-// EP_MAX : Max number of bi-directional endpoints including EP0
-// EP_FIFO_SIZE : Size of dedicated USB SRAM
+// EP_MAX : Max number of bi-directional endpoints including EP0
+// DFIFO_DEPTH_FS/HS : DFIFO depth in 32-bit words (OTG_DFIFO_DEPTH)
#if CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
#define EP_MAX_FS 4
- #define EP_FIFO_SIZE_FS 1280
+ #define DFIFO_DEPTH_FS 320
#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
#include "stm32f2xx.h"
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+ #define DFIFO_DEPTH_FS 320
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
+ #define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
#include "stm32f4xx.h"
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+ #define DFIFO_DEPTH_FS 320
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
+ #define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
#include "stm32h7xx.h"
#define EP_MAX_FS 9
- #define EP_FIFO_SIZE_FS 4096
+ #define DFIFO_DEPTH_FS 1024
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
// NOTE: H7 with only 1 USB port: H72x / H73x / H7Ax / H7Bx
// USB_OTG_FS_PERIPH_BASE and OTG_FS_IRQn not defined
@@ -72,18 +72,18 @@ extern "C" {
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
#include "stm32h7rsxx.h"
#define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
+ #define DFIFO_DEPTH_FS 320
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
#include "stm32n6xx.h"
#define EP_MAX_FS 9
- #define EP_FIFO_SIZE_FS 4096
+ #define DFIFO_DEPTH_FS 1024
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
#define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_BASE
#define OTG_FS_IRQn USB1_OTG_HS_IRQn
@@ -94,15 +94,15 @@ extern "C" {
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
+ #define DFIFO_DEPTH_FS 320
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
#define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
+ #define DFIFO_DEPTH_FS 320
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
#include "stm32u5xx.h"
@@ -110,11 +110,11 @@ extern "C" {
#ifdef USB_OTG_FS
#define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE
#define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
+ #define DFIFO_DEPTH_FS 320
#else
#define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
#endif
#elif CFG_TUSB_MCU == OPT_MCU_STM32WBA
@@ -131,7 +131,7 @@ extern "C" {
#define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE_NS
#define OTG_HS_IRQn USB_OTG_HS_IRQn
#define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+ #define DFIFO_DEPTH_HS 1024
#else
#error "Unsupported MCUs"
#endif
@@ -147,11 +147,11 @@ extern "C" {
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
#ifdef USB_OTG_FS_PERIPH_BASE
- { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS },
+ { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .otg_dfifo_depth = DFIFO_DEPTH_FS },
#endif
#ifdef USB_OTG_HS_PERIPH_BASE
- { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS },
+ { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .otg_dfifo_depth = DFIFO_DEPTH_HS },
#endif
};
@@ -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_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index 596bd0b34..7c03a4a91 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -47,7 +47,7 @@ typedef struct
uint32_t irqnum;
uint8_t ep_count;
uint8_t ep_in_count;
- uint32_t ep_fifo_size;
+ uint16_t otg_dfifo_depth; // total SPRAM in 32-bit words = ghwcfg3.dfifo_depth + EP_LOC_CNT
}dwc2_controller_t;
// DWC OTG HW Release versions
@@ -63,6 +63,7 @@ typedef struct
#define DWC2_CORE_REV_4_00a 0x4f54400a
#define DWC2_CORE_REV_4_11a 0x4f54411a
#define DWC2_CORE_REV_4_20a 0x4f54420a
+#define DWC2_CORE_REV_5_00b 0x4F54500b
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
#define DWC2_CORE_REV_MASK 0x0000ffff
diff --git a/src/portable/synopsys/dwc2/dwc2_xmc.h b/src/portable/synopsys/dwc2/dwc2_xmc.h
index aca3873df..85b2a2633 100644
--- a/src/portable/synopsys/dwc2/dwc2_xmc.h
+++ b/src/portable/synopsys/dwc2/dwc2_xmc.h
@@ -39,9 +39,15 @@
static const dwc2_controller_t _dwc2_controller[] =
{
// Note: XMC has some custom control registers before DWC registers
- { .reg_base = USB0_BASE, .irqnum = USB0_0_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 }
+ { .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 e12e44a41..6098d6eaa 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -323,9 +323,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t pa
We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
possible since the free space is located between the RX and TX FIFOs.
- ----------------- ep_fifo_size
- | HCDMAn |
- |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ ----------------- otg_dfifo_depth
+ | HCDMAn | (DMA only, sized per runtime DMA mode)
+ |--------------|-- gdfifocfg.EPINFOBASE (= gdfifocfg.GDFIFOCfg)
| Non-Periodic |
| TX FIFO |
|--------------|--- GNPTXFSIZ.addr (fixed size)
@@ -358,15 +358,22 @@ static void dfifo_host_init(uint8_t rhport, bool is_hs_phy) {
// Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel
const bool is_dma = dma_host_enabled(dwc2);
- uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4;
+ uint16_t dfifo_top = dwc2_controller->otg_dfifo_depth;
if (is_dma) {
dfifo_top -= ghwcfg2.num_host_ch;
}
// fixed allocation for now, improve later:
- // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
- uint32_t nptx_largest = is_hs_phy ? TUSB_EPSIZE_BULK_HS / 4 : TUSB_EPSIZE_BULK_FS / 4;
- uint32_t ptx_largest = is_hs_phy ? TUSB_EPSIZE_ISO_HS_MAX / 4 : 256 / 4;
+ // - ptx_largest is limited to 64 words for FS since most FS core only has 256-320 words total
+ uint32_t nptx_largest;
+ uint32_t ptx_largest;
+ if (is_hs_phy) {
+ nptx_largest = TUSB_EPSIZE_BULK_HS / 4;
+ ptx_largest = TUSB_EPSIZE_ISO_HS_MAX / 4;
+ } else {
+ nptx_largest = TUSB_EPSIZE_BULK_FS / 4;
+ ptx_largest = 256 / 4;
+ }
uint16_t nptxfsiz = 2 * nptx_largest;
uint16_t rxfsiz = 2 * (ptx_largest + 2) + ghwcfg2.num_host_ch;
@@ -400,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));