diff options
| author | Dang Fan <[email protected]> | 2026-04-14 13:58:02 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-14 13:58:02 +0800 |
| commit | 41462d2b79d5c7f82ce9e8fa8268b342f3f89a67 (patch) | |
| tree | 23131631cfd865c9486883f6292ba809125cb87d | |
| parent | ce9864a0bcad0b22b1494466b7ef379e1b688319 (diff) | |
| parent | 3cfe7a0ed1c8c0ca7b1ad747cfce502a5312bf1c (diff) | |
Merge branch 'hathach:master' into big-endian-for-setup-packets
27 files changed, 454 insertions, 115 deletions
diff --git a/README.rst b/README.rst index fe260f72f..6be9d4873 100644 --- a/README.rst +++ b/README.rst @@ -182,7 +182,9 @@ Supported CPUs +--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+ | MindMotion | mm32 | ✔ | | ✖ | mm32f327x_otg | ci_fs variant | +--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+ -| NordicSemi | nRF 52833, 52840, 5340 | ✔ | ✖ | ✖ | nrf5x | only ep8 is ISO | +| NordicSemi | nRF52, nRF53 | ✔ | ✖ | ✖ | nrf5x | only ep8 is ISO | +| +-----------------------------+--------+------+-----------+------------------------+--------------------+ +| | nRF54 | ✔ | ✖ | ✔ | dwc2 | | +--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+ | Nuvoton | NUC120 | ✔ | ✖ | ✖ | nuc120 | | | +-----------------------------+--------+------+-----------+------------------------+--------------------+ diff --git a/docs/reference/boards.rst b/docs/reference/boards.rst index ec91b343e..dbb5942ca 100644 --- a/docs/reference/boards.rst +++ b/docs/reference/boards.rst @@ -221,6 +221,7 @@ nrf52840dk Nordic nRF52840DK nrf ht nrf52840dongle Nordic nRF52840 Dongle nrf https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle nrf5340dk Nordic nRF5340 DK nrf https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK nrf54h20dk Nordic nRF54H20 DK nrf https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK +nrf54lm20dk Nordic nRF54LM20 DK nrf https://www.nordicsemi.com/Products/Development-hardware/nRF54LM20-DK =========================== ===================================== ======== ============================================================================== ====== Raspberry Pi diff --git a/hw/bsp/BoardPresets.json b/hw/bsp/BoardPresets.json index 5c3aaf2b4..86609d075 100644 --- a/hw/bsp/BoardPresets.json +++ b/hw/bsp/BoardPresets.json @@ -471,6 +471,10 @@ "inherits": "default" }, { + "name": "nrf54lm20dk", + "inherits": "default" + }, + { "name": "nutiny_nuc126v", "inherits": "default" }, @@ -1564,6 +1568,11 @@ "configurePreset": "nrf54h20dk" }, { + "name": "nrf54lm20dk", + "description": "Build preset for the nrf54lm20dk board", + "configurePreset": "nrf54lm20dk" + }, + { "name": "nutiny_nuc126v", "description": "Build preset for the nutiny_nuc126v board", "configurePreset": "nutiny_nuc126v" @@ -3712,6 +3721,19 @@ ] }, { + "name": "nrf54lm20dk", + "steps": [ + { + "type": "configure", + "name": "nrf54lm20dk" + }, + { + "type": "build", + "name": "nrf54lm20dk" + } + ] + }, + { "name": "nutiny_nuc126v", "steps": [ { diff --git a/hw/bsp/nrf/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/nrf/FreeRTOSConfig/FreeRTOSConfig.h index 0ddd536fb..b6395306f 100644 --- a/hw/bsp/nrf/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/nrf/FreeRTOSConfig/FreeRTOSConfig.h @@ -53,6 +53,12 @@ #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE (1024) +/* nRF54 runs entirely in Secure mode (SAU present but no SPE bootloader). + * Tell FreeRTOS CM33 NTZ port to use Secure-compatible EXC_RETURN values. */ +#if defined(NRF54H20_XXAA) || defined(NRF54LM20A_ENGA_XXAA) +#define configRUN_FREERTOS_SECURE_ONLY 1 +#endif + #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configCPU_CLOCK_HZ SystemCoreClock diff --git a/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake b/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake new file mode 100644 index 000000000..8c2b83346 --- /dev/null +++ b/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake @@ -0,0 +1,8 @@ +set(MCU_VARIANT nrf54lm20a_enga) +set(JLINK_DEVICE NRF54LM20A_M33) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + CFG_EXAMPLE_VIDEO_READONLY + ) +endfunction() diff --git a/hw/bsp/nrf/boards/nrf54lm20dk/board.h b/hw/bsp/nrf/boards/nrf54lm20dk/board.h new file mode 100644 index 000000000..6bf5da891 --- /dev/null +++ b/hw/bsp/nrf/boards/nrf54lm20dk/board.h @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, Ha Thach (tinyusb.org) + * Copyright (c) 2026, Gabriel Koppenstein + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/* metadata: + name: Nordic nRF54LM20 DK + url: https://www.nordicsemi.com/Products/Development-hardware/nRF54LM20-DK +*/ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +// LED0 active high (MOSFET-driven) +#define LED_PIN _PINNUM(1, 22) +#define LED_STATE_ON 1 + +// Button0 active low +#define BUTTON_PIN _PINNUM(1, 26) +#define BUTTON_STATE_ACTIVE 0 + +// UART20 (VCOM via debugger) +#define UART_TX_PIN _PINNUM(1, 16) +#define UART_RX_PIN _PINNUM(1, 17) + +#ifdef __cplusplus + } +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/nrf/boards/nrf54lm20dk/board.mk b/hw/bsp/nrf/boards/nrf54lm20dk/board.mk new file mode 100644 index 000000000..ad0177b40 --- /dev/null +++ b/hw/bsp/nrf/boards/nrf54lm20dk/board.mk @@ -0,0 +1,6 @@ +MCU_VARIANT = nrf54lm20a_enga +CFLAGS += -DNRF54LM20A_ENGA_XXAA + +# flash using jlink +JLINK_DEVICE = NRF54LM20A_M33 +flash: flash-jlink diff --git a/hw/bsp/nrf/family.c b/hw/bsp/nrf/family.c index f1e8b829c..31a6bac9e 100644 --- a/hw/bsp/nrf/family.c +++ b/hw/bsp/nrf/family.c @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2026, Gabriel Koppenstein * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,7 +46,7 @@ #include "nrfx.h" #include "hal/nrf_gpio.h" #include "nrfx_gpiote.h" -#if !defined(NRF54H20_XXAA) +#if !defined(NRF54H20_XXAA) && !defined(NRF54LM20A_ENGA_XXAA) #include "nrfx_power.h" #endif #include "nrfx_uarte.h" @@ -79,42 +80,74 @@ enum { }; // Forward USB interrupt events to TinyUSB IRQ Handler -#if defined(NRF54H20_XXAA) -#define USBD_IRQn USBHS_IRQn +#if defined(NRF54H20_XXAA) || defined(NRF54LM20A_ENGA_XXAA) + #define USBD_IRQn USBHS_IRQn void USBHS_IRQHandler(void) { tusb_int_handler(0, true); } + #if defined(NRF54LM20A_ENGA_XXAA) +static nrfx_uarte_t _uart_id = NRFX_UARTE_INSTANCE(20); + #else static nrfx_uarte_t _uart_id = NRFX_UARTE_INSTANCE(120); + #endif #else + #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. extern void tusb_hal_nrf_power_event(uint32_t event); -#if !defined(NRF54H20_XXAA) +#if !defined(NRF54H20_XXAA) && !defined(NRF54LM20A_ENGA_XXAA) // nrf power callback, could be unused if SD is enabled or usb is disabled (board_test example) TU_ATTR_UNUSED static void power_event_handler(nrfx_power_usb_evt_t event) { tusb_hal_nrf_power_event((uint32_t) event); @@ -131,9 +164,8 @@ static nrfx_gpiote_t _gpiote = NRFX_GPIOTE_INSTANCE(0); //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ - void board_init(void) { -#if !defined(NRF54H20_XXAA) +#if !defined(NRF54H20_XXAA) && !defined(NRF54LM20A_ENGA_XXAA) // stop LF clock just in case we jump from application without reset NRF_CLOCK->TASKS_LFCLKSTOP = 1UL; @@ -167,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 = { @@ -181,16 +215,26 @@ 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) + // 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 + // Priorities 0, 1, 4 (nRF52) are reserved for SoftDevice // 2 is highest for application NVIC_SetPriority(USBD_IRQn, 2); -#if !defined(NRF54H20_XXAA) +#if !defined(NRF54H20_XXAA) && !defined(NRF54LM20A_ENGA_XXAA) // USB power may already be ready at this time -> no event generated // We need to invoke the handler based on the status initially uint32_t usb_reg; @@ -258,7 +302,7 @@ size_t board_get_unique_id(uint8_t id[], size_t max_len) { #if defined(NRF54H20_XXAA) uintptr_t did_addr = (uintptr_t) NRF_FICR->BLE.ADDR; -#elif defined(NRF5340_XXAA) +#elif defined(NRF54LM20A_ENGA_XXAA) || defined(NRF5340_XXAA) uintptr_t did_addr = (uintptr_t) NRF_FICR->INFO.DEVICEID; #else uintptr_t did_addr = (uintptr_t) NRF_FICR->DEVICEID; @@ -272,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) { @@ -284,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/hw/bsp/nrf/family.cmake b/hw/bsp/nrf/family.cmake index 3a6e7cc8b..fba5e6e69 100644 --- a/hw/bsp/nrf/family.cmake +++ b/hw/bsp/nrf/family.cmake @@ -10,15 +10,19 @@ if (NOT board_cmake_included) endif () # toolchain set up -if (MCU_VARIANT STREQUAL nrf5340 OR MCU_VARIANT STREQUAL nrf54h20) +if (MCU_VARIANT STREQUAL nrf5340 OR MCU_VARIANT STREQUAL nrf54h20 OR MCU_VARIANT STREQUAL nrf54lm20a_enga) set(CMAKE_SYSTEM_CPU cortex-m33 CACHE INTERNAL "System Processor") - set(JLINK_DEVICE ${MCU_VARIANT}_xxaa_app) + if (NOT DEFINED JLINK_DEVICE) + set(JLINK_DEVICE ${MCU_VARIANT}_xxaa_app) + endif () else () set(CMAKE_SYSTEM_CPU cortex-m4 CACHE INTERNAL "System Processor") - set(JLINK_DEVICE ${MCU_VARIANT}_xxaa) + if (NOT DEFINED JLINK_DEVICE) + set(JLINK_DEVICE ${MCU_VARIANT}_xxaa) + endif () endif () -if (MCU_VARIANT STREQUAL "nrf54h20") +if (MCU_VARIANT STREQUAL "nrf54h20" OR MCU_VARIANT STREQUAL "nrf54lm20a_enga") set(FAMILY_MCUS NRF54 CACHE INTERNAL "") else () set(FAMILY_MCUS NRF5X CACHE INTERNAL "") @@ -29,7 +33,10 @@ set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOL #------------------------------------ # Startup & Linker script #------------------------------------ -if (MCU_VARIANT STREQUAL nrf54h20) +if (MCU_VARIANT STREQUAL nrf54lm20a_enga) + set(LD_FILE_GNU_DEFAULT ${CMAKE_CURRENT_LIST_DIR}/linker/${MCU_VARIANT}_xxaa_application.ld) + set(STARTUP_FILE_GNU ${NRFX_PATH}/mdk/gcc_startup_${MCU_VARIANT}_application.S) +elseif (MCU_VARIANT STREQUAL nrf54h20) set(LD_FILE_GNU_DEFAULT ${CMAKE_CURRENT_LIST_DIR}/linker/${MCU_VARIANT}_xxaa_application.ld) set(STARTUP_FILE_GNU ${NRFX_PATH}/mdk/gcc_startup_${MCU_VARIANT}_application.S) elseif (MCU_VARIANT STREQUAL nrf5340) @@ -52,13 +59,20 @@ function(family_add_board BOARD_TARGET) add_library(${BOARD_TARGET} STATIC ${NRFX_PATH}/helpers/nrfx_flag32_allocator.c ${NRFX_PATH}/drivers/src/nrfx_gpiote.c - ${NRFX_PATH}/drivers/src/nrfx_power.c ${NRFX_PATH}/drivers/src/nrfx_spim.c ${NRFX_PATH}/drivers/src/nrfx_uarte.c ${NRFX_PATH}/soc/nrfx_atomic.c ) - if (MCU_VARIANT STREQUAL nrf54h20) + if (NOT FAMILY_MCUS STREQUAL "NRF54") + target_sources(${BOARD_TARGET} PRIVATE ${NRFX_PATH}/drivers/src/nrfx_power.c) + endif () + + if (MCU_VARIANT STREQUAL nrf54lm20a_enga) + target_sources(${BOARD_TARGET} PRIVATE + ${NRFX_PATH}/mdk/system_nrf54l.c + ) + elseif (MCU_VARIANT STREQUAL nrf54h20) target_sources(${BOARD_TARGET} PRIVATE ${NRFX_PATH}/mdk/system_nrf54h.c ) diff --git a/hw/bsp/nrf/family.mk b/hw/bsp/nrf/family.mk index e7e9e7d0a..0cc3c81ef 100644 --- a/hw/bsp/nrf/family.mk +++ b/hw/bsp/nrf/family.mk @@ -4,6 +4,15 @@ NRFX_PATH = hw/mcu/nordic/nrfx include $(TOP)/$(BOARD_PATH)/board.mk +ifeq (${MCU_VARIANT},nrf54lm20a_enga) + CPU_CORE = cortex-m33 + CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_NRF54 + LD_FILE_DEFAULT = ${FAMILY_PATH}/linker/${MCU_VARIANT}_xxaa_application.ld + SRC_C += ${NRFX_PATH}/mdk/system_nrf54l.c + SRC_S += ${NRFX_PATH}/mdk/gcc_startup_$(MCU_VARIANT)_application.S + JLINK_DEVICE ?= $(MCU_VARIANT)_xxaa_app + +else ifeq (${MCU_VARIANT},nrf54h20) CPU_CORE = cortex-m33 CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_NRF54 @@ -32,6 +41,7 @@ else JLINK_DEVICE ?= $(MCU_VARIANT)_xxaa endif endif +endif CFLAGS += \ -DNRF_APPLICATION \ @@ -70,11 +80,14 @@ SRC_C += \ src/portable/synopsys/dwc2/hcd_dwc2.c \ ${NRFX_PATH}/helpers/nrfx_flag32_allocator.c \ ${NRFX_PATH}/drivers/src/nrfx_gpiote.c \ - ${NRFX_PATH}/drivers/src/nrfx_power.c \ ${NRFX_PATH}/drivers/src/nrfx_spim.c \ ${NRFX_PATH}/drivers/src/nrfx_uarte.c \ ${NRFX_PATH}/soc/nrfx_atomic.c +ifeq (,$(findstring OPT_MCU_NRF54,$(CFLAGS))) +SRC_C += ${NRFX_PATH}/drivers/src/nrfx_power.c +endif + INC += \ $(TOP)/$(BOARD_PATH) \ $(TOP)/$(FAMILY_PATH)/nrfx_config \ diff --git a/hw/bsp/nrf/linker/nrf54lm20a_enga_xxaa_application.ld b/hw/bsp/nrf/linker/nrf54lm20a_enga_xxaa_application.ld new file mode 100644 index 000000000..367cf6a89 --- /dev/null +++ b/hw/bsp/nrf/linker/nrf54lm20a_enga_xxaa_application.ld @@ -0,0 +1,13 @@ +/* Linker script to configure memory regions. */ + +SEARCH_DIR(.) +/*GROUP(-lgcc -lc) not compatible with clang*/ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x1FD000 /* Inside global RRAM0 */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000 + RAM1 (rwx) : ORIGIN = 0x20040000, LENGTH = 0x40000 +} + +INCLUDE "nrf_common.ld" diff --git a/hw/bsp/nrf/nrfx_config/nrfx_config_common.h b/hw/bsp/nrf/nrfx_config/nrfx_config_common.h index a5a29bb8e..02b7390e2 100644 --- a/hw/bsp/nrf/nrfx_config/nrfx_config_common.h +++ b/hw/bsp/nrf/nrfx_config/nrfx_config_common.h @@ -62,6 +62,9 @@ #if defined(NRF54H20_XXAA) #define NRFX_UARTE120_ENABLED 1 +#elif defined(NRF54LM20A_ENGA_XXAA) +#define NRFX_UARTE20_ENABLED 1 + #else #define NRFX_POWER_ENABLED 1 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)); |
