summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2025-09-12 15:49:40 +0200
committerHiFiPhile <[email protected]>2025-09-12 15:49:40 +0200
commitfad3dc492630a6a31a38b3340f506849ef3679ff (patch)
tree5ab1fef9daf657c1b3f1b7e238e02eb78e1b14cf /src/portable
parent4ae433fa6ef451cccb2b09aa3748978b46aade5f (diff)
parent2027ac246791caa9f1dc03e9727b60d90bee0f2d (diff)
Merge branch 'master' into sb-ep
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/microchip/samd/hcd_samd.c765
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c3
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c2
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_at32.h220
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_at32.h121
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c25
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h2
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py3
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h94
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c2
-rw-r--r--src/portable/wch/hcd_ch32_usbfs.c669
13 files changed, 1958 insertions, 70 deletions
diff --git a/src/portable/microchip/samd/hcd_samd.c b/src/portable/microchip/samd/hcd_samd.c
new file mode 100644
index 000000000..1f4b2b233
--- /dev/null
+++ b/src/portable/microchip/samd/hcd_samd.c
@@ -0,0 +1,765 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 ChrisDeadman
+ *
+ * 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.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && \
+ !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) && \
+ (CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
+ CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \
+ CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21)
+
+#include "host/hcd.h"
+#include "sam.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define USB_HOST_PTYPE_DIS 0x0
+#define USB_HOST_PTYPE_CTRL 0x1
+#define USB_HOST_PTYPE_ISO 0x2
+#define USB_HOST_PTYPE_BULK 0x3
+#define USB_HOST_PTYPE_INT 0x4
+#define USB_HOST_PTYPE_EXT 0x5
+
+#define USB_HOST_PCFG_PTOKEN_SETUP 0x0
+#define USB_HOST_PCFG_PTOKEN_IN 0x1
+#define USB_HOST_PCFG_PTOKEN_OUT 0x2
+
+#define USB_PCKSIZE_ENUM(size) \
+ ((size) >= 1024 ? 7 \
+ : (size) >= 1023 ? 7 \
+ : (size) > 256 ? 6 \
+ : (size) > 128 ? 5 \
+ : (size) > 64 ? 4 \
+ : (size) > 32 ? 3 \
+ : (size) > 16 ? 2 \
+ : (size) > 8 ? 1 \
+ : 0)
+
+// Uncomment to use fake frame number.
+// Low-Speed devices stall FNUM during enumeration :/
+// #define HCD_SAMD_FAKE_FNUM
+
+typedef struct {
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint16_t max_packet_size;
+ uint16_t xfer_length;
+ uint16_t xfer_remaining;
+} usb_pipe_status_t;
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile UsbHostDescriptor usb_pipe_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile usb_pipe_status_t usb_pipe_status_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile uint32_t fake_fnum;
+
+static uint8_t samd_configure_pipe(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t pipe;
+ uint8_t token;
+ volatile usb_pipe_status_t* pipe_status;
+ bool same_addr = false;
+ bool same_ep_addr = false;
+
+ // evaluate pipe token
+ token = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_HOST_PCFG_PTOKEN_IN
+ : tu_edpt_number(ep_addr) == 0 ? USB_HOST_PCFG_PTOKEN_SETUP
+ : USB_HOST_PCFG_PTOKEN_OUT;
+
+ TU_LOG3("samd_configure_pipe(token=%02X, dev_addr=%02X, ep_addr=%02X)=", token, dev_addr, ep_addr);
+
+ // find already allocated pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ same_addr = (pipe_status->dev_addr == dev_addr);
+ same_ep_addr = (tu_edpt_number(pipe_status->ep_addr) == tu_edpt_number(ep_addr));
+ if (same_ep_addr && (same_addr || (tu_edpt_number(ep_addr) == 0))) {
+ break;
+ }
+ }
+
+ // allocate from pool of free pipes
+ if (pipe >= USB_PIPE_NUM) {
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ // found a free pipe
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ break;
+ }
+ }
+ }
+
+ // no pipe available :(
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return pipe;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer should be in progress
+ TU_ASSERT(((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_DIS) ||
+ USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1),
+ USB_PIPE_NUM);
+
+ // update addr and ep_addr
+ pipe_status->dev_addr = dev_addr;
+ pipe_status->ep_addr = ep_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PDADDR = dev_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PEPNUM = tu_edpt_number(ep_addr);
+
+ // token specific configuration
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTOKEN = token;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ if (token == USB_HOST_PCFG_PTOKEN_SETUP) {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TXSTP;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else if (token == USB_HOST_PCFG_PTOKEN_IN) {
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ }
+
+ return pipe;
+}
+
+static void samd_free_pipe(uint8_t pipe)
+{
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->dev_addr = UINT8_MAX;
+ pipe_status->ep_addr = UINT8_MAX;
+ pipe_status->max_packet_size = 0;
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ memset((uint8_t*)(uintptr_t) &usb_pipe_table[pipe], 0, sizeof(usb_pipe_table[pipe]));
+}
+
+static void samd_free_all_pipes(void)
+{
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ samd_free_pipe(pipe);
+ }
+}
+
+static bool samd_on_xfer(uint8_t pipe, xfer_result_t xfer_result)
+{
+ uint16_t xfer_delta;
+ bool xfer_complete;
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+
+ // freeze the pipe
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+
+ // get number of transferred bytes
+ if (xfer_result == XFER_RESULT_SUCCESS) {
+ xfer_delta = usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
+ } else {
+ xfer_delta = 0;
+ }
+
+ TU_LOG3("samd_on_xfer(%d, result=%d, xdelta=%d, rem=%d)\r\n", xfer_result, pipe, xfer_delta, pipe_status->xfer_remaining);
+
+ // update pipe status
+ if (xfer_delta > pipe_status->xfer_remaining) {
+ xfer_delta = pipe_status->xfer_remaining;
+ }
+ pipe_status->xfer_remaining -= xfer_delta;
+ pipe_status->xfer_length += xfer_delta;
+
+ // last packet handling
+ if (xfer_delta < pipe_status->max_packet_size) {
+ pipe_status->xfer_remaining = 0;
+ }
+
+ // transfer complete
+ xfer_complete = (xfer_result != XFER_RESULT_SUCCESS) || (pipe_status->xfer_remaining == 0);
+ if (xfer_complete) {
+ return true;
+ }
+
+ // continue receiving
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // continue sending
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // advance packet buffer
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg += xfer_delta;
+
+ // start next transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// Interrupt Handler
+void hcd_int_handler(uint8_t rhport, bool in_isr)
+{
+ (void) rhport;
+
+ uint16_t int_flags;
+ uint8_t pint_flags;
+ xfer_result_t xfer_result;
+ volatile usb_pipe_status_t* pipe_status;
+
+ //
+ // Check INTFLAG
+ //
+ int_flags = USB->HOST.INTFLAG.reg;
+ if (int_flags & USB_HOST_INTFLAG_HSOF) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_HSOF;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RST) {
+ TU_LOG2("USB_HOST_INTFLAG_RST\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ }
+ if (int_flags & USB_HOST_INTFLAG_WAKEUP) {
+ TU_LOG3("USB_HOST_INTFLAG_WAKEUP\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_WAKEUP;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DNRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_DNRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DNRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_UPRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_UPRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_UPRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RAMACER) {
+ TU_LOG1("USB_HOST_INTFLAG_RAMACER\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RAMACER;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DCONN) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DCONN;
+ hcd_event_device_attach(rhport, in_isr);
+ }
+ if (int_flags & USB_HOST_INTFLAG_DDISC) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DDISC;
+ hcd_event_device_remove(rhport, in_isr);
+ }
+
+ // handle pipe interrupts
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ // get pipe handle
+ pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ continue;
+ }
+
+ //
+ // Check PINTFLAG
+ //
+ pint_flags = USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+ xfer_result = XFER_RESULT_INVALID;
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT0) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT0;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT1) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT1;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TXSTP) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TXSTP;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_STALL) {
+ TU_LOG2("USB_HOST_PINTFLAG_STALL\r\n");
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_STALL;
+ xfer_result = XFER_RESULT_STALLED;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRFAIL) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRFAIL;
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_ERRORFLOW) {
+ TU_LOG1("USB_HOST_STATUS_BK_ERRORFLOW\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_CRCERR) {
+ TU_LOG1("USB_HOST_STATUS_BK_CRCERR\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else {
+ // SAMD Quirk #1:
+ // Likes to report TRFAIL for no apparent reason -> ignore
+ }
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_PERR) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_PERR;
+ // Handled by STATUS_PIPE checks below
+ }
+
+ //
+ // Check STATUS_PIPE
+ //
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DTGLER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DTGLER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DTGLER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DAPIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DAPIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DAPIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_PIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_PIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_PIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_CRC16ER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_CRC16ER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_CRC16ER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_TOUTER) {
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_TOUTER;
+
+ if ((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_INT) &&
+ (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN)) {
+ // ignore timeouts from INT pipes
+ } else {
+ if (xfer_result == XFER_RESULT_INVALID) {
+ xfer_result = XFER_RESULT_TIMEOUT;
+ }
+ }
+ }
+
+ // prevent PERR from too high error counts, that is handled by TinyUSB anyways
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.bit.ERCNT = 0;
+
+ // no updates
+ if (xfer_result == XFER_RESULT_INVALID) {
+ continue;
+ }
+
+ // continue / complete transfer
+ if (samd_on_xfer(pipe, xfer_result)) {
+ hcd_event_xfer_complete(pipe_status->dev_addr, pipe_status->ep_addr, pipe_status->xfer_length, xfer_result, true);
+ }
+ }
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ TU_ASSERT(rhport == 0);
+ (void) rh_init;
+ fake_fnum = 0;
+
+ // reset to get in a clean state.
+ USB->HOST.CTRLA.bit.SWRST = 1;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 0)
+ ;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 1)
+ ;
+
+ // load pad calibration
+ USB->HOST.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+ USB->HOST.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+ USB->HOST.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+ USB->HOST.QOSCTRL.bit.CQOS = 3; // High Quality
+ USB->HOST.QOSCTRL.bit.DQOS = 3; // High Quality
+
+ // configure host-mode
+ samd_free_all_pipes(); // initializes pipe handles and usb_pipe_table
+ USB->HOST.DESCADD.reg = (uint32_t) (&usb_pipe_table[0]);
+ USB->HOST.CTRLB.reg = USB_HOST_CTRLB_SPDCONF_NORMAL | USB_HOST_CTRLB_VBUSOK;
+ USB->HOST.CTRLA.reg = USB_CTRLA_MODE_HOST | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
+ while (USB->HOST.SYNCBUSY.bit.ENABLE == 1)
+ ;
+
+ // enable basic USB interrupts
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.INTENCLR.reg = USB_HOST_INTENCLR_MASK;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DCONN;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DDISC;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_WAKEUP;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_RST;
+
+ return true;
+}
+
+#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_0_IRQn);
+ NVIC_EnableIRQ(USB_1_IRQn);
+ NVIC_EnableIRQ(USB_2_IRQn);
+ NVIC_EnableIRQ(USB_3_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_3_IRQn);
+ NVIC_DisableIRQ(USB_2_IRQn);
+ NVIC_DisableIRQ(USB_1_IRQn);
+ NVIC_DisableIRQ(USB_0_IRQn);
+}
+
+#elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
+ CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+#else
+
+#error "No implementation available for hcd_int_enable / hcd_int_disable"
+
+#endif
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport)
+{
+ (void) rhport;
+
+// SAMD Quirk #2:
+// FNUM is stalled before enumeration of Low-Speed devices.
+// internal frame counter can be used as workaround (not very accurate)
+#ifdef HCD_SAMD_FAKE_FNUM
+ uint8_t start, current, prev;
+ uint8_t loop_count = (USB->HOST.STATUS.bit.SPEED == TUSB_SPEED_HIGH) ? 8 : 1;
+ for (uint8_t i = 0; i < loop_count; i++) {
+ start = USB->HOST.FLENHIGH.reg;
+ current = start;
+ // wait until wrap-around
+ prev = current;
+ while (current <= start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ // wait until start is reached again
+ prev = current;
+ while (current > start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ }
+ fake_fnum += 1;
+ return fake_fnum;
+#else
+ return USB->HOST.FNUM.bit.FNUM;
+#endif // HCD_SAMD_FAKE_FNUM
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport)
+{
+ TU_ASSERT(rhport == 0);
+ return USB->HOST.STATUS.bit.LINESTATE != 0;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be
+// complete. Some port would require hcd_port_reset_end() to be invoked after 10ms to
+// complete the reset sequence.
+void hcd_port_reset(uint8_t rhport)
+{
+ hcd_int_disable(rhport);
+ samd_free_all_pipes();
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.CTRLB.bit.BUSRESET = 1;
+ fake_fnum = 0;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport)
+{
+ while (USB->HOST.INTFLAG.bit.RST == 0)
+ ;
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ USB->HOST.CTRLB.bit.SOFE = 1;
+ hcd_int_enable(rhport);
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+ (void) rhport;
+
+ switch (USB->HOST.STATUS.bit.SPEED) {
+ case 0:
+ return TUSB_SPEED_FULL;
+ case 1:
+ return TUSB_SPEED_LOW;
+ case 2:
+ return TUSB_SPEED_HIGH;
+ default:
+ return TUSB_SPEED_INVALID;
+ }
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr == dev_addr) {
+ samd_free_pipe(pipe);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+ const uint8_t ep_addr = ep_desc->bEndpointAddress;
+ const uint8_t bmAttributes = (ep_desc->bmAttributes.xfer) |
+ ((ep_desc->bmAttributes.sync) << 2) |
+ ((ep_desc->bmAttributes.usage) << 4);
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // initial configuration
+ pipe_status = &usb_pipe_status_table[pipe];
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE = bmAttributes + 1;
+ USB->HOST.HostPipe[pipe].BINTERVAL.reg = ep_desc->bInterval;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ pipe_status->max_packet_size = ep_desc->wMaxPacketSize;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.SIZE = USB_PCKSIZE_ENUM(pipe_status->max_packet_size);
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.AUTO_ZLP = 0;
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete
+// hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, 0);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) setup_packet;
+ pipe_status->xfer_remaining = 8;
+ pipe_status->xfer_length = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 8;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t* buffer, uint16_t buflen)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) buffer;
+ pipe_status->xfer_remaining = buflen;
+ pipe_status->xfer_length = 0;
+ // receive data
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // send data
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been
+// started Return true if a queued transfer is aborted, false if there is no transfer
+// to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_abort_xfer(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer in progress
+ if (USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1) {
+ return false;
+ }
+
+ // abort the transfer
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ return true;
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_clear_stall(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // clear stalled state
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+
+ return true;
+}
+
+#endif
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 358ce84bc..a89c2f42b 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -190,8 +190,9 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
bool done = hw_endpoint_xfer_continue(ep);
if (done) {
// Notify
- dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
+ const uint16_t xferred_len = ep->xferred_len;
hw_endpoint_reset_transfer(ep);
+ dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true);
}
remaining_buffers &= ~bit;
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index ad733b16a..3b9825e25 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -117,6 +117,8 @@
#include "fsdev_stm32.h"
#elif defined(TUP_USBIP_FSDEV_CH32)
#include "fsdev_ch32.h"
+#elif defined(TUP_USBIP_FSDEV_AT32)
+ #include "fsdev_at32.h"
#else
#error "Unknown USB IP"
#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_at32.h b/src/portable/st/stm32_fsdev/fsdev_at32.h
new file mode 100644
index 000000000..3a785bbbd
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_at32.h
@@ -0,0 +1,220 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (tinyusb.org)
+ *
+ * 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 TUSB_FSDEV_AT32_H
+#define TUSB_FSDEV_AT32_H
+
+#include "common/tusb_compiler.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_AT32F403A_407
+ #include "at32f403a_407.h"
+
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F413
+ #include "at32f413.h"
+
+#endif
+
+#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
+#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
+
+/**************************** ISTR interrupt events *************************/
+#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */
+#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */
+#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */
+#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */
+#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */
+#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */
+#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */
+#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */
+#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */
+#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */
+
+/* Legacy defines */
+#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR
+
+#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */
+#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/
+#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */
+#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */
+#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */
+#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */
+#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */
+#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */
+
+/* Legacy defines */
+#define USB_CLR_PMAOVRM USB_CLR_PMAOVR
+
+/************************* CNTR control register bits definitions ***********/
+#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */
+#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */
+#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */
+#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */
+#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */
+#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */
+#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */
+#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */
+#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */
+#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */
+#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */
+#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */
+#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */
+
+/* Legacy defines */
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR
+#define USB_CNTR_LP_MODE USB_CNTR_LPMODE
+
+/******************** FNR Frame Number Register bit definitions ************/
+#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */
+#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */
+#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */
+#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */
+#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */
+
+/******************** DADDR Device ADDRess bit definitions ****************/
+#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */
+#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */
+
+/****************************** Endpoint register *************************/
+#define USB_EP0R USB_BASE /*!< endpoint 0 register address */
+#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */
+#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */
+#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */
+#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */
+#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */
+#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */
+#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */
+/* bit positions */
+#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */
+#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */
+#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */
+#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */
+#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */
+#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */
+#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */
+#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */
+#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */
+#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */
+
+/* EndPoint REGister MASK (no toggle fields) */
+#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+ /*!< EP_TYPE[1:0] EndPoint TYPE */
+#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */
+#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */
+#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */
+#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */
+#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */
+#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK)
+
+#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */
+ /*!< STAT_TX[1:0] STATus for TX transfer */
+#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */
+#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */
+#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */
+#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */
+#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */
+#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */
+#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK)
+ /*!< STAT_RX[1:0] STATus for RX transfer */
+#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */
+#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */
+#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */
+#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */
+#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
+
+#include "fsdev_type.h"
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+static const IRQn_Type fsdev_irq[] = {
+ USBFS_H_CAN1_TX_IRQn,
+ USBFS_L_CAN1_RX0_IRQn,
+ USBFSWakeUp_IRQn
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+
+#else
+ #error "Unsupported MCU"
+#endif
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+ #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+ // AT32F403A/407 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (CRM->intmap_bit.usbintmap) {
+ NVIC_DisableIRQ(USBFS_MAPH_IRQn);
+ NVIC_DisableIRQ(USBFS_MAPL_IRQn);
+ NVIC_DisableIRQ(USBFSWakeUp_IRQn);
+ } else
+ #endif
+ {
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+ #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+ // AT32F403A/407 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (CRM->intmap_bit.usbintmap) {
+ NVIC_DisableIRQ(USBFS_MAPH_IRQn);
+ NVIC_DisableIRQ(USBFS_MAPL_IRQn);
+ NVIC_DisableIRQ(USBFSWakeUp_IRQn);
+ } else
+ #endif
+ {
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ /* disable usb phy */
+ FSDEV_REG->CNTR |= USB_CNTR_PDWN;
+ /* D+ 1.5k pull-up disable, USB->cfg_bit.puo = TRUE; */
+ *(uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ /* enable usb phy */
+ FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
+ /* Dp 1.5k pull-up enable, USB->cfg_bit.puo = 0; */
+ *(uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1);
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 5f86d6b76..f10f0bdc3 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -431,7 +431,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
#endif
// Enable required interrupts
- dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
// TX FIFO empty level for interrupt is complete empty
uint32_t gahbcfg = dwc2->gahbcfg;
@@ -1032,16 +1032,19 @@ void dcd_int_handler(uint8_t rhport) {
if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
dwc2->gintsts = GINTSTS_ENUMDNE;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
handle_enum_done(rhport);
}
if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
+ dwc2->gintmsk &= ~GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1061,6 +1064,7 @@ void dcd_int_handler(uint8_t rhport) {
if(gintsts & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
diff --git a/src/portable/synopsys/dwc2/dwc2_at32.h b/src/portable/synopsys/dwc2/dwc2_at32.h
new file mode 100644
index 000000000..37b6592c4
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_at32.h
@@ -0,0 +1,121 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * 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.
+ */
+
+
+#ifndef DWC2_AT32_H_
+#define DWC2_AT32_H_
+
+#define DWC2_EP_MAX TUP_DCD_ENDPOINT_MAX
+
+#if CFG_TUSB_MCU == OPT_MCU_AT32F415
+ #include <at32f415.h>
+ #define OTG1_FIFO_SIZE 1280
+ #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_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_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_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_IRQn OTGFS1_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+#endif
+
+#ifdef __cplusplus
+ 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},
+#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}
+#endif
+ };
+
+ 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;
+ if (enabled) {
+ NVIC_EnableIRQ(irqn);
+ } else {
+ NVIC_DisableIRQ(irqn);
+ }
+ }
+
+ TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
+ }
+
+ TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
+ }
+
+ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
+ // try to delay for 1 ms
+ uint32_t count = system_core_clock / 1000;
+ while (count--) __asm volatile("nop");
+ }
+
+ // 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) {
+ (void) dwc2;
+ // Enable on-chip HS PHY
+ if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
+ } else if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
+ }
+ }
+
+ // 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) {
+ (void) dwc2;
+ (void) hs_phy_type;
+
+ dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN | STM32_GCCFG_DCDEN | STM32_GCCFG_PDEN;
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DWC2_GD32_H_ */
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index e1e7d5c1a..d7d157149 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ * Copyright (c) 2024-2025 Ha Thach (tinyusb.org)
*
* 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,11 +45,14 @@
//
//--------------------------------------------------------------------
static void reset_core(dwc2_regs_t* dwc2) {
+ // load gsnpsid (it is not readable after reset is asserted)
+ uint32_t gsnpsid = dwc2->gsnpsid;
+
// reset core
dwc2->grstctl |= GRSTCTL_CSRST;
- if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
- // prior v42.0 CSRST is self-clearing
+ if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ // prior v4.20a CSRST is self-clearing
while (dwc2->grstctl & GRSTCTL_CSRST) {}
} else {
// From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking.
@@ -92,6 +95,14 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4};
+ uint8_t phy_width;
+ if (CFG_TUSB_MCU != OPT_MCU_AT32F402_405 && // at32f402_405 does not support 16-bit
+ ghwcfg4.phy_data_width) {
+ phy_width = 16; // 16-bit PHY interface if supported
+ } else {
+ phy_width = 8; // 8-bit PHY interface
+ }
+
// De-select FS PHY
gusbcfg &= ~GUSBCFG_PHYSEL;
@@ -119,10 +130,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
// Set 16-bit interface if supported
- if (ghwcfg4.phy_data_width) {
- gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ if (phy_width == 16) {
+ gusbcfg |= GUSBCFG_PHYIF16;
} else {
- gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
+ gusbcfg &= ~GUSBCFG_PHYIF16;
}
}
@@ -139,7 +150,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// - 9 if using 8-bit PHY interface
// - 5 if using 16-bit PHY interface
gusbcfg &= ~GUSBCFG_TRDT_Msk;
- gusbcfg |= (ghwcfg4.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
+ gusbcfg |= (phy_width == 16 ? 5u : 9u) << GUSBCFG_TRDT_Pos;
dwc2->gusbcfg = gusbcfg;
// MCU specific PHY update post reset
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index 18b93894f..33219f786 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -49,6 +49,8 @@
#include "dwc2_efm32.h"
#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
#include "dwc2_xmc.h"
+#elif defined(TUP_USBIP_DWC2_AT32)
+ #include "dwc2_at32.h"
#else
#error "Unsupported MCUs"
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index 595c89b75..e6b7820bc 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
-|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------|
-| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
-| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
-| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
-| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
-| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
-| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
-| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
-| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
-| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
-| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------|
+| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
+| - single_point | hub | hub | hub | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
+| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
+| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
+| - optional_feature_removed | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
+| - num_dev_period_in_ep | 0 | 0 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - ahb_freq_min | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
+| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - vbus_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index 3b1993cc5..a90150632 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -9,6 +9,9 @@ import pandas as pd
# Note: FS is FullSpeed, HS is HighSpeed
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 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],
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index dc4251c29..08950ccc0 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -113,6 +113,22 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
#endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32WBA
+ #if defined(STM32WBA62xx)
+ #include "stm32wba62xx.h"
+ #elif defined(STM32WBA64xx)
+ #include "stm32wba64xx.h"
+ #elif defined(STM32WBA65xx)
+ #include "stm32wba65xx.h"
+ #else
+ #error "The selected STM32WBA series chip does not support OTG USB HS"
+ #endif
+
+ #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
#else
#error "Unsupported MCUs"
#endif
@@ -166,6 +182,7 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// MCU specific PHY init, called BEFORE core reset
// - dwc2 3.30a (H5) use USB_HS_PHYC
// - dwc2 4.11a (U5) use femtoPHY
+// - dwc2 x.xxx (WBA) use USB_OTG_HS
static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Enable on-chip FS PHY
@@ -194,11 +211,10 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
#endif
} else {
-#if CFG_TUSB_MCU != OPT_MCU_STM32U5
+#if CFG_TUSB_MCU != OPT_MCU_STM32U5 && CFG_TUSB_MCU != OPT_MCU_STM32WBA
// Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN;
#endif
-
// Enable on-chip HS PHY
if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
#ifdef USB_HS_PHYC
@@ -233,6 +249,9 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// Enable PLL internal PHY
USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN;
+
+ // Wait ~2ms until the PLL is ready (there's no RDY bit to query)
+ tusb_time_delay_ms_api(2);
#else
#endif
@@ -279,6 +298,77 @@ static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
}
}
+//------------- DCache -------------//
+#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
+
+typedef struct {
+ uintptr_t start;
+ uintptr_t end;
+} mem_region_t;
+
+// Can be used to define additional uncached regions
+#ifndef CFG_DWC2_MEM_UNCACHED_REGIONS
+#define CFG_DWC2_MEM_UNCACHED_REGIONS
+#endif
+
+static mem_region_t uncached_regions[] = {
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+#if CFG_TUSB_MCU == OPT_MCU_STM32H7
+ {.start = 0x20000000, .end = 0x2001FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+ {.start = 0x20000000, .end = 0x2002FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
+ // DTCM
+ {.start = 0x20000000, .end = 0x2000FFFF},
+#else
+#error "Cache maintenance is not supported yet"
+#endif
+ CFG_DWC2_MEM_UNCACHED_REGIONS
+};
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) {
+ size = (size & ~(CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) + CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT;
+ }
+ return size;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uintptr_t addr) {
+ for (unsigned int i = 0; i < TU_ARRAY_SIZE(uncached_regions); i++) {
+ if (uncached_regions[i].start <= addr && addr <= uncached_regions[i].end) { return false; }
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index f8bf3c889..4a208b9df 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -27,7 +27,7 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && defined(CFG_TUD_WCH_USBIP_USBHS) && CFG_TUD_WCH_USBIP_USBHS
#include "ch32_usbhs_reg.h"
#include "device/dcd.h"
diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c
new file mode 100644
index 000000000..200136906
--- /dev/null
+++ b/src/portable/wch/hcd_ch32_usbfs.c
@@ -0,0 +1,669 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Mitsumine Suzu (verylowfreq)
+ *
+ * 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.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_WCH_USBFS) && defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS
+
+#include <stdlib.h>
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "host/usbh_pvt.h"
+
+#include "bsp/board_api.h"
+
+#include "ch32v20x.h"
+#include "ch32v20x_usb.h"
+
+#define USBFS_RX_BUF_LEN 64
+#define USBFS_TX_BUF_LEN 64
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_RX_Buf[USBFS_RX_BUF_LEN];
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_TX_Buf[USBFS_TX_BUF_LEN];
+
+#define USB_XFER_TIMEOUT_MILLIS 100
+// #define USB_INTERRUPT_XFER_TIMEOUT_MILLIS 1
+
+#define PANIC(...) \
+ do { \
+ printf("%s() L%d: ", __func__, __LINE__); \
+ printf("\r\n[PANIC] " __VA_ARGS__); \
+ while (true) {} \
+ } while (false)
+
+#define LOG_CH32_USBFSH(...) TU_LOG3(__VA_ARGS__)
+
+// Busywait for delay microseconds/nanoseconds
+TU_ATTR_ALWAYS_INLINE static inline void loopdelay(uint32_t count) {
+ volatile uint32_t c = count / 3;
+ if (c == 0) { return; }
+ // while (c-- != 0);
+ asm volatile(
+ "1: \n" // loop label
+ " addi %0, %0, -1 \n" // c--
+ " bne %0, zero, 1b \n" // if (c != 0) goto loop
+ : "+r"(c) // c is input/output operand
+ );
+}
+
+// Endpoint status
+typedef struct usb_edpt {
+ // Is this a valid struct
+ bool configured;
+
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t max_packet_size;
+
+ uint8_t xfer_type;
+
+ // Data toggle (0 or not 0) for DATA0/1
+ uint8_t data_toggle;
+
+ bool is_nak_pending;
+ uint16_t buflen;
+ uint8_t* buf;
+} usb_edpt_t;
+
+static usb_edpt_t usb_edpt_list[CFG_TUH_DEVICE_MAX * 6] = {};
+
+typedef struct usb_current_xfer_st {
+ bool is_busy;
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ // Xfer started time in millis for timeout
+ uint32_t start_ms;
+ uint8_t *buffer;
+ uint16_t bufferlen;
+ uint16_t xferred_len;
+ bool nak_pending;
+} usb_current_xfer_t;
+
+static volatile usb_current_xfer_t usb_current_xfer_info = {};
+
+static usb_edpt_t *get_edpt_record(uint8_t dev_addr, uint8_t ep_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ usb_edpt_t *cur = &usb_edpt_list[i];
+ if (cur->configured && cur->dev_addr == dev_addr && cur->ep_addr == ep_addr) {
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *get_empty_record_slot(void) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (!usb_edpt_list[i].configured) {
+ return &usb_edpt_list[i];
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *slot = get_empty_record_slot();
+ TU_ASSERT(slot != NULL, NULL);
+
+ slot->dev_addr = dev_addr;
+ slot->ep_addr = ep_addr;
+ slot->max_packet_size = max_packet_size;
+ slot->xfer_type = xfer_type;
+ slot->data_toggle = 0;
+ slot->is_nak_pending = false;
+ slot->buflen = 0;
+ slot->buf = NULL;
+
+ slot->configured = true;
+
+ return slot;
+}
+
+static usb_edpt_t *get_or_add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *ret = get_edpt_record(dev_addr, ep_addr);
+ if (ret != NULL) {
+ return ret;
+ } else {
+ return add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type);
+ }
+}
+
+static void remove_edpt_record_for_device(uint8_t dev_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (usb_edpt_list[i].configured && usb_edpt_list[i].dev_addr == dev_addr) {
+ usb_edpt_list[i].configured = false;
+ }
+ }
+}
+
+// static void dump_edpt_record_list() {
+// for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+// usb_edpt_t* cur = &usb_edpt_list[i];
+// if (cur->configured) {
+// printf("[%2d] Device 0x%02x Endpoint 0x%02x\r\n", i, cur->dev_addr, cur->ep_addr);
+// } else {
+// printf("[%2d] not configured\r\n", i);
+// }
+// }
+// }
+
+static bool interrupt_enabled = false;
+
+/** Enable or disable USBFS Host function */
+static void hardware_init_host(bool enabled) {
+ // Reset USBOTG module
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
+
+ tusb_time_delay_ms_api(1);
+ USBOTG_H_FS->BASE_CTRL = 0;
+
+ if (!enabled) {
+ // Disable all feature
+ USBOTG_H_FS->BASE_CTRL = 0;
+ } else {
+ // Enable USB Host features
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(0);
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_HOST_MODE | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
+ USBOTG_H_FS->HOST_EP_MOD = USBFS_UH_EP_TX_EN | USBFS_UH_EP_RX_EN;
+ USBOTG_H_FS->HOST_RX_DMA = (uint32_t) USBFS_RX_Buf;
+ USBOTG_H_FS->HOST_TX_DMA = (uint32_t) USBFS_TX_Buf;
+ // USBOTG_H_FS->INT_EN = USBFS_UIE_TRANSFER | USBFS_UIE_DETECT;
+ USBOTG_H_FS->INT_EN = USBFS_UIE_DETECT;
+ }
+}
+
+static bool hardware_start_xfer(uint8_t pid, uint8_t ep_addr, uint8_t data_toggle) {
+ LOG_CH32_USBFSH("hardware_start_xfer(pid=%s(0x%02x), ep_addr=0x%02x, toggle=%d)\r\n",
+ pid == USB_PID_IN ? "IN" : pid == USB_PID_OUT ? "OUT"
+ : pid == USB_PID_SETUP ? "SETUP"
+ : "(other)",
+ pid, ep_addr, data_toggle);
+
+ //WORKAROUND: For LowSpeed device, insert small delay
+ bool is_lowspeed_device = tuh_speed_get(usb_current_xfer_info.dev_addr) == TUSB_SPEED_LOW;
+ if (is_lowspeed_device) {
+ //NOTE: worked -> SystemCoreClock / 1000000 * 50, 25
+ // NOT worked -> 20 and less (at 144MHz internal clock)
+ loopdelay(SystemCoreClock / 1000000 * 40);
+ }
+
+ uint8_t pid_edpt = (pid << 4) | (tu_edpt_number(ep_addr) & 0x0f);
+ USBOTG_H_FS->HOST_TX_CTRL = (data_toggle != 0) ? USBFS_UH_T_TOG : 0;
+ USBOTG_H_FS->HOST_RX_CTRL = (data_toggle != 0) ? USBFS_UH_R_TOG : 0;
+ USBOTG_H_FS->HOST_EP_PID = pid_edpt;
+ USBOTG_H_FS->INT_EN |= USBFS_UIE_TRANSFER;
+ USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ return true;
+}
+
+
+/** Set device address to communicate */
+static void hardware_update_device_address(uint8_t dev_addr) {
+ // Keep the bit of GP_BIT. Other 7bits are actual device address.
+ USBOTG_H_FS->DEV_ADDR = (USBOTG_H_FS->DEV_ADDR & USBFS_UDA_GP_BIT) | (dev_addr & USBFS_USB_ADDR_MASK);
+}
+
+/** Set port speed */
+static void hardware_update_port_speed(tusb_speed_t speed) {
+ LOG_CH32_USBFSH("hardware_update_port_speed(%s)\r\n", speed == TUSB_SPEED_FULL ? "Full" : speed == TUSB_SPEED_LOW ? "Low"
+ : "(invalid)");
+ switch (speed) {
+ case TUSB_SPEED_LOW:
+ USBOTG_H_FS->BASE_CTRL |= USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_PRE_PID_EN;
+ return;
+ case TUSB_SPEED_FULL:
+ USBOTG_H_FS->BASE_CTRL &= ~USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP &= ~USBFS_UH_PRE_PID_EN;
+ return;
+ default:
+ PANIC("hardware_update_port_speed(%d)\r\n", speed);
+ }
+}
+
+static void hardware_set_port_address_speed(uint8_t dev_addr) {
+ hardware_update_device_address(dev_addr);
+ tusb_speed_t rhport_speed = hcd_port_speed_get(0);
+ tusb_speed_t dev_speed = tuh_speed_get(dev_addr);
+ hardware_update_port_speed(dev_speed);
+ if (rhport_speed == TUSB_SPEED_FULL && dev_speed == TUSB_SPEED_LOW) {
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ }
+}
+
+static bool hardware_device_attached(void) {
+ return USBOTG_H_FS->MIS_ST & USBFS_UMS_DEV_ATTACH;
+}
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void) rhport;
+ (void) rh_init;
+ hardware_init_host(true);
+
+ return true;
+}
+
+bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+ hardware_init_host(false);
+
+ return true;
+}
+
+static bool int_state_for_portreset = false;
+
+void hcd_port_reset(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset()\r\n");
+ int_state_for_portreset = interrupt_enabled;
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(rhport);
+ hardware_update_device_address(0x00);
+
+ // USBOTG_H_FS->HOST_SETUP = 0x00;
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_BUS_RESET;
+
+ return;
+}
+
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset_end()\r\n");
+
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_BUS_RESET;
+ tusb_time_delay_ms_api(2);
+
+ if ((USBOTG_H_FS->HOST_CTRL & USBFS_UH_PORT_EN) == 0) {
+ if (hcd_port_speed_get(0) == TUSB_SPEED_LOW) {
+ hardware_update_port_speed(TUSB_SPEED_LOW);
+ }
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ // Suppress the attached event
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_DETECT;
+
+ if (int_state_for_portreset) {
+ hcd_int_enable(rhport);
+ }
+}
+
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+
+ return hardware_device_attached();
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+ if (USBOTG_H_FS->MIS_ST & USBFS_UMS_DM_LEVEL) {
+ return TUSB_SPEED_LOW;
+ } else {
+ return TUSB_SPEED_FULL;
+ }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_device_close(%d, 0x%02x)\r\n", rhport, dev_addr);
+ remove_edpt_record_for_device(dev_addr);
+}
+
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+
+ return tusb_time_millis_api();
+}
+
+void hcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBFS_IRQn);
+ interrupt_enabled = true;
+}
+
+void hcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBFS_IRQn);
+ interrupt_enabled = false;
+}
+
+
+static void xfer_retry(void* _params) {
+ LOG_CH32_USBFSH("xfer_retry()\r\n");
+ usb_edpt_t* edpt_info = (usb_edpt_t*)_params;
+ if (usb_current_xfer_info.nak_pending) {
+ usb_current_xfer_info.nak_pending = false;
+ edpt_info->is_nak_pending = false;
+
+ uint8_t dev_addr = edpt_info->dev_addr;
+ uint8_t ep_addr = edpt_info->ep_addr;
+ uint16_t buflen = edpt_info->buflen;
+ uint8_t* buf = edpt_info->buf;
+
+ // Check connectivity
+ usb_edpt_t* edpt_info_current = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info_current) {
+ hcd_edpt_xfer(0, dev_addr, ep_addr, buf, buflen);
+ }
+ }
+}
+
+
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) rhport;
+ (void) in_isr;
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_DETECT) {
+ // Clear the flag
+ USBOTG_H_FS->INT_FG = USBFS_UIF_DETECT;
+ // Read the detection state
+ bool attached = hardware_device_attached();
+ LOG_CH32_USBFSH("hcd_int_handler() attached = %d\r\n", attached ? 1 : 0);
+ if (attached) {
+ hcd_event_device_attach(rhport, true);
+ } else {
+ hcd_event_device_remove(rhport, true);
+ }
+ return;
+ }
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) {
+ // Disable transfer interrupt
+ USBOTG_H_FS->INT_EN &= ~USBFS_UIE_TRANSFER;
+ // Clear the flag
+ // USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ // Copy PID and Endpoint
+ uint8_t pid_edpt = USBOTG_H_FS->HOST_EP_PID;
+ uint8_t status = USBOTG_H_FS->INT_ST;
+ uint8_t dev_addr = USBOTG_H_FS->DEV_ADDR & USBFS_USB_ADDR_MASK;
+ // Clear register to stop transfer
+ // USBOTG_H_FS->HOST_EP_PID = 0x00;
+
+ LOG_CH32_USBFSH("hcd_int_handler() pid_edpt=0x%02x\r\n", pid_edpt);
+
+ uint8_t request_pid = pid_edpt >> 4;
+ uint8_t response_pid = status & USBFS_UIS_H_RES_MASK;
+ uint8_t ep_addr = pid_edpt & 0x0f;
+ if (request_pid == USB_PID_IN) {
+ ep_addr |= 0x80;
+ }
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info == NULL) {
+ PANIC("\r\nget_edpt_record(0x%02x, 0x%02x) returned NULL in USBHD_IRQHandler\r\n", dev_addr, ep_addr);
+ }
+
+ if (status & USBFS_UIS_TOG_OK) {
+ edpt_info->data_toggle ^= 0x01;
+
+ switch (request_pid) {
+ case USB_PID_SETUP:
+ case USB_PID_OUT: {
+ uint16_t tx_len = USBOTG_H_FS->HOST_TX_LEN;
+ usb_current_xfer_info.bufferlen -= tx_len;
+ usb_current_xfer_info.xferred_len += tx_len;
+ if (usb_current_xfer_info.bufferlen == 0) {
+ LOG_CH32_USBFSH("USB_PID_%s completed %d bytes\r\n", request_pid == USB_PID_OUT ? "OUT" : "SETUP", usb_current_xfer_info.xferred_len);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, usb_current_xfer_info.xferred_len, XFER_RESULT_SUCCESS, in_isr);
+ return;
+ } else {
+ LOG_CH32_USBFSH("USB_PID_OUT continue...\r\n");
+ usb_current_xfer_info.buffer += tx_len;
+ uint16_t copylen = TU_MIN(edpt_info->max_packet_size, usb_current_xfer_info.bufferlen);
+ memcpy(USBFS_TX_Buf, usb_current_xfer_info.buffer, copylen);
+ hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ case USB_PID_IN: {
+ uint16_t received_len = USBOTG_H_FS->RX_LEN;
+ usb_current_xfer_info.xferred_len += received_len;
+ uint16_t xferred_len = usb_current_xfer_info.xferred_len;
+ LOG_CH32_USBFSH("Read %d bytes\r\n", received_len);
+ // if (received_len > 0 && (usb_current_xfer_info.buffer == NULL || usb_current_xfer_info.bufferlen == 0)) {
+ // PANIC("Data received but buffer not set\r\n");
+ // }
+ memcpy(usb_current_xfer_info.buffer, USBFS_RX_Buf, received_len);
+ usb_current_xfer_info.buffer += received_len;
+ if ((received_len < edpt_info->max_packet_size) || (xferred_len == usb_current_xfer_info.bufferlen)) {
+ // USB device sent all data.
+ LOG_CH32_USBFSH("USB_PID_IN completed\r\n");
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, XFER_RESULT_SUCCESS, in_isr);
+ return;
+ } else {
+ // USB device may send more data.
+ LOG_CH32_USBFSH("Read more data\r\n");
+ hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ default: {
+ LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ }
+ }
+ } else {
+ if (response_pid == USB_PID_STALL) {
+ LOG_CH32_USBFSH("STALL response\r\n");
+ hcd_edpt_clear_stall(0, dev_addr, ep_addr);
+ edpt_info->data_toggle = 0;
+ hardware_start_xfer(request_pid, ep_addr, 0);
+ return;
+ } else if (response_pid == USB_PID_NAK) {
+ LOG_CH32_USBFSH("NAK reposense\r\n");
+ uint32_t elapsed_time = tusb_time_millis_api() - usb_current_xfer_info.start_ms;
+ (void)elapsed_time;
+ if (edpt_info->xfer_type == TUSB_XFER_INTERRUPT) {
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, in_isr);
+
+ } else {
+ usb_current_xfer_info.is_busy = false;
+ usb_current_xfer_info.nak_pending = true;
+
+
+ edpt_info->is_nak_pending = true;
+ edpt_info->buflen = usb_current_xfer_info.bufferlen;
+ edpt_info->buf = usb_current_xfer_info.buffer;
+
+ hcd_event_t event = {
+ .rhport = rhport,
+ .dev_addr = dev_addr,
+ .event_id = USBH_EVENT_FUNC_CALL,
+ .func_call = {
+ .func = xfer_retry,
+ .param = edpt_info
+ }
+ };
+ hcd_event_handler(&event, in_isr);
+ }
+ return;
+ } else if (response_pid == USB_PID_DATA0 || response_pid == USB_PID_DATA1) {
+ LOG_CH32_USBFSH("Data toggle mismatched and DATA0/1 (not STALL). RX_LEN=%d\r\n", USBOTG_H_FS->RX_LEN);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ } else {
+ LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc) {
+ (void) rhport;
+ uint8_t ep_addr = ep_desc->bEndpointAddress;
+ uint8_t ep_num = tu_edpt_number(ep_addr);
+ uint16_t max_packet_size = ep_desc->wMaxPacketSize;
+ uint8_t xfer_type = ep_desc->bmAttributes.xfer;
+ LOG_CH32_USBFSH("hcd_edpt_open(rhport=%d, dev_addr=0x%02x, %p) EndpointAdderss=0x%02x,maxPacketSize=%d,xfer_type=%d\r\n", rhport, dev_addr, ep_desc, ep_addr, max_packet_size, xfer_type);
+
+ while (usb_current_xfer_info.is_busy) { }
+
+ if (ep_num == 0x00) {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x00, max_packet_size, xfer_type) != NULL, false);
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x80, max_packet_size, xfer_type) != NULL, false);
+ } else {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type) != NULL, false);
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ hardware_set_port_address_speed(dev_addr);
+
+ return true;
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
+ (void) rhport;
+
+ LOG_CH32_USBFSH("hcd_edpt_xfer(%d, 0x%02x, 0x%02x, ...)\r\n", rhport, dev_addr, ep_addr);
+
+ while (usb_current_xfer_info.is_busy) {}
+ usb_current_xfer_info.is_busy = true;
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ TU_ASSERT(edpt_info != NULL);
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.buffer = buffer;
+ usb_current_xfer_info.bufferlen = buflen;
+ usb_current_xfer_info.start_ms = tusb_time_millis_api();
+ usb_current_xfer_info.xferred_len = 0;
+ usb_current_xfer_info.nak_pending = false;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): READ, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ return hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ } else {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): WRITE, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ uint16_t copylen = TU_MIN(edpt_info->max_packet_size, buflen);
+ USBOTG_H_FS->HOST_TX_LEN = copylen;
+ memcpy(USBFS_TX_Buf, buffer, copylen);
+ return hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ }
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+
+ while (usb_current_xfer_info.is_busy) {}
+
+ usb_current_xfer_info.is_busy = true;
+
+ LOG_CH32_USBFSH("hcd_setup_send(rhport=%d, dev_addr=0x%02x, %p)\r\n", rhport, dev_addr, setup_packet);
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_edpt_t *edpt_info_tx = get_edpt_record(dev_addr, 0x00);
+ usb_edpt_t *edpt_info_rx = get_edpt_record(dev_addr, 0x80);
+ TU_ASSERT(edpt_info_tx != NULL, false);
+ TU_ASSERT(edpt_info_rx != NULL, false);
+
+ // Initialize data toggle (SETUP always starts with DATA0)
+ // Data toggle for OUT is toggled in hcd_int_handler()
+ edpt_info_tx->data_toggle = 0;
+ // Data toggle for IN must be set 0x01 manually.
+ edpt_info_rx->data_toggle = 0x01;
+ const uint16_t setup_packet_datalen = 8;
+ memcpy(USBFS_TX_Buf, setup_packet, setup_packet_datalen);
+ USBOTG_H_FS->HOST_TX_LEN = setup_packet_datalen;
+ uint8_t ep_addr = (setup_packet[0] & 0x80) ? 0x80 : 0x00;
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.start_ms = tusb_time_millis_api();
+ usb_current_xfer_info.buffer = USBFS_TX_Buf;
+ usb_current_xfer_info.bufferlen = setup_packet_datalen;
+ usb_current_xfer_info.xferred_len = 0;
+ usb_current_xfer_info.nak_pending = false;
+
+ hardware_start_xfer(USB_PID_SETUP, 0, 0);
+
+ return true;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall(rhport=%d, dev_addr=0x%02x, ep_addr=0x%02x)\r\n", rhport, dev_addr, ep_addr);
+ uint8_t edpt_num = tu_edpt_number(ep_addr);
+ uint8_t setup_request_clear_stall[8] = {
+ 0x02, 0x01, 0x00, 0x00, edpt_num, 0x00, 0x00, 0x00
+ };
+ memcpy(USBFS_TX_Buf, setup_request_clear_stall, 8);
+ USBOTG_H_FS->HOST_TX_LEN = 8;
+
+ bool prev_int_state = interrupt_enabled;
+ hcd_int_disable(0);
+
+ USBOTG_H_FS->HOST_EP_PID = (USB_PID_SETUP << 4) | 0x00;
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_TRANSFER;
+ while ((USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) == 0) {}
+ USBOTG_H_FS->HOST_EP_PID = 0;
+ uint8_t response_pid = USBOTG_H_FS->INT_ST & USBFS_UIS_H_RES_MASK;
+ (void) response_pid;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall() response pid=0x%02x\r\n", response_pid);
+
+ if (prev_int_state) {
+ hcd_int_enable(0);
+ }
+
+ return true;
+}
+
+#endif