From 1eff1e942ac28417942314498c58cad5bf502052 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 22 Jun 2026 09:52:41 -0400 Subject: [PATCH 1/4] Add SAME5x TinyUSB support --- src/arduino/Adafruit_USBH_Host.cpp | 13 ++- .../ports/samd/Adafruit_TinyUSB_samd.cpp | 39 ++++++++- src/arduino/ports/samd/tusb_config_samd.h | 4 +- src/portable/microchip/samd/dcd_samd.c | 20 ++++- src/portable/microchip/samd/hcd_samd.c | 25 +++++- .../microchip/samd/same5x_usb_compat.h | 85 +++++++++++++++++++ 6 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 src/portable/microchip/samd/same5x_usb_compat.h diff --git a/src/arduino/Adafruit_USBH_Host.cpp b/src/arduino/Adafruit_USBH_Host.cpp index cdba7732..a3a7da7c 100644 --- a/src/arduino/Adafruit_USBH_Host.cpp +++ b/src/arduino/Adafruit_USBH_Host.cpp @@ -273,11 +273,20 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled) { (void)host; #ifdef ARDUINO_ARCH_SAMD - //--- SAMD51 ---// -#ifdef __SAMD51__ +//--- SAMD51/SAME51 ---// +#if TU_CHECK_MCU(OPT_MCU_SAMD51) const IRQn_Type irq = (IRQn_Type)(EIC_0_IRQn + g_APinDescription[host->_intr].ulExtInt); + if (enabled) { + NVIC_EnableIRQ(irq); + } else { + NVIC_DisableIRQ(irq); + } +#elif TU_CHECK_MCU(OPT_MCU_SAME5X) + const IRQn_Type irq = + (IRQn_Type)(EIC_EXTINT_0_IRQn + g_APinDescription[host->_intr].ulExtInt); + if (enabled) { NVIC_EnableIRQ(irq); } else { diff --git a/src/arduino/ports/samd/Adafruit_TinyUSB_samd.cpp b/src/arduino/ports/samd/Adafruit_TinyUSB_samd.cpp index f6fe45e0..214d3fe6 100644 --- a/src/arduino/ports/samd/Adafruit_TinyUSB_samd.cpp +++ b/src/arduino/ports/samd/Adafruit_TinyUSB_samd.cpp @@ -37,7 +37,7 @@ //--------------------------------------------------------------------+ extern "C" { -#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X +#if TU_CHECK_MCU(OPT_MCU_SAMD51) // SAMD51 void USB_0_Handler(void) { tud_int_handler(0); } @@ -45,7 +45,15 @@ void USB_1_Handler(void) { tud_int_handler(0); } void USB_2_Handler(void) { tud_int_handler(0); } void USB_3_Handler(void) { tud_int_handler(0); } -#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 +#elif TU_CHECK_MCU(OPT_MCU_SAME5X) + +// SAME53/54 +void USB_OTHER_Handler(void) { tud_int_handler(0); } +void USB_SOF_HSOF_Handler(void) { tud_int_handler(0); } +void USB_TRCPT0_Handler(void) { tud_int_handler(0); } +void USB_TRCPT1_Handler(void) { tud_int_handler(0); } + +#elif TU_CHECK_MCU(OPT_MCU_SAMD21) // SAMD21 void USB_Handler(void) { tud_int_handler(0); } @@ -61,7 +69,7 @@ void TinyUSB_Port_InitDevice(uint8_t rhport) { (void)rhport; /* Enable USB clock */ -#if defined(__SAMD51__) +#if TU_CHECK_MCU(OPT_MCU_SAMD51) MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; @@ -84,6 +92,29 @@ void TinyUSB_Port_InitDevice(uint8_t rhport) { NVIC_SetPriority(USB_1_IRQn, 0UL); NVIC_SetPriority(USB_2_IRQn, 0UL); NVIC_SetPriority(USB_3_IRQn, 0UL); +#elif TU_CHECK_MCU(OPT_MCU_SAME5X) + MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; + MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; + + // Set up the USB DP/DN pins + PORT->Group[0].PINCFG[PIN_PA24H_USB_DM].bit.PMUXEN = 1; + PORT->Group[0].PMUX[PIN_PA24H_USB_DM / 2].reg &= + ~(0xF << (4 * (PIN_PA24H_USB_DM & 0x01u))); + PORT->Group[0].PMUX[PIN_PA24H_USB_DM / 2].reg |= + MUX_PA24H_USB_DM << (4 * (PIN_PA24H_USB_DM & 0x01u)); + PORT->Group[0].PINCFG[PIN_PA25H_USB_DP].bit.PMUXEN = 1; + PORT->Group[0].PMUX[PIN_PA25H_USB_DP / 2].reg &= + ~(0xF << (4 * (PIN_PA25H_USB_DP & 0x01u))); + PORT->Group[0].PMUX[PIN_PA25H_USB_DP / 2].reg |= + MUX_PA25H_USB_DP << (4 * (PIN_PA25H_USB_DP & 0x01u)); + + GCLK->PCHCTRL[USB_GCLK_ID].reg = + GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); + + NVIC_SetPriority(USB_OTHER_IRQn, 0UL); + NVIC_SetPriority(USB_SOF_HSOF_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT0_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT1_IRQn, 0UL); #else PM->APBBMASK.reg |= PM_APBBMASK_USB; @@ -126,7 +157,7 @@ void TinyUSB_Port_EnterDFU(void) { } uint8_t TinyUSB_Port_GetSerialNumber(uint8_t serial_id[16]) { -#ifdef __SAMD51__ +#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) uint32_t *id_addresses[4] = {(uint32_t *)0x008061FC, (uint32_t *)0x00806010, (uint32_t *)0x00806014, (uint32_t *)0x00806018}; #else // samd21 diff --git a/src/arduino/ports/samd/tusb_config_samd.h b/src/arduino/ports/samd/tusb_config_samd.h index d2276d12..f4875022 100644 --- a/src/arduino/ports/samd/tusb_config_samd.h +++ b/src/arduino/ports/samd/tusb_config_samd.h @@ -32,8 +32,10 @@ extern "C" { //-------------------------------------------------------------------- // COMMON CONFIGURATION //-------------------------------------------------------------------- -#ifdef __SAMD51__ +#if defined(__SAMD51__) || defined(__SAME51__) #define CFG_TUSB_MCU OPT_MCU_SAMD51 +#elif defined(__SAME53__) || defined(__SAME54__) +#define CFG_TUSB_MCU OPT_MCU_SAME5X #else #define CFG_TUSB_MCU OPT_MCU_SAMD21 #endif diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index cdfb00e3..c2004ae1 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -29,6 +29,7 @@ #if CFG_TUD_ENABLED && TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X, OPT_MCU_SAMD51, OPT_MCU_SAME5X) #include "sam.h" +#include "same5x_usb_compat.h" #include "device/dcd.h" /*------------------------------------------------------------------*/ @@ -103,7 +104,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; } -#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) +#if TU_CHECK_MCU(OPT_MCU_SAMD51) void dcd_int_enable(uint8_t rhport) { (void) rhport; NVIC_EnableIRQ(USB_0_IRQn); @@ -120,6 +121,23 @@ void dcd_int_disable(uint8_t rhport) { NVIC_DisableIRQ(USB_0_IRQn); } +#elif TU_CHECK_MCU(OPT_MCU_SAME5X) +void dcd_int_enable(uint8_t rhport) { + (void) rhport; + NVIC_EnableIRQ(USB_OTHER_IRQn); + NVIC_EnableIRQ(USB_SOF_HSOF_IRQn); + NVIC_EnableIRQ(USB_TRCPT0_IRQn); + NVIC_EnableIRQ(USB_TRCPT1_IRQn); +} + +void dcd_int_disable(uint8_t rhport) { + (void) rhport; + NVIC_DisableIRQ(USB_TRCPT1_IRQn); + NVIC_DisableIRQ(USB_TRCPT0_IRQn); + NVIC_DisableIRQ(USB_SOF_HSOF_IRQn); + NVIC_DisableIRQ(USB_OTHER_IRQn); +} + #elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X) void dcd_int_enable(uint8_t rhport) { (void) rhport; diff --git a/src/portable/microchip/samd/hcd_samd.c b/src/portable/microchip/samd/hcd_samd.c index 0de7ddeb..018b5f5a 100644 --- a/src/portable/microchip/samd/hcd_samd.c +++ b/src/portable/microchip/samd/hcd_samd.c @@ -31,6 +31,7 @@ #include "host/hcd.h" #include "sam.h" +#include "same5x_usb_compat.h" /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM @@ -425,7 +426,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; } -#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) +#if TU_CHECK_MCU(OPT_MCU_SAMD51) // Enable USB interrupt void hcd_int_enable(uint8_t rhport) @@ -447,6 +448,28 @@ void hcd_int_disable(uint8_t rhport) NVIC_DisableIRQ(USB_0_IRQn); } +#elif TU_CHECK_MCU(OPT_MCU_SAME5X) + +// Enable USB interrupt +void hcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USB_OTHER_IRQn); + NVIC_EnableIRQ(USB_SOF_HSOF_IRQn); + NVIC_EnableIRQ(USB_TRCPT0_IRQn); + NVIC_EnableIRQ(USB_TRCPT1_IRQn); +} + +// Disable USB interrupt +void hcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USB_TRCPT1_IRQn); + NVIC_DisableIRQ(USB_TRCPT0_IRQn); + NVIC_DisableIRQ(USB_SOF_HSOF_IRQn); + NVIC_DisableIRQ(USB_OTHER_IRQn); +} + #elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X) // Enable USB interrupt diff --git a/src/portable/microchip/samd/same5x_usb_compat.h b/src/portable/microchip/samd/same5x_usb_compat.h new file mode 100644 index 00000000..38df5c90 --- /dev/null +++ b/src/portable/microchip/samd/same5x_usb_compat.h @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Chris Rabel + * + * 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. + */ + +#ifndef _SAME5X_USB_COMPAT_H_ +#define _SAME5X_USB_COMPAT_H_ + +#if defined(__GNUC__) +#pragma GCC system_header +#endif + +#include "tusb_option.h" + +#if TU_CHECK_MCU(OPT_MCU_SAME5X) + +#ifndef _Ul +#define _Ul(x) x##U +#endif + +#ifndef TUSB_SAME5X_COMPAT_ROREG8 +#define TUSB_SAME5X_COMPAT_ROREG8 1 +typedef volatile const uint8_t RoReg8; +#endif + +#if defined(__has_include) +#if __has_include("samd51/include/component/usb.h") +#include "samd51/include/component/usb.h" +#else +#error "SAME5x USB support requires SAMD51 USB component headers on the include path." +#endif +#else +#include "samd51/include/component/usb.h" +#endif + +#if !defined(NVMCTRL_SW0) && defined(SW0_ADDR) +#define NVMCTRL_SW0 SW0_ADDR +#elif !defined(NVMCTRL_SW0) && defined(SW0_FUSES_BASE_ADDRESS) +#define NVMCTRL_SW0 SW0_FUSES_BASE_ADDRESS +#endif + +#ifndef USB_FUSES_TRANSN_ADDR +#define USB_FUSES_TRANSN_ADDR (NVMCTRL_SW0 + 4) +#define USB_FUSES_TRANSN_Pos 0 +#define USB_FUSES_TRANSN_Msk (_Ul(0x1F) << USB_FUSES_TRANSN_Pos) +#endif + +#ifndef USB_FUSES_TRANSP_ADDR +#define USB_FUSES_TRANSP_ADDR (NVMCTRL_SW0 + 4) +#define USB_FUSES_TRANSP_Pos 5 +#define USB_FUSES_TRANSP_Msk (_Ul(0x1F) << USB_FUSES_TRANSP_Pos) +#endif + +#ifndef USB_FUSES_TRIM_ADDR +#define USB_FUSES_TRIM_ADDR (NVMCTRL_SW0 + 4) +#define USB_FUSES_TRIM_Pos 10 +#define USB_FUSES_TRIM_Msk (_Ul(0x7) << USB_FUSES_TRIM_Pos) +#endif + +#if !defined(USB) && defined(USB_REGS) +#define USB ((Usb *)USB_REGS) +#endif + +#endif + +#endif From 6aed4349a5606f719fd5ec3a748790424920033b Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 22 Jun 2026 11:44:00 -0400 Subject: [PATCH 2/4] Tune SAMD MAX3421 SPI clock limits --- src/arduino/Adafruit_USBH_Host.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/arduino/Adafruit_USBH_Host.cpp b/src/arduino/Adafruit_USBH_Host.cpp index a3a7da7c..c7254609 100644 --- a/src/arduino/Adafruit_USBH_Host.cpp +++ b/src/arduino/Adafruit_USBH_Host.cpp @@ -211,11 +211,14 @@ void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) { SPIClass *spi = host->_spi; if (active) { - // MAX3421e max clock is 26MHz - // Depending on mcu ports, it may need to be clipped down + // MAX3421e max clock is 26MHz. SAMD SERCOM clocks are quantized by the + // baud generator, so request values that stay within each port's limit. #ifdef ARDUINO_ARCH_SAMD - // SAMD 21/51 can only work reliably at 12MHz +#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) + uint32_t const max_clock = 25000000ul; +#else uint32_t const max_clock = 12000000ul; +#endif #else uint32_t const max_clock = 26000000ul; #endif From 40213b77171f7296085c1a38fb937b34ab5b9863 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 22 Jun 2026 11:50:24 -0400 Subject: [PATCH 3/4] Rename SAME5x USB support header --- src/portable/microchip/samd/dcd_samd.c | 2 +- src/portable/microchip/samd/hcd_samd.c | 2 +- .../microchip/samd/{same5x_usb_compat.h => same5x_usb.h} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/portable/microchip/samd/{same5x_usb_compat.h => same5x_usb.h} (97%) diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index c2004ae1..c21e94ea 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -29,7 +29,7 @@ #if CFG_TUD_ENABLED && TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X, OPT_MCU_SAMD51, OPT_MCU_SAME5X) #include "sam.h" -#include "same5x_usb_compat.h" +#include "same5x_usb.h" #include "device/dcd.h" /*------------------------------------------------------------------*/ diff --git a/src/portable/microchip/samd/hcd_samd.c b/src/portable/microchip/samd/hcd_samd.c index 018b5f5a..110190af 100644 --- a/src/portable/microchip/samd/hcd_samd.c +++ b/src/portable/microchip/samd/hcd_samd.c @@ -31,7 +31,7 @@ #include "host/hcd.h" #include "sam.h" -#include "same5x_usb_compat.h" +#include "same5x_usb.h" /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM diff --git a/src/portable/microchip/samd/same5x_usb_compat.h b/src/portable/microchip/samd/same5x_usb.h similarity index 97% rename from src/portable/microchip/samd/same5x_usb_compat.h rename to src/portable/microchip/samd/same5x_usb.h index 38df5c90..3dc9fbce 100644 --- a/src/portable/microchip/samd/same5x_usb_compat.h +++ b/src/portable/microchip/samd/same5x_usb.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef _SAME5X_USB_COMPAT_H_ -#define _SAME5X_USB_COMPAT_H_ +#ifndef _SAME5X_USB_H_ +#define _SAME5X_USB_H_ #if defined(__GNUC__) #pragma GCC system_header From 1707645f6249bb129da32b791ad23bb5a0d269d1 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 22 Jun 2026 12:09:47 -0400 Subject: [PATCH 4/4] Rerun CI