summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-03-06 15:23:32 +0100
committerHiFiPhile <[email protected]>2026-03-06 15:23:32 +0100
commit860f0e01f21891691185daf048f669ac9797a05a (patch)
tree8e082381198b2fa97d518672ad34905b9575b4aa /src/portable
parent5e49aa8e4d44a8a99a73e8faded2099c82ca023d (diff)
parent06a4c6d7190ef83f76b2a65496f2a48a54a8c134 (diff)
Merge remote-tracking branch 'tinyusb/master' into hcd_ip3516
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_hpm.h54
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h8
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_rw61x.h48
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c422
-rw-r--r--src/portable/chipidea/ci_hs/hcd_ci_hs.c56
-rw-r--r--src/portable/ehci/ehci.c25
-rw-r--r--src/portable/ehci/ehci_api.h3
-rw-r--r--src/portable/mentor/musb/dcd_musb.c100
-rw-r--r--src/portable/mentor/musb/musb_ti.h5
-rw-r--r--src/portable/mentor/musb/musb_type.h2
-rw-r--r--src/portable/microchip/samg/dcd_samg.c4
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c4
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c269
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c220
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c260
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.h179
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c259
-rw-r--r--src/portable/renesas/rusb2/hcd_rusb2.c135
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.c80
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.h58
-rw-r--r--src/portable/renesas/rusb2/rusb2_type.h20
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c512
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_at32.h19
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_ch32.h10
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.c116
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.h (renamed from src/portable/st/stm32_fsdev/fsdev_type.h)223
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h201
-rw-r--r--src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c882
-rw-r--r--src/portable/sunxi/dcd_sunxi_musb.c3
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c167
-rw-r--r--src/portable/synopsys/dwc2/dwc2_at32.h11
-rw-r--r--src/portable/synopsys/dwc2/dwc2_bcm.h7
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c124
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h7
-rw-r--r--src/portable/synopsys/dwc2/dwc2_efm32.h8
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h7
-rw-r--r--src/portable/synopsys/dwc2/dwc2_gd32.h7
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py10
-rw-r--r--src/portable/synopsys/dwc2/dwc2_nrf.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h90
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h42
-rw-r--r--src/portable/synopsys/dwc2/dwc2_xmc.h7
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c91
44 files changed, 2974 insertions, 1903 deletions
diff --git a/src/portable/chipidea/ci_hs/ci_hs_hpm.h b/src/portable/chipidea/ci_hs/ci_hs_hpm.h
new file mode 100644
index 000000000..68211448c
--- /dev/null
+++ b/src/portable/chipidea/ci_hs/ci_hs_hpm.h
@@ -0,0 +1,54 @@
+/*
+ * 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 _CI_HS_HPM_H_
+#define _CI_HS_HPM_H_
+
+#include "ci_hs_type.h"
+#include "hpm_soc.h"
+#include "hpm_interrupt.h"
+#include "hpm_usb_drv.h"
+
+static const ci_hs_controller_t _ci_controller[] =
+{
+ { .reg_base = HPM_USB0_BASE, .irqnum = IRQn_USB0},
+ #ifdef HPM_USB1_BASE
+ { .reg_base = HPM_USB1_BASE, .irqnum = IRQn_USB1},
+ #endif
+};
+
+#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
+
+//------------- DCD -------------//
+#define CI_DCD_INT_ENABLE(_p) intc_m_enable_irq (_ci_controller[_p].irqnum)
+#define CI_DCD_INT_DISABLE(_p) intc_m_disable_irq(_ci_controller[_p].irqnum)
+
+//------------- HCD -------------//
+#define CI_HCD_INT_ENABLE(_p) intc_m_enable_irq (_ci_controller[_p].irqnum)
+#define CI_HCD_INT_DISABLE(_p) intc_m_disable_irq(_ci_controller[_p].irqnum)
+
+
+#endif
diff --git a/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h b/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
index 178eec419..c22aea887 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
@@ -47,10 +47,10 @@ static const ci_hs_controller_t _ci_controller[] =
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
-#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
-#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
+#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
+#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
-#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
-#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
+#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
+#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
#endif
diff --git a/src/portable/chipidea/ci_hs/ci_hs_rw61x.h b/src/portable/chipidea/ci_hs/ci_hs_rw61x.h
new file mode 100644
index 000000000..114fe26c3
--- /dev/null
+++ b/src/portable/chipidea/ci_hs/ci_hs_rw61x.h
@@ -0,0 +1,48 @@
+/*
+ * 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 _CI_HS_RW61X_H_
+#define _CI_HS_RW61X_H_
+
+#include "fsl_device_registers.h"
+
+static const ci_hs_controller_t _ci_controller[] = {
+ {.reg_base = USBOTG_BASE, .irqnum = USB_IRQn}
+};
+
+TU_ATTR_ALWAYS_INLINE static inline ci_hs_regs_t* CI_HS_REG(uint8_t port) {
+ (void) port;
+ return ((ci_hs_regs_t*) _ci_controller[0].reg_base);
+}
+
+#define CI_DCD_INT_ENABLE(_p) do { (void) _p; NVIC_EnableIRQ (_ci_controller[0].irqnum); } while (0)
+#define CI_DCD_INT_DISABLE(_p) do { (void) _p; NVIC_DisableIRQ(_ci_controller[0].irqnum); } while (0)
+
+#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
+#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
+
+
+#endif
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index 4a5e5c91f..b9f6a8a7b 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -34,19 +34,19 @@
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
#include "ci_hs_imxrt.h"
-#if CFG_TUD_MEM_DCACHE_ENABLE
-bool dcd_dcache_clean(void const* addr, uint32_t data_size) {
- return imxrt_dcache_clean(addr, data_size);
-}
+ #if CFG_TUD_MEM_DCACHE_ENABLE
+ bool dcd_dcache_clean(const void *addr, uint32_t data_size) {
+ return imxrt_dcache_clean(addr, data_size);
+ }
-bool dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
- return imxrt_dcache_invalidate(addr, data_size);
-}
+ bool dcd_dcache_invalidate(const void *addr, uint32_t data_size) {
+ return imxrt_dcache_invalidate(addr, data_size);
+ }
-bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
- return imxrt_dcache_clean_invalidate(addr, data_size);
-}
-#endif
+ bool dcd_dcache_clean_invalidate(const void *addr, uint32_t data_size) {
+ return imxrt_dcache_clean_invalidate(addr, data_size);
+ }
+ #endif
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
#include "ci_hs_lpc18_43.h"
@@ -55,6 +55,12 @@ bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
// MCX N9 only port 1 use this controller
#include "ci_hs_mcx.h"
+#elif TU_CHECK_MCU(OPT_MCU_HPM)
+ #include "ci_hs_hpm.h"
+
+#elif TU_CHECK_MCU(OPT_MCU_RW61X)
+ #include "ci_hs_rw61x.h"
+
#else
#error "Unsupported MCUs"
#endif
@@ -76,7 +82,7 @@ enum {
};
#define ENDPTCTRL_TYPE(_type) ((_type) << ENDPTCTRL_TYPE_POS)
- #define ENDPTCTRL_RESET_MASK (ENDPTCTRL_TYPE(TUSB_XFER_BULK) | (ENDPTCTRL_TYPE(TUSB_XFER_BULK) << 16u))
+#define ENDPTCTRL_RESET_MASK (ENDPTCTRL_TYPE(TUSB_XFER_BULK) | (ENDPTCTRL_TYPE(TUSB_XFER_BULK) << 16u))
// USBSTS, USBINTR
enum {
@@ -90,26 +96,28 @@ enum {
};
// Queue Transfer Descriptor
-typedef struct
-{
+typedef struct {
// Word 0: Next QTD Pointer
uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
// Word 1: qTQ Token
- uint32_t : 3 ;
- volatile uint32_t xact_err : 1 ;
- uint32_t : 1 ;
- volatile uint32_t buffer_err : 1 ;
- volatile uint32_t halted : 1 ;
- volatile uint32_t active : 1 ;
- uint32_t : 2 ;
- uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
- uint32_t : 3 ;
- uint32_t int_on_complete : 1 ;
- volatile uint32_t total_bytes : 15 ;
- uint32_t : 1 ;
+ uint32_t : 3;
+ volatile uint32_t xact_err : 1;
+ uint32_t : 1;
+ volatile uint32_t buffer_err : 1;
+ volatile uint32_t halted : 1;
+ volatile uint32_t active : 1;
+ uint32_t : 2;
+ uint32_t iso_mult_override : 2; ///< This field can be used for transmit ISOs to override the MULT field in the dQH.
+ ///< This field must be zero for all packet types that are not transmit-ISO.
+ uint32_t : 3;
+ uint32_t int_on_complete : 1;
+ volatile uint32_t total_bytes : 15;
+ uint32_t : 1;
- // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
+ // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The
+ // lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the
+ // start of a 4K page
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
//--------------------------------------------------------------------+
@@ -117,21 +125,28 @@ typedef struct
// Therefore there are 4 bytes padding that we can use.
//--------------------------------------------------------------------+
uint16_t expected_bytes;
- uint8_t reserved[2];
+ uint8_t reserved[2];
} dcd_qtd_t;
-TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
+TU_VERIFY_STATIC(sizeof(dcd_qtd_t) == 32, "size is not correct");
// Queue Head
-typedef struct
-{
+typedef struct {
// Word 0: Capabilities and Characteristics
- uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
- uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
- uint32_t max_packet_size : 11 ; ///< Endpoint's wMaxPacketSize
- uint32_t : 2 ;
- uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
- uint32_t iso_mult : 2 ; ///<
+ uint32_t : 15; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated
+ ///< by the USB variable length protocol where N is computed using Max_packet_length and the
+ ///< Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 -
+ ///< Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark:
+ ///< Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
+ uint32_t int_on_setup : 1; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is
+ ///< set in response to a setup being received.
+ uint32_t max_packet_size : 11; ///< Endpoint's wMaxPacketSize
+ uint32_t : 2;
+ uint32_t zero_length_termination : 1; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to
+ ///< terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to
+ ///< terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on
+ ///< transfers that are equal in length to a multiple Max_packet_length.
+ uint32_t iso_mult : 2; ///<
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
@@ -146,11 +161,11 @@ typedef struct
// QHD is 64 bytes aligned but occupies only 48 bytes
// Therefore there are 16 bytes padding that we can use.
//--------------------------------------------------------------------+
- tu_fifo_t * ff;
- uint8_t reserved[12];
+ tu_fifo_t *ff;
+ uint8_t reserved[12];
} dcd_qhd_t;
-TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
+TU_VERIFY_STATIC(sizeof(dcd_qhd_t) == 64, "size is not correct");
//--------------------------------------------------------------------+
// Variables
@@ -164,10 +179,9 @@ typedef struct {
// for portability, TinyUSB only queue 1 TD for each Qhd
dcd_qhd_t qhd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64);
dcd_qtd_t qtd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
-}dcd_data_t;
+} dcd_data_t;
-CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(2048)
-static dcd_data_t _dcd_data;
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(2048) static dcd_data_t _dcd_data;
//--------------------------------------------------------------------+
// Prototypes and Helper Functions
@@ -182,16 +196,15 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t ci_ep_count(const ci_hs_regs_t *dcd_
//--------------------------------------------------------------------+
/// follows LPC43xx User Manual 23.10.3
-static void bus_reset(uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+static void bus_reset(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
// The reset value for all endpoint types is the control endpoint. If one endpoint
// direction is enabled and the paired endpoint of opposite direction is disabled, then the
// endpoint type of the unused direction must be changed from the control type to any other
// type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
// for the data PID tracking on the active endpoint.
- uint8_t const ep_count = ci_ep_count(dcd_reg);
+ const uint8_t ep_count = ci_ep_count(dcd_reg);
for (uint8_t i = 1; i < ep_count; i++) {
dcd_reg->ENDPTCTRL[i] = ENDPTCTRL_RESET_MASK;
}
@@ -214,7 +227,7 @@ static void bus_reset(uint8_t rhport)
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
_dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1;
- _dcd_data.qhd[0][0].max_packet_size = _dcd_data.qhd[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+ _dcd_data.qhd[0][0].max_packet_size = _dcd_data.qhd[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
_dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
_dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
@@ -222,92 +235,110 @@ static void bus_reset(uint8_t rhport)
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
}
-bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void)rh_init;
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX);
+ #if TU_CHECK_MCU(OPT_MCU_HPM)
+ usb_phy_init((USB_Type *)dcd_reg, false);
+ #endif
+
// Reset controller
dcd_reg->USBCMD |= USBCMD_RESET;
- while( dcd_reg->USBCMD & USBCMD_RESET ) {}
+ while (dcd_reg->USBCMD & USBCMD_RESET) {}
// Set mode to device, must be set immediately after reset
uint32_t usbmode = dcd_reg->USBMODE & ~USBMOD_CM_MASK;
usbmode |= USBMODE_CM_DEVICE;
dcd_reg->USBMODE = usbmode;
-#ifdef CFG_TUD_CI_HS_VBUS_CHARGE
+ #ifdef CFG_TUD_CI_HS_VBUS_CHARGE
dcd_reg->OTGSC = OTGSC_VBUS_CHARGE | OTGSC_OTG_TERMINATION;
-#else
+ #else
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
-#endif
+ #endif
-#if !TUD_OPT_HIGH_SPEED
- dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
-#endif
+ #if !TUD_OPT_HIGH_SPEED
+ dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ #endif
+
+ #if TU_CHECK_MCU(OPT_MCU_HPM)
+ dcd_reg->PORTSC1 &= ~USB_PORTSC1_STS_MASK;
+ #endif
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
- dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
- dcd_reg->USBSTS = dcd_reg->USBSTS;
- dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
+ dcd_reg->ENDPTLISTADDR = (uint32_t)_dcd_data.qhd; // Endpoint List Address has to be 2K alignment
+ dcd_reg->USBSTS = dcd_reg->USBSTS;
+ dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
uint32_t usbcmd = dcd_reg->USBCMD;
usbcmd &= ~USBCMD_INTR_THRESHOLD_MASK; // Interrupt Threshold Interval = 0
- usbcmd |= USBCMD_RUN_STOP; // run
+ usbcmd |= USBCMD_RUN_STOP; // run
dcd_reg->USBCMD = usbcmd;
return true;
}
-void dcd_int_enable(uint8_t rhport)
-{
+bool dcd_deinit(uint8_t rhport) {
+ ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+
+ // disable all interrupt
+ dcd_reg->USBINTR = 0;
+
+ // unattach from bus
+ dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
+
+ // flush all endpoints
+ while (dcd_reg->ENDPTPRIME) {}
+ dcd_reg->ENDPTFLUSH = 0xFFFFFFFF;
+ while (dcd_reg->ENDPTFLUSH) {}
+
+ return true;
+}
+
+void dcd_int_enable(uint8_t rhport) {
CI_DCD_INT_ENABLE(rhport);
}
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
CI_DCD_INT_DISABLE(rhport);
}
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
// Response with status first before changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0, false);
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
- dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+void dcd_remote_wakeup(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME;
}
-void dcd_connect(uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+void dcd_connect(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
dcd_reg->USBCMD |= USBCMD_RUN_STOP;
}
-void dcd_disconnect(uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+void dcd_disconnect(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
if (en) {
- dcd_reg->USBINTR |= INTR_SOF;
+ dcd_reg->USBINTR |= INTR_SOF;
} else {
- dcd_reg->USBINTR &= ~INTR_SOF;
+ dcd_reg->USBINTR &= ~INTR_SOF;
}
}
@@ -374,26 +405,24 @@ TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_clear(volatile uint32_t *epctrl
ep_ctrl_mask(epctrl, dir, ~mask, 0);
}
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
// flush to abort any primed buffer
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
}
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
// data toggle also need to be reset
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
- dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 );
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << (dir ? 16 : 0);
dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << (dir ? 16 : 0));
}
@@ -471,7 +500,7 @@ void dcd_edpt_close_all(uint8_t rhport) {
ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
// Disable all non-control endpoints
- uint8_t const ep_count = ci_ep_count(dcd_reg);
+ const uint8_t ep_count = ci_ep_count(dcd_reg);
for (uint8_t epnum = 1; epnum < ep_count; epnum++) {
_dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
_dcd_data.qhd[epnum][TUSB_DIR_IN].qtd_overlay.halted = 1;
@@ -481,37 +510,34 @@ void dcd_edpt_close_all(uint8_t rhport) {
}
}
-static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
- dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
+static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ dcd_qtd_t *p_qtd = &_dcd_data.qtd[epnum][dir];
- p_qhd->qtd_overlay.halted = false; // clear any previous error
- p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
+ p_qhd->qtd_overlay.halted = false; // clear any previous error
+ p_qhd->qtd_overlay.next = (uint32_t)p_qtd; // link qtd to qhd
// flush cache
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
- if ( epnum == 0 )
- {
+ if (epnum == 0) {
// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
// wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
- while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
+ while (dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
}
// start transfer
dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0));
}
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr)
-{
- (void) is_isr;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
+ (void)is_isr;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
- dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ dcd_qtd_t *p_qtd = &_dcd_data.qtd[epnum][dir];
// Prepare qtd
qtd_init(p_qtd, buffer, total_bytes);
@@ -523,58 +549,47 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
return true;
}
-#if !CFG_TUD_MEM_DCACHE_ENABLE
+ #if !CFG_TUD_MEM_DCACHE_ENABLE
// fifo has to be aligned to 4k boundary
// It's incompatible with dcache enabled transfer, since neither address nor size is aligned to cache line
-bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes, bool is_isr)
-{
- (void) is_isr;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) {
+ (void)is_isr;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
- dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ dcd_qtd_t *p_qtd = &_dcd_data.qtd[epnum][dir];
tu_fifo_buffer_info_t fifo_info;
- if (dir)
- {
+ if (dir) {
tu_fifo_get_read_info(ff, &fifo_info);
- } else
- {
+ } else {
tu_fifo_get_write_info(ff, &fifo_info);
}
- if ( fifo_info.linear.len >= total_bytes )
- {
+ if (fifo_info.linear.len >= total_bytes) {
// Linear length is enough for this transfer
qtd_init(p_qtd, fifo_info.linear.ptr, total_bytes);
- }
- else
- {
+ } else {
// linear part is not enough
// prepare TD up to linear length
qtd_init(p_qtd, fifo_info.linear.ptr, fifo_info.linear.len);
- if ( !tu_offset4k((uint32_t) fifo_info.wrapped.ptr) && !tu_offset4k(tu_fifo_depth(ff)) )
- {
+ if (!tu_offset4k((uint32_t)fifo_info.wrapped.ptr) && !tu_offset4k(tu_fifo_depth(ff))) {
// If buffer is aligned to 4K & buffer size is multiple of 4K
// We can make use of buffer page array to also combine the linear + wrapped length
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
- for(uint8_t i = 1, page = 0; i < 5; i++)
- {
+ for (uint8_t i = 1, page = 0; i < 5; i++) {
// pick up buffer array where linear ends
- if (p_qtd->buffer[i] == 0)
- {
- p_qtd->buffer[i] = (uint32_t) fifo_info.wrapped.ptr + 4096 * page;
+ if (p_qtd->buffer[i] == 0) {
+ p_qtd->buffer[i] = (uint32_t)fifo_info.wrapped.ptr + 4096 * page;
page++;
}
}
- }
- else
- {
+ } else {
// TODO we may need to carry the wrapped length after the linear part complete
// for now only transfer up to linear part
}
@@ -586,36 +601,32 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
return true;
}
-#endif
+ #endif
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
-static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir)
-{
- dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
+static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir) {
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ dcd_qtd_t *p_qtd = &_dcd_data.qtd[epnum][dir];
- uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
- ( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
+ uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED
+ : (p_qtd->xact_err || p_qtd->buffer_err) ? XFER_RESULT_FAILED
+ : XFER_RESULT_SUCCESS;
- if ( result != XFER_RESULT_SUCCESS )
- {
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ if (result != XFER_RESULT_SUCCESS) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
// flush to abort error buffer
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
}
- uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
+ const uint16_t xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
- if (p_qhd->ff)
- {
- if (dir == TUSB_DIR_IN)
- {
+ if (p_qhd->ff) {
+ if (dir == TUSB_DIR_IN) {
tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes);
- } else
- {
+ } else {
tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
}
}
@@ -624,82 +635,74 @@ static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir
dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
}
-void dcd_int_handler(uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+void dcd_int_handler(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
- uint32_t const int_enable = dcd_reg->USBINTR;
- uint32_t const int_status = dcd_reg->USBSTS & int_enable;
- dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
+ const uint32_t int_enable = dcd_reg->USBINTR;
+ const uint32_t int_status = dcd_reg->USBSTS & int_enable;
+ dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
// disabled interrupt sources
- if (int_status == 0) return;
+ if (int_status == 0) {
+ return;
+ }
// Set if the port controller enters the full or high-speed operational state.
// either from Bus Reset or Suspended state
- if (int_status & INTR_PORT_CHANGE)
- {
- // TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
+ if (int_status & INTR_PORT_CHANGE) {
+ // TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
- // Reset interrupt is not enabled, we manually check if Port Change is due
- // to connection / disconnection
- if ( dcd_reg->USBSTS & INTR_RESET )
- {
- dcd_reg->USBSTS = INTR_RESET;
+ // Reset interrupt is not enabled, we manually check if Port Change is due
+ // to connection / disconnection
+ if (dcd_reg->USBSTS & INTR_RESET) {
+ dcd_reg->USBSTS = INTR_RESET;
- if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS)
- {
- uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
- bus_reset(rhport);
- dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
- }else
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
- }
- }
- else
- {
- // Triggered by resuming from suspended state
- if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) )
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
- }
- }
- }
+ if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) {
+ const uint32_t speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
+ bus_reset(rhport);
+ dcd_event_bus_reset(rhport, (tusb_speed_t)speed, true);
+ } else {
+ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+ }
+ } else {
+ // Triggered by resuming from suspended state
+ if (!(dcd_reg->PORTSC1 & PORTSC1_SUSPEND)) {
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+ }
+ }
+ }
- if (int_status & INTR_SUSPEND)
- {
+ if (int_status & INTR_SUSPEND) {
// TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1);
- if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
- {
+ if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND) {
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
// Skip suspend event if we are not addressed
- if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
- {
+ if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) {
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
}
}
- if (int_status & INTR_USB)
- {
+ if (int_status & INTR_USB) {
// Make sure we read the latest version of _dcd_data.
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
- uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
- dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
+ const uint32_t edpt_complete = dcd_reg->ENDPTCOMPLETE;
+ dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
// nothing to do, we will submit xfer as error to usbd
// if (int_status & INTR_ERROR) { }
- if ( edpt_complete )
- {
- for(uint8_t epnum = 0; epnum < TUP_DCD_ENDPOINT_MAX; epnum++)
- {
- if ( tu_bit_test(edpt_complete, epnum) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
- if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
+ if (edpt_complete) {
+ for (uint8_t epnum = 0; epnum < TUP_DCD_ENDPOINT_MAX; epnum++) {
+ if (tu_bit_test(edpt_complete, epnum)) {
+ process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
+ }
+ if (tu_bit_test(edpt_complete, epnum + 16)) {
+ process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
+ }
}
}
@@ -709,12 +712,11 @@ void dcd_int_handler(uint8_t rhport)
// in the same frame and we should handle previous status first.
if (dcd_reg->ENDPTSETUPSTAT) {
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
- dcd_event_setup_received(rhport, (uint8_t *) (uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
+ dcd_event_setup_received(rhport, (uint8_t *)(uintptr_t)&_dcd_data.qhd[0][0].setup_request, true);
}
}
- if (int_status & INTR_SOF)
- {
+ if (int_status & INTR_SOF) {
const uint32_t frame = dcd_reg->FRINDEX;
dcd_event_sof(rhport, frame, true);
}
diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
index b5324a754..c0d14fe57 100644
--- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
@@ -41,28 +41,36 @@
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
-#include "ci_hs_imxrt.h"
+ #include "ci_hs_imxrt.h"
-#if CFG_TUH_MEM_DCACHE_ENABLE
-bool hcd_dcache_clean(void const* addr, uint32_t data_size) {
+ #if CFG_TUH_MEM_DCACHE_ENABLE
+bool hcd_dcache_clean(const void *addr, uint32_t data_size) {
return imxrt_dcache_clean(addr, data_size);
}
-bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) {
+bool hcd_dcache_invalidate(const void *addr, uint32_t data_size) {
return imxrt_dcache_invalidate(addr, data_size);
}
-bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+bool hcd_dcache_clean_invalidate(const void *addr, uint32_t data_size) {
return imxrt_dcache_clean_invalidate(addr, data_size);
}
-#endif
+ #endif
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
-#include "ci_hs_lpc18_43.h"
+ #include "ci_hs_lpc18_43.h"
+
+#elif TU_CHECK_MCU(OPT_MCU_HPM)
+
+ #include "ci_hs_hpm.h"
+
+#elif TU_CHECK_MCU(OPT_MCU_RW61X)
+
+ #include "ci_hs_rw61x.h"
#else
-#error "Unsupported MCUs"
+ #error "Unsupported MCUs"
#endif
//--------------------------------------------------------------------+
@@ -73,27 +81,35 @@ bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
// Controller API
//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void)rh_init;
ci_hs_regs_t *hcd_reg = CI_HS_REG(rhport);
+ #if CFG_TUSB_MCU == OPT_MCU_HPM
+ usb_phy_init((USB_Type *)hcd_reg, true);
+ #endif
+
// Reset controller
hcd_reg->USBCMD |= USBCMD_RESET;
- while ( hcd_reg->USBCMD & USBCMD_RESET ) {}
+ while (hcd_reg->USBCMD & USBCMD_RESET) {}
- // Set mode to device, must be set immediately after reset
-#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
+ // Set mode to host, must be set immediately after reset
+ #if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
// LPC18XX/43XX need to set VBUS Power Select to HIGH
- // RHPORT1 is fullspeed only (need external PHY for Highspeed)
hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
- if (rhport == 1) {
- hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
- }
-#else
+ #else
hcd_reg->USBMODE = USBMODE_CM_HOST;
-#endif
+ #endif
+
+ #if !TUH_OPT_HIGH_SPEED
+ hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ #endif
+
+ return ehci_init(rhport, (uint32_t)&hcd_reg->CAPLENGTH, (uint32_t)&hcd_reg->USBCMD);
+}
- return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
+bool hcd_deinit(uint8_t rhport) {
+ return ehci_deinit(rhport);
}
void hcd_int_enable(uint8_t rhport) {
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index c33c970e4..03c3b91fd 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -40,10 +40,14 @@
#include "ehci.h"
// NXP specific fixes
-#if TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX, OPT_MCU_LPC55, OPT_MCU_MCXN9)
+#if TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX, OPT_MCU_LPC55, OPT_MCU_MCXN9, OPT_MCU_RW61X)
#include "fsl_device_registers.h"
#endif
+#if TU_CHECK_MCU(OPT_MCU_HPM)
+#include "ci_hs_hpm.h"
+#endif
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -237,6 +241,14 @@ void hcd_port_reset(uint8_t rhport) {
// mask out Write-1-to-Clear bits
uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
+#if TU_CHECK_MCU(OPT_MCU_HPM)
+ if (usb_phy_get_line_state((USB_Type *)CI_HS_REG(rhport)) == usb_line_state2) {
+ portsc |= USB_PORTSC1_STS_MASK;
+ } else {
+ portsc &= ~USB_PORTSC1_STS_MASK;
+ }
+#endif
+
// EHCI Table 2-16 PortSC
// when software writes Port Reset bit to a one, it must also write a zero to the Port Enable bit.
portsc &= ~(EHCI_PORTSC_MASK_PORT_EANBLED);
@@ -399,17 +411,22 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
return true;
}
-#if 0
-static void ehci_stop(uint8_t rhport) {
+bool ehci_deinit(uint8_t rhport) {
(void) rhport;
ehci_registers_t* regs = ehci_data.regs;
+
+ // Disable all the interrupt
+ regs->inten = 0;
+
+ // Disable schedules
regs->command_bm.run_stop = 0;
// USB Spec: controller has to stop within 16 uframe = 2 frames
while( regs->status_bm.hc_halted == 0 ) {}
+
+ return true;
}
-#endif
//--------------------------------------------------------------------+
// Endpoint API
diff --git a/src/portable/ehci/ehci_api.h b/src/portable/ehci/ehci_api.h
index 79fbe702a..e9018639f 100644
--- a/src/portable/ehci/ehci_api.h
+++ b/src/portable/ehci/ehci_api.h
@@ -38,6 +38,9 @@
// Initialize EHCI driver
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg);
+// De-initialize EHCI driver
+bool ehci_deinit(uint8_t rhport);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c
index ad20d64bd..d329285e9 100644
--- a/src/portable/mentor/musb/dcd_musb.c
+++ b/src/portable/mentor/musb/dcd_musb.c
@@ -32,12 +32,6 @@
#define MUSB_DEBUG 2
#define MUSB_REGS(rhport) ((musb_regs_t*) MUSB_BASES[rhport])
-#if __GNUC__ > 8 && defined(__ARM_FEATURE_UNALIGNED)
-/* GCC warns that an address may be unaligned, even though
- * the target CPU has the capability for unaligned memory access. */
-_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
-#endif
-
#include "musb_type.h"
#include "device/dcd.h"
@@ -73,7 +67,10 @@ typedef struct TU_ATTR_PACKED
typedef struct
{
- tusb_control_request_t setup_packet;
+ union {
+ tusb_control_request_t setup_packet;
+ uint32_t setup_buffer[2];
+ };
uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
int8_t status_out;
pipe_state_t pipe0;
@@ -170,76 +167,12 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigne
}
}
-static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
-{
- volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
- uintptr_t addr = (uintptr_t)buf;
- while (len >= 4) {
- reg->u32 = *(uint32_t const *)addr;
- addr += 4;
- len -= 4;
- }
- if (len >= 2) {
- reg->u16 = *(uint16_t const *)addr;
- addr += 2;
- len -= 2;
- }
- if (len) {
- reg->u8 = *(uint8_t const *)addr;
- }
-}
-
-static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
-{
- volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
- uintptr_t addr = (uintptr_t)buf;
- while (len >= 4) {
- *(uint32_t *)addr = reg->u32;
- addr += 4;
- len -= 4;
- }
- if (len >= 2) {
- *(uint16_t *)addr = reg->u16;
- addr += 2;
- len -= 2;
- }
- if (len) {
- *(uint8_t *)addr = reg->u8;
- }
-}
-
-static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigned len, unsigned dir)
-{
- static const struct {
- void (*tu_fifo_get_info)(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
- void (*tu_fifo_advance)(tu_fifo_t *f, uint16_t n);
- void (*pipe_read_write)(void *buf, volatile void *fifo, unsigned len);
- } ops[] = {
- /* OUT */ {tu_fifo_get_write_info,tu_fifo_advance_write_pointer,pipe_read_packet},
- /* IN */ {tu_fifo_get_read_info, tu_fifo_advance_read_pointer, pipe_write_packet},
- };
- tu_fifo_buffer_info_t info;
- ops[dir].tu_fifo_get_info(f, &info);
- unsigned total_len = len;
- len = TU_MIN(total_len, info.linear.len);
- ops[dir].pipe_read_write(info.linear.ptr, fifo, len);
- unsigned rem = total_len - len;
- if (rem) {
- len = TU_MIN(rem, info.wrapped.len);
- ops[dir].pipe_read_write(info.wrapped.ptr, fifo, len);
- rem -= len;
- }
- ops[dir].tu_fifo_advance(f, total_len - rem);
-}
-
static void process_setup_packet(uint8_t rhport) {
musb_regs_t* musb_regs = MUSB_REGS(rhport);
// Read setup packet
- uint32_t *p = (void*)&_dcd.setup_packet;
- volatile uint32_t *fifo_ptr = &musb_regs->fifo[0];
- p[0] = *fifo_ptr;
- p[1] = *fifo_ptr;
+ _dcd.setup_buffer[0] = musb_regs->fifo[0];
+ _dcd.setup_buffer[1] = musb_regs->fifo[0];
_dcd.pipe0.buf = NULL;
_dcd.pipe0.length = 0;
@@ -256,14 +189,13 @@ static void process_setup_packet(uint8_t rhport) {
}
}
-static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr)
-{
+static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) {
unsigned epnum = tu_edpt_number(ep_addr);
unsigned epnum_minus1 = epnum - 1;
pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
const unsigned rem = pipe->remaining;
- if (!rem) {
+ if (rem == 0 && pipe->length > 0) {
pipe->buf = NULL;
return true;
}
@@ -277,10 +209,10 @@ static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr)
// TU_LOG1(" %p mps %d len %d rem %d\r\n", buf, mps, len, rem);
if (len) {
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff(buf, fifo_ptr, len, TUSB_DIR_IN);
+ tu_hwfifo_write_from_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL);
} else {
- pipe_write_packet(buf, fifo_ptr, len);
- pipe->buf = buf + len;
+ tu_hwfifo_write(fifo_ptr, buf, len, NULL);
+ pipe->buf = (uint8_t*)buf + len;
}
pipe->remaining = rem - len;
}
@@ -308,10 +240,10 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr)
volatile void *fifo_ptr = &musb_regs->fifo[epnum];
if (len) {
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff(buf, fifo_ptr, len, TUSB_DIR_OUT);
+ tu_hwfifo_read_to_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL);
} else {
- pipe_read_packet(buf, fifo_ptr, len);
- pipe->buf = buf + len;
+ tu_hwfifo_read(fifo_ptr, buf, len, NULL);
+ pipe->buf = (uint8_t*)buf + len;
}
pipe->remaining = rem - len;
}
@@ -378,7 +310,7 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
const unsigned len = TU_MIN(TU_MIN(rem, 64), total_bytes);
volatile void *fifo_ptr = &musb_regs->fifo[0];
if (dir_in) {
- pipe_write_packet(buffer, fifo_ptr, len);
+ tu_hwfifo_write(fifo_ptr, buffer, len, NULL);
_dcd.pipe0.buf = buffer + len;
_dcd.pipe0.length = len;
@@ -458,7 +390,7 @@ static void process_ep0(uint8_t rhport)
const unsigned rem = _dcd.pipe0.remaining;
const unsigned len = TU_MIN(TU_MIN(rem, 64), vld);
volatile void *fifo_ptr = &musb_regs->fifo[0];
- pipe_read_packet(_dcd.pipe0.buf, fifo_ptr, len);
+ tu_hwfifo_read(fifo_ptr, _dcd.pipe0.buf, len, NULL);
_dcd.pipe0.remaining = rem - len;
_dcd.remaining_ctrl -= len;
diff --git a/src/portable/mentor/musb/musb_ti.h b/src/portable/mentor/musb/musb_ti.h
index d17e836ee..68e89d77d 100644
--- a/src/portable/mentor/musb/musb_ti.h
+++ b/src/portable/mentor/musb/musb_ti.h
@@ -35,7 +35,10 @@
#include "TM4C123.h"
#define FIFO0_WORD FIFO0
#define FIFO1_WORD FIFO1
-//#elif CFG_TUSB_MCU == OPT_MCU_TM4C129
+#elif CFG_TUSB_MCU == OPT_MCU_TM4C129
+ #include "TM4C129.h"
+ #define FIFO0_WORD FIFOA
+ #define FIFO1_WORD FIFOB
#elif CFG_TUSB_MCU == OPT_MCU_MSP432E4
#include "msp.h"
#else
diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h
index 4e448c0ed..b2f6492fa 100644
--- a/src/portable/mentor/musb/musb_type.h
+++ b/src/portable/mentor/musb/musb_type.h
@@ -147,7 +147,7 @@ typedef struct TU_ATTR_PACKED {
TU_VERIFY_STATIC(sizeof(musb_ep_csr_t) == 16, "size is not correct");
-typedef struct TU_ATTR_PACKED {
+typedef struct {
//------------- Common -------------//
__IO uint8_t faddr; // 0x00: FADDR
union {
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index 1faac2aa8..4115eecc5 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -437,7 +437,7 @@ void dcd_int_handler(uint8_t rhport)
// write to EP fifo
#if 0 // TODO support dcd_edpt_xfer_fifo
if (xfer->ff) {
- tu_fifo_read_n_access_mode(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len, TU_FIFO_FIXED_ADDR_RW32);
+ tu_fifo_read_n_access_mode(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len, true);
}
else
#endif
@@ -471,7 +471,7 @@ void dcd_int_handler(uint8_t rhport)
// Read from EP fifo
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff) {
- tu_fifo_write_n_access_mode(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len, TU_FIFO_FIXED_ADDR_RW32);
+ tu_fifo_write_n_access_mode(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len, true);
}
else
#endif
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index 91b876718..ca17d6251 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -194,7 +194,7 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transferred as 32-bit */
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff) {
- tu_fifo_read_n_access_mode(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now, TU_FIFO_FIXED_ADDR_RW32);
+ tu_fifo_read_n_access_mode(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now, true);
}
else
#endif
@@ -696,7 +696,7 @@ void dcd_int_handler(uint8_t rhport)
/* copy the data from the PC to the previously provided buffer */
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff) {
- tu_fifo_write_n_access_mode(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far), TU_FIFO_FIXED_ADDR_RW32);
+ tu_fifo_write_n_access_mode(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far), true);
}
else
#endif
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 15852f29f..240e6c727 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -39,122 +39,103 @@
#include "device/dcd.h"
// Current implementation force vbus detection as always present, causing device think it is always plugged into host.
-// Therefore it cannot detect disconnect event, mistaken it as suspend.
+// Therefore, it cannot detect disconnect event, mistaken it as suspend.
// Note: won't work if change to 0 (for now)
-#define FORCE_VBUS_DETECT 1
+ #define FORCE_VBUS_DETECT 1
+
+ #define USB_INTS_ERROR_BITS \
+ (USB_INTS_ERROR_DATA_SEQ_BITS | USB_INTS_ERROR_BIT_STUFF_BITS | USB_INTS_ERROR_CRC_BITS | \
+ USB_INTS_ERROR_RX_OVERFLOW_BITS | USB_INTS_ERROR_RX_TIMEOUT_BITS)
/*------------------------------------------------------------------*/
/* Low level controller
*------------------------------------------------------------------*/
-// Init these in dcd_init
-static uint8_t* next_buffer_ptr;
+// HW buffer pointer from USB buffer space (max 3840 bytes)
+static uint8_t *hw_buffer_ptr;
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
-// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
+// SOF may be used by remote wakeup as RESUME, this indicates whether SOF is actually used by usbd
static bool _sof_enable = false;
-TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
- return &hw_endpoints[num][dir];
-}
-
-TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) {
- uint8_t num = tu_edpt_number(ep_addr);
- tusb_dir_t dir = tu_edpt_dir(ep_addr);
- return hw_endpoint_get_by_num(num, dir);
-}
-
-// Allocate from the USB buffer space (max 3840 bytes)
-static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) {
- // round up size to multiple of 64
- size = tu_round_up(ep->wMaxPacketSize, 64);
-
- // double buffered Bulk endpoint
- if (ep->transfer_type == TUSB_XFER_BULK) {
- size *= 2u;
- }
-
- // assign buffer
- ep->hw_data_buf = next_buffer_ptr;
- next_buffer_ptr += size;
-
- hard_assert(next_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data));
- pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf);
+TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get(uint8_t epnum, tusb_dir_t dir) {
+ return &hw_endpoints[epnum][dir];
}
-// Enable endpoint
-TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) {
- uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf);
- *ep->endpoint_control = reg;
+TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8_t ep_addr) {
+ const uint8_t num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ return hw_endpoint_get(num, dir);
}
// main processing for dcd_edpt_iso_activate
-static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
-
- const uint8_t num = tu_edpt_number(ep_addr);
- const tusb_dir_t dir = tu_edpt_dir(ep_addr);
-
- ep->ep_addr = ep_addr;
-
- // For device, IN is a tx transfer and OUT is an rx transfer
- ep->rx = (dir == TUSB_DIR_OUT);
-
- ep->next_pid = 0u;
+static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
+ ep->ep_addr = ep_addr;
+ ep->next_pid = 0u;
ep->wMaxPacketSize = wMaxPacketSize;
- ep->transfer_type = transfer_type;
-
- // Every endpoint has a buffer control register in dpram
- if (dir == TUSB_DIR_IN) {
- ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
- } else {
- ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
- }
// Clear existing buffer control state
- *ep->buffer_control = 0;
-
- if (num == 0) {
- // EP0 has no endpoint control register because the buffer offsets are fixed
- ep->endpoint_control = NULL;
+ io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ *buf_ctrl_reg = 0;
+ // allocated hw buffer
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ if (epnum == 0) {
// Buffer offset is fixed (also double buffered)
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
} else {
- // Set the endpoint control register (starts at EP1, hence num-1)
- if (dir == TUSB_DIR_IN) {
- ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
- } else {
- ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
+ // round up size to multiple of 64
+ uint16_t size = (uint16_t)tu_round_up(wMaxPacketSize, 64);
+
+ // double buffered Bulk endpoint
+ if (transfer_type == TUSB_XFER_BULK) {
+ size *= 2u;
+
+ #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ ep->e15_bulk_in = true;
+ }
+ #endif
}
+
+ // assign buffer
+ ep->hw_data_buf = hw_buffer_ptr;
+ hw_buffer_ptr += size;
+
+ hard_assert(hw_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data));
+ pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf);
}
}
-// Init, allocate buffer and enable endpoint
-static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
- hw_endpoint_init(ep_addr, wMaxPacketSize, transfer_type);
- const uint8_t num = tu_edpt_number(ep_addr);
- if (num != 0) {
- // EP0 is already enabled
- hw_endpoint_alloc(ep, ep->wMaxPacketSize);
- hw_endpoint_enable(ep);
+static void hw_endpoint_enable(hw_endpoint_t *ep, uint8_t transfer_type) {
+ io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep);
+ // Set endpoint control register to enable (EP0 has no endpoint control register)
+ if (ctrl_reg != NULL) {
+ const uint32_t ctrl_value =
+ EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf);
+ *ctrl_reg = ctrl_value;
}
}
-static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
- hw_endpoint_xfer_start(ep, buffer, total_bytes);
+// Init and enable endpoint
+static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
+
+ hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type);
+ hw_endpoint_enable(ep, transfer_type);
}
static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
// Abort any pending transfer
+ const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr);
+ const uint8_t epnum = tu_edpt_number(ep->ep_addr);
+ const uint32_t abort_mask = TU_BIT((epnum << 1) | (dir ? 0 : 1));
+
// Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
// Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
- const uint8_t dir = tu_edpt_dir(ep->ep_addr);
- const uint8_t epnum = tu_edpt_number(ep->ep_addr);
- const uint32_t abort_mask = TU_BIT((epnum << 1) | (dir ? 0 : 1));
if (rp2040_chip_version() >= 2) {
usb_hw_set->abort = abort_mask;
while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
@@ -165,7 +146,8 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
buf_ctrl |= USB_BUF_CTRL_DATA1_PID;
}
- _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
+ io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl);
hw_endpoint_reset_transfer(ep);
if (rp2040_chip_version() >= 2) {
@@ -174,7 +156,7 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
}
}
-static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
+static void __tusb_irq_path_func(handle_hw_buff_status)(void) {
uint32_t remaining_buffers = usb_hw->buf_status;
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
uint bit = 1u;
@@ -184,12 +166,13 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
usb_hw_clear->buf_status = bit;
// IN transfer for even i, OUT transfer for odd i
- struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
+ const uint8_t epnum = i >> 1u;
+ const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN;
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
- // Continue xfer
- bool done = hw_endpoint_xfer_continue(ep);
+ const bool done = hw_endpoint_xfer_continue(ep);
if (done) {
- // Notify
+ // Notify usbd
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);
@@ -204,7 +187,7 @@ TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
// If we have finished this transfer on EP0 set pid back to 1 for next
// setup transfer. Also clear a stall in case
for (uint8_t dir = 0; dir < 2; dir++) {
- struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
+ struct hw_endpoint *ep = hw_endpoint_get(0, dir);
ep->next_pid = 1u;
if (ep->active) {
hw_endpoint_abort_xfer(ep); // Abort any pending transfer per USB specs
@@ -223,11 +206,11 @@ static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
// reclaim buffer space
- next_buffer_ptr = &usb_dpram->epx_data[0];
+ hw_buffer_ptr = &usb_dpram->epx_data[0];
}
static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
- uint32_t const status = usb_hw->ints;
+ const uint32_t status = usb_hw->ints;
uint32_t handled = 0;
if (status & USB_INTF_DEV_SOF_BITS) {
@@ -240,27 +223,26 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
e15_last_sof = time_us_32();
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
- struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
+ struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN);
// Active Bulk IN endpoint requires SOF
- if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
+ if (ep->e15_bulk_in && ep->active) {
keep_sof_alive = true;
hw_endpoint_lock_update(ep, 1);
-
- // Deferred enable?
if (ep->pending) {
ep->pending = 0;
hw_endpoint_start_next_buffer(ep);
}
-
hw_endpoint_lock_update(ep, -1);
}
}
#endif
// disable SOF interrupt if it is used for RESUME in remote wakeup
- if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
+ if (!keep_sof_alive && !_sof_enable) {
+ usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
+ }
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
}
@@ -269,7 +251,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
// before closing the EP, the events will be delivered in same order.
if (status & USB_INTS_BUFF_STATUS_BITS) {
handled |= USB_INTS_BUFF_STATUS_BITS;
- hw_handle_buff_status();
+ handle_hw_buff_status();
}
if (status & USB_INTS_SETUP_REQ_BITS) {
@@ -287,15 +269,12 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
#if FORCE_VBUS_DETECT == 0
// Since we force VBUS detect On, device will always think it is connected and
// couldn't distinguish between disconnect and suspend
- if (status & USB_INTS_DEV_CONN_DIS_BITS)
- {
+ if (status & USB_INTS_DEV_CONN_DIS_BITS) {
handled |= USB_INTS_DEV_CONN_DIS_BITS;
- if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
- {
+ if (usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS) {
// Connected: nothing to do
- }else
- {
+ } else {
// Disconnected
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
}
@@ -315,10 +294,12 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
-#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+ #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
// Only run enumeration workaround if pull up is enabled
- if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix();
-#endif
+ if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) {
+ rp2040_usb_device_enumeration_fix();
+ }
+ #endif
}
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
@@ -346,13 +327,6 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
}
}
-#define USB_INTS_ERROR_BITS ( \
- USB_INTS_ERROR_DATA_SEQ_BITS | \
- USB_INTS_ERROR_BIT_STUFF_BITS | \
- USB_INTS_ERROR_CRC_BITS | \
- USB_INTS_ERROR_RX_OVERFLOW_BITS | \
- USB_INTS_ERROR_RX_TIMEOUT_BITS)
-
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
@@ -369,9 +343,9 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
// Reset hardware to default state
- rp2040_usb_init();
+ rp2usb_init();
-#if FORCE_VBUS_DETECT
+ #if FORCE_VBUS_DETECT
// Force VBUS detect so the device thinks it is plugged into a host
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
#endif
@@ -425,12 +399,11 @@ void dcd_int_disable(__unused uint8_t rhport) {
irq_set_enabled(USBCTRL_IRQ, false);
}
-void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
- assert(rhport == 0);
-
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void)dev_addr;
// Can't set device address in hardware until status xfer has complete
// Send 0len complete response on EP0 IN
- hw_endpoint_xfer(0x80, NULL, 0);
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0, false);
}
void dcd_remote_wakeup(__unused uint8_t rhport) {
@@ -475,7 +448,6 @@ void dcd_sof_enable(uint8_t rhport, bool en) {
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
-
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
(void) rhport;
@@ -489,7 +461,6 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
(void) rhport;
const uint8_t xfer_type = desc_edpt->bmAttributes.xfer;
- TU_VERIFY(xfer_type != TUSB_XFER_ISOCHRONOUS);
hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type);
return true;
}
@@ -497,56 +468,65 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
// New API: Allocate packet buffer used by ISO endpoints
// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
- (void) rhport;
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
- hw_endpoint_init(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
- hw_endpoint_alloc(ep, largest_packet_size);
+ (void)rhport;
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_init(ep, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
return true;
}
// New API: Configure and enable an ISO endpoint according to descriptor
-bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
- (void) rhport;
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_desc->bEndpointAddress);
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ (void)rhport;
+ const uint8_t epnum = tu_edpt_number(ep_desc->bEndpointAddress);
+ const tusb_dir_t dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+ struct hw_endpoint *ep = hw_endpoint_get(epnum, dir);
TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and allocated previously
if (ep->active) {
hw_endpoint_abort_xfer(ep); // abort any pending transfer
}
-
ep->wMaxPacketSize = ep_desc->wMaxPacketSize;
- hw_endpoint_enable(ep);
+
+ hw_endpoint_enable(ep, TUSB_XFER_ISOCHRONOUS);
return true;
}
void dcd_edpt_close_all(uint8_t rhport) {
(void) rhport;
-
// may need to use EP Abort
reset_non_control_endpoints();
}
-bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) {
- (void) is_isr;
- assert(rhport == 0);
- hw_endpoint_xfer(ep_addr, buffer, total_bytes);
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
+ (void)rhport;
+ (void)is_isr;
+ hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_xfer_start(ep, buffer, NULL, total_bytes);
+ return true;
+}
+
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) {
+ (void)rhport;
+ (void)is_isr;
+ hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_xfer_start(ep, NULL, ff, total_bytes);
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport;
+ (void)rhport;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
- if (tu_edpt_number(ep_addr) == 0) {
+ if (epnum == 0) {
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
- usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS
- : USB_EP_STALL_ARM_EP0_OUT_BITS;
+ usb_hw_set->ep_stall_arm = (dir == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
-
- // stall and clear current pending buffer
- // may need to use EP_ABORT
- _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
+ // stall and clear current pending buffer, may need to use EP_ABORT
+ io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ hwbuf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL);
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
@@ -557,7 +537,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
// clear stall also reset toggle to DATA0, ready for next transfer
ep->next_pid = 0;
- _hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
+ io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ hwbuf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL);
}
}
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index cd8c905d5..06c0ce340 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -62,34 +62,33 @@ enum {
USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS
};
-static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
-{
+static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) {
uint8_t num = tu_edpt_number(ep_addr);
- if ( num == 0 ) return &epx;
+ if (num == 0) {
+ return &epx;
+ }
- for ( uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ )
- {
+ for (uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) {
struct hw_endpoint *ep = &ep_pool[i];
- if ( ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr) ) return ep;
+ if (ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr)) {
+ return ep;
+ }
}
return NULL;
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) {
return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
}
-TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) {
// If this device is different to the speed of the root device
// (i.e. is a low speed device on a full speed hub) then need pre
return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
}
-static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result)
-{
+static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result) {
// Mark transfer as done before we tell the tinyusb stack
uint8_t dev_addr = ep->dev_addr;
uint8_t ep_addr = ep->ep_addr;
@@ -98,47 +97,28 @@ static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_
hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true);
}
-static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep)
-{
+static void __tusb_irq_path_func(handle_hwbuf_status_bit)(uint bit, struct hw_endpoint *ep) {
usb_hw_clear->buf_status = bit;
- // EP may have been stalled?
- assert(ep->active);
- bool done = hw_endpoint_xfer_continue(ep);
- if ( done )
- {
+ const bool done = hw_endpoint_xfer_continue(ep);
+ if (done) {
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
}
}
-static void __tusb_irq_path_func(hw_handle_buff_status)(void)
-{
- uint32_t remaining_buffers = usb_hw->buf_status;
- pico_trace("buf_status 0x%08lx\n", remaining_buffers);
+static void __tusb_irq_path_func(handle_hwbuf_status)(void) {
+ uint32_t buf_status = usb_hw->buf_status;
+ pico_trace("buf_status 0x%08lx\n", buf_status);
// Check EPX first
- uint bit = 0b1;
- if ( remaining_buffers & bit )
- {
- remaining_buffers &= ~bit;
+ uint32_t bit = 1u;
+ if (buf_status & bit) {
+ buf_status &= ~bit;
struct hw_endpoint * ep = &epx;
-
- uint32_t ep_ctrl = *ep->endpoint_control;
- if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS )
- {
- TU_LOG(3, "Double Buffered: ");
- }
- else
- {
- TU_LOG(3, "Single Buffered: ");
- }
- TU_LOG_HEX(3, ep_ctrl);
-
- _handle_buff_status_bit(bit, ep);
+ handle_hwbuf_status_bit(bit, ep);
}
// Check "interrupt" (asynchronous) endpoints for both IN and OUT
- for ( uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++ )
- {
+ for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && buf_status; i++) {
// EPX is bit 0 & 1
// IEP1 IN is bit 2
// IEP1 OUT is bit 3
@@ -147,20 +127,17 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void)
// IEP3 IN is bit 6
// IEP3 OUT is bit 7
// etc
- for ( uint j = 0; j < 2; j++ )
- {
+ for (uint j = 0; j < 2; j++) {
bit = 1 << (i * 2 + j);
- if ( remaining_buffers & bit )
- {
- remaining_buffers &= ~bit;
- _handle_buff_status_bit(bit, &ep_pool[i]);
+ if (buf_status & bit) {
+ buf_status &= ~bit;
+ handle_hwbuf_status_bit(bit, &ep_pool[i]);
}
}
}
- if ( remaining_buffers )
- {
- panic("Unhandled buffer %d\n", remaining_buffers);
+ if (buf_status) {
+ panic("Unhandled buffer %d\n", buf_status);
}
}
@@ -220,7 +197,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void)
{
handled |= USB_INTS_BUFF_STATUS_BITS;
TU_LOG(2, "Buffer complete\r\n");
- hw_handle_buff_status();
+ handle_hwbuf_status();
}
if ( status & USB_INTS_TRANS_COMPLETE_BITS )
@@ -240,9 +217,8 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void)
if ( status & USB_INTS_ERROR_DATA_SEQ_BITS )
{
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
- TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n",
- tu_u32_low16(*epx.buffer_control),
- tu_u32_high16(*epx.buffer_control));
+ TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hwbuf_ctrl_reg_host(&epx)),
+ tu_u32_high16(*hwbuf_ctrl_reg_host(&epx)));
panic("Data Seq Error \n");
}
@@ -266,7 +242,7 @@ static struct hw_endpoint *_next_free_interrupt_ep(void)
ep = &ep_pool[i];
if ( !ep->configured )
{
- // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
+ // Will be configured by hw_endpoint_init / hw_endpoint_allocate
ep->interrupt_num = (uint8_t) (i - 1);
return ep;
}
@@ -274,41 +250,31 @@ static struct hw_endpoint *_next_free_interrupt_ep(void)
return ep;
}
-static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
-{
- struct hw_endpoint * ep = NULL;
+static hw_endpoint_t *hw_endpoint_allocate(uint8_t transfer_type) {
+ hw_endpoint_t *ep = NULL;
- if ( transfer_type != TUSB_XFER_CONTROL )
- {
+ if (transfer_type == TUSB_XFER_CONTROL) {
+ ep = &epx;
+ ep->hw_data_buf = &usbh_dpram->epx_data[0];
+ } else {
// Note: even though datasheet name these "Interrupt" endpoints. These are actually
// "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation)
ep = _next_free_interrupt_ep();
pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num);
assert(ep);
- ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
- ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
// 0 for epx (double buffered): TODO increase to 1024 for ISO
// 2x64 for intep0
// 3x64 for intep1
// etc
ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
}
- else
- {
- ep = &epx;
- ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
- ep->endpoint_control = &usbh_dpram->epx_ctrl;
- ep->hw_data_buf = &usbh_dpram->epx_data[0];
- }
return ep;
}
-static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
-{
+static void hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize,
+ uint8_t transfer_type, uint8_t bmInterval) {
// Already has data buffer, endpoint control, and buffer control allocated at this point
- assert(ep->endpoint_control);
- assert(ep->buffer_control);
assert(ep->hw_data_buf);
uint8_t const num = tu_edpt_number(ep_addr);
@@ -317,49 +283,41 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
ep->ep_addr = ep_addr;
ep->dev_addr = dev_addr;
- // For host, IN to host == RX, anything else rx == false
- ep->rx = (dir == TUSB_DIR_IN);
-
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = (num == 0 ? 1u : 0u);
ep->wMaxPacketSize = wMaxPacketSize;
- ep->transfer_type = transfer_type;
- pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type);
+ pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, transfer_type);
pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf);
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
// Bits 0-5 should be 0
assert(!(dpram_offset & 0b111111));
// Fill in endpoint control register with buffer offset
- uint32_t ep_reg = EP_CTRL_ENABLE_BITS
- | EP_CTRL_INTERRUPT_PER_BUFFER
- | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
- | dpram_offset;
- if ( bmInterval )
- {
- ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
+ uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER |
+ ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
+ if (bmInterval) {
+ ctrl_value |= (uint32_t)((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
}
- *ep->endpoint_control = ep_reg;
- pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg);
+
+ io_rw_32 *ctrl_reg = hwep_ctrl_reg_host(ep);
+ *ctrl_reg = ctrl_value;
+ pico_trace("endpoint control (0x%p) <- 0x%lx\n", ctrl_reg, ctrl_value);
ep->configured = true;
- if ( ep != &epx )
- {
+ if (ep != &epx) {
// Endpoint has its own addr_endp and interrupt bits to be setup!
// This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with:
// - device address
// - endpoint number / direction
// - preamble
- uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB));
+ uint32_t reg = (uint32_t)(dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB));
- if ( dir == TUSB_DIR_OUT )
- {
+ if (dir == TUSB_DIR_OUT) {
reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS;
}
- if ( need_pre(dev_addr) )
- {
+ if (need_pre(dev_addr)) {
reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
}
usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
@@ -367,8 +325,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
// Finally, enable interrupt that endpoint
usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
- // If it's an interrupt endpoint we need to set up the buffer control
- // register
+ // If it's an interrupt endpoint we need to set up the buffer control register
}
}
@@ -382,7 +339,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
assert(rhport == 0);
// Reset any previous state
- rp2040_usb_init();
+ rp2usb_init();
// Force VBUS detect to always present, for now we assume vbus is always provided (without using VBUS En)
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
@@ -466,8 +423,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
// reset epx if it is currently active with unplugged device
if (epx.configured && epx.active && epx.dev_addr == dev_addr) {
epx.configured = false;
- *epx.endpoint_control = 0;
- *epx.buffer_control = 0;
+ *hwep_ctrl_reg_host(&epx) = 0;
+ *hwbuf_ctrl_reg_host(&epx) = 0;
hw_endpoint_reset_transfer(&epx);
}
@@ -482,54 +439,41 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
// unconfigure the endpoint
ep->configured = false;
- *ep->endpoint_control = 0;
- *ep->buffer_control = 0;
+ *hwep_ctrl_reg_host(ep) = 0;
+ *hwbuf_ctrl_reg_host(ep) = 0;
hw_endpoint_reset_transfer(ep);
}
}
}
}
-uint32_t hcd_frame_number(uint8_t rhport)
-{
- (void) rhport;
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void)rhport;
return usb_hw->sof_rd;
}
-void hcd_int_enable(uint8_t rhport)
-{
- (void) rhport;
- assert(rhport == 0);
+void hcd_int_enable(uint8_t rhport) {
+ (void)rhport;
irq_set_enabled(USBCTRL_IRQ, true);
}
-void hcd_int_disable(uint8_t rhport)
-{
- (void) rhport;
+void hcd_int_disable(uint8_t rhport) {
+ (void)rhport;
// todo we should check this is disabling from the correct core; note currently this is never called
- assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, false);
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
-{
- (void) rhport;
-
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) {
+ (void)rhport;
pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
-
- // Allocated differently based on if it's an interrupt endpoint or not
- struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
+ hw_endpoint_t *ep = hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
TU_ASSERT(ep);
- _hw_endpoint_init(ep,
- dev_addr,
- ep_desc->bEndpointAddress,
- tu_edpt_packet_size(ep_desc),
- ep_desc->bmAttributes.xfer,
- ep_desc->bInterval);
+ hw_endpoint_init(ep, dev_addr, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer,
+ ep_desc->bInterval);
return true;
}
@@ -539,13 +483,12 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
return false; // TODO not implemented yet
}
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
-{
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
(void) rhport;
pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
- uint8_t const ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
// Get appropriate ep. Either EPX or interrupt endpoint
@@ -557,20 +500,18 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
assert(!ep->active);
// Control endpoint can change direction 0x00 <-> 0x80
- if ( ep_addr != ep->ep_addr )
- {
+ if (ep_addr != ep->ep_addr) {
assert(ep_num == 0);
// Direction has flipped on endpoint control so re init it but with same properties
- _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
+ hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, TUSB_XFER_CONTROL, 0);
}
// If a normal transfer (non-interrupt) then initiate using
// sie ctrl registers. Otherwise, interrupt ep registers should
// already be configured
- if ( ep == &epx )
- {
- hw_endpoint_xfer_start(ep, buffer, buflen);
+ if (ep == &epx) {
+ hw_endpoint_xfer_start(ep, buffer, NULL, buflen);
// That has set up buffer control, endpoint control etc
// for host we have to initiate the transfer
@@ -585,9 +526,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS;
busy_wait_at_least_cycles(12);
usb_hw->sie_ctrl = flags;
- }else
- {
- hw_endpoint_xfer_start(ep, buffer, buflen);
+ } else {
+ hw_endpoint_xfer_start(ep, buffer, NULL, buflen);
}
return true;
@@ -611,14 +551,14 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
}
// Configure EP0 struct with setup info for the trans complete
- struct hw_endpoint * ep = _hw_endpoint_allocate( (uint8_t) TUSB_XFER_CONTROL);
+ hw_endpoint_t *ep = hw_endpoint_allocate((uint8_t)TUSB_XFER_CONTROL);
TU_ASSERT(ep);
// EPX should be inactive
assert(!ep->active);
// EP0 out
- _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
+ hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
assert(ep->configured);
ep->remaining_len = 8;
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 9d0bd762d..3b65f57a4 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -35,34 +35,35 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPE
//--------------------------------------------------------------------+
-static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep);
+static void sync_xfer(hw_endpoint_t *ep);
-#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
- static bool e15_is_bulkin_ep(struct hw_endpoint* ep);
- static bool e15_is_critical_frame_period(struct hw_endpoint* ep);
-#else
- #define e15_is_bulkin_ep(x) (false)
- #define e15_is_critical_frame_period(x) (false)
-#endif
-
-// if usb hardware is in host mode
-TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
- return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
-}
+ #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+static bool e15_is_critical_frame_period(struct hw_endpoint *ep);
+ #else
+ #define e15_is_critical_frame_period(x) (false)
+ #endif
//--------------------------------------------------------------------+
// Implementation
//--------------------------------------------------------------------+
// Provide own byte by byte memcpy as not all copies are aligned
-static void unaligned_memcpy(void *dst, const void *src, size_t n) {
- uint8_t *dst_byte = (uint8_t*)dst;
- const uint8_t *src_byte = (const uint8_t*)src;
+static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) {
while (n--) {
- *dst_byte++ = *src_byte++;
+ *dst++ = *src++;
}
}
-void rp2040_usb_init(void) {
+void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) {
+ (void)access_mode;
+ unaligned_memcpy((uint8_t *)(uintptr_t)hwfifo, src, len);
+}
+
+void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, const tu_hwfifo_access_t *access_mode) {
+ (void)access_mode;
+ unaligned_memcpy(dest, (const uint8_t *)(uintptr_t)hwfifo, len);
+}
+
+void rp2usb_init(void) {
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
@@ -93,35 +94,38 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) {
ep->user_buf = 0;
}
-void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask,
- uint32_t or_mask) {
- uint32_t value = 0;
+void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) {
+ const bool is_host = rp2usb_is_host_mode();
+ uint32_t value = 0;
+ uint32_t buf_ctrl = *buf_ctrl_reg;
if (and_mask) {
- value = *ep->buffer_control & and_mask;
+ value = buf_ctrl & and_mask;
}
if (or_mask) {
value |= or_mask;
if (or_mask & USB_BUF_CTRL_AVAIL) {
- if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
- panic("ep %02X was already available", ep->ep_addr);
+ if (buf_ctrl & USB_BUF_CTRL_AVAIL) {
+ panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_ctrl_reg);
}
- *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
- // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control
+ *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL;
+
+ // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, we need to
+ // wait at least 1/48 mhz (usb clock), 12 cycles should be good for 48*12Mhz = 576Mhz.
// Don't need delay in host mode as host is in charge
- if (!is_host_mode()) {
+ if (!is_host) {
busy_wait_at_least_cycles(12);
}
}
}
- *ep->buffer_control = value;
+ *buf_ctrl_reg = value;
}
-// prepare buffer, return buffer control
-static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
- uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
+// prepare buffer, move data if tx, return buffer control
+static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) {
+ const uint16_t buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
@@ -130,10 +134,18 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep,
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
ep->next_pid ^= 1u;
- if (!ep->rx) {
- // Copy data from user buffer to hw buffer
- unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
- ep->user_buf += buflen;
+ if (!is_rx) {
+ if (buflen) {
+ // Copy data from user buffer/fifo to hw buffer
+ uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64;
+ if (ep->is_xfer_fifo) {
+ // not in sram, may mess up timing with E15 workaround
+ tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL);
+ } else {
+ unaligned_memcpy(hw_buf, ep->user_buf, buflen);
+ ep->user_buf += buflen;
+ }
+ }
// Mark as full
buf_ctrl |= USB_BUF_CTRL_FULL;
@@ -146,52 +158,75 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep,
buf_ctrl |= USB_BUF_CTRL_LAST;
}
- if (buf_id) buf_ctrl = buf_ctrl << 16;
+ if (buf_id) {
+ buf_ctrl = buf_ctrl << 16;
+ }
return buf_ctrl;
}
// Prepare buffer control register value
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
- uint32_t ep_ctrl = *ep->endpoint_control;
+ const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr);
+ bool is_rx;
+ bool is_host = false;
+ io_rw_32 *ep_ctrl_reg;
+ io_rw_32 *buf_ctrl_reg;
+
+ #if CFG_TUH_ENABLED
+ is_host = rp2usb_is_host_mode();
+ if (is_host) {
+ buf_ctrl_reg = hwbuf_ctrl_reg_host(ep);
+ ep_ctrl_reg = hwep_ctrl_reg_host(ep);
+ is_rx = (dir == TUSB_DIR_IN);
+ } else
+ #endif
+ {
+ buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ ep_ctrl_reg = hwep_ctrl_reg_device(ep);
+ is_rx = (dir == TUSB_DIR_OUT);
+ }
// always compute and start with buffer 0
- uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL;
+ uint32_t buf_ctrl = prepare_ep_buffer(ep, 0, is_rx) | USB_BUF_CTRL_SEL;
- // For now: skip double buffered for OUT endpoint in Device mode, since
- // host could send < 64 bytes and cause short packet on buffer0
- // NOTE: this could happen to Host mode IN endpoint
- // Also, Host mode "interrupt" endpoint hardware is only single buffered,
- // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
- bool const is_host = is_host_mode();
- bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
- (is_host && tu_edpt_number(ep->ep_addr) != 0);
+ // EP0 has no endpoint control register, also usbd only schedule 1 packet at a time (single buffer)
+ if (ep_ctrl_reg != NULL) {
+ uint32_t ep_ctrl = *ep_ctrl_reg;
- if (ep->remaining_len && !force_single) {
- // Use buffer 1 (double buffered) if there is still data
- // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
+ // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause
+ // short packet on buffer0
+ // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single
+ // buffered,
+ // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
+ const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0);
- buf_ctrl |= prepare_ep_buffer(ep, 1);
+ if (ep->remaining_len && !force_single) {
+ // Use buffer 1 (double buffered) if there is still data
+ // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
- // Set endpoint control double buffered bit if needed
- ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
- ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
- } else {
- // Single buffered since 1 is enough
- ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
- ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
- }
+ buf_ctrl |= prepare_ep_buffer(ep, 1, is_rx);
- *ep->endpoint_control = ep_ctrl;
+ // Set endpoint control double buffered bit if needed
+ ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
+ ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
+ } else {
+ // Single buffered since 1 is enough
+ ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
+ ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
+ }
+
+ *ep_ctrl_reg = ep_ctrl;
+ }
TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
// Finally, write to buffer_control which will trigger the transfer
// the next time the controller polls this dpram address
- _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
+ hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl);
}
-void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) {
+void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) {
hw_endpoint_lock_update(ep, 1);
if (ep->active) {
@@ -204,15 +239,25 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to
ep->remaining_len = total_len;
ep->xferred_len = 0;
ep->active = true;
- ep->user_buf = buffer;
- if (e15_is_bulkin_ep(ep)) {
+ if (ff != NULL) {
+ ep->user_fifo = ff;
+ ep->is_xfer_fifo = true;
+ } else {
+ ep->user_buf = buffer;
+ ep->is_xfer_fifo = false;
+ }
+
+ #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ if (ep->e15_bulk_in) {
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
}
if (e15_is_critical_frame_period(ep)) {
- ep->pending = 1;
- } else {
+ ep->pending = 1; // skip transfer if we are in critical frame period
+ } else
+ #endif
+ {
hw_endpoint_start_next_buffer(ep);
}
@@ -220,31 +265,37 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to
}
// sync endpoint buffer and return transferred bytes
-static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
- uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
- if (buf_id) buf_ctrl = buf_ctrl >> 16;
+static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id,
+ bool is_rx) {
+ uint32_t buf_ctrl = *buf_ctrl_reg;
+ if (buf_id) {
+ buf_ctrl = buf_ctrl >> 16;
+ }
- uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
+ const uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
- if (!ep->rx) {
+ if (!is_rx) {
// We are continuing a transfer here. If we are TX, we have successfully
// sent some data can increase the length we have sent
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
-
- ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
} else {
// If we have received some data, so can increase the length
// we have received AFTER we have copied it to the user buffer at the appropriate offset
assert(buf_ctrl & USB_BUF_CTRL_FULL);
- unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
- ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
- ep->user_buf += xferred_bytes;
+ uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64;
+ if (ep->is_xfer_fifo) {
+ // not in sram, may mess up timing with E15 workaround
+ tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL);
+ } else {
+ unaligned_memcpy(ep->user_buf, hw_buf, xferred_bytes);
+ ep->user_buf += xferred_bytes;
+ }
}
+ ep->xferred_len += xferred_bytes;
// Short packet
if (xferred_bytes < ep->wMaxPacketSize) {
- pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes);
// Reduce total length as this is last packet
ep->remaining_len = 0;
}
@@ -252,21 +303,38 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin
return xferred_bytes;
}
-static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) {
- // Update hw endpoint struct with info from hardware
- // after a buff status interrupt
+// Update hw endpoint struct with info from hardware after a buff status interrupt
+static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep) {
+ // const uint8_t ep_num = tu_edpt_number(ep->ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr);
- uint32_t __unused buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
- TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
+ io_rw_32 *buf_ctrl_reg;
+ io_rw_32 *ep_ctrl_reg;
+ bool is_rx;
- // always sync buffer 0
- uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
+ #if CFG_TUH_ENABLED
+ const bool is_host = rp2usb_is_host_mode();
+ if (is_host) {
+ buf_ctrl_reg = hwbuf_ctrl_reg_host(ep);
+ ep_ctrl_reg = hwep_ctrl_reg_host(ep);
+ is_rx = (dir == TUSB_DIR_IN);
+ } else
+ #endif
+ {
+ buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ ep_ctrl_reg = hwep_ctrl_reg_device(ep);
+ is_rx = (dir == TUSB_DIR_OUT);
+ }
+
+ TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_ctrl_reg),
+ tu_u32_high16(*buf_ctrl_reg));
+ uint16_t buf0_bytes = sync_ep_buffer(ep, buf_ctrl_reg, 0, is_rx); // always sync buffer 0
// sync buffer 1 if double buffered
- if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
+ if (ep_ctrl_reg != NULL && (*ep_ctrl_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
if (buf0_bytes == ep->wMaxPacketSize) {
// sync buffer 1 if not short packet
- sync_ep_buffer(ep, 1);
+ sync_ep_buffer(ep, buf_ctrl_reg, 1, is_rx);
} else {
// short packet on buffer 0
// TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
@@ -287,7 +355,8 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep)
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
- _hw_endpoint_buffer_control_set_value32(ep, 0);
+ io_rw_32 *buf_ctrl_reg = is_host ? hwbuf_ctrl_reg_host(ep) : hwbuf_ctrl_reg_device(ep);
+ hwbuf_ctrl_set(buf_ctrl_reg, 0);
usb_hw->abort &= ~TU_BIT(ep_id);
@@ -307,8 +376,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
}
- // Update EP struct from hardware state
- _hw_endpoint_xfer_sync(ep);
+ sync_xfer(ep); // Update EP struct from hardware state
// Now we have synced our state with the hardware. Is there more data to transfer?
// If we are done then notify tinyusb
@@ -318,9 +386,12 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
hw_endpoint_lock_update(ep, -1);
return true;
} else {
+ #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
if (e15_is_critical_frame_period(ep)) {
ep->pending = 1;
- } else {
+ } else
+ #endif
+ {
hw_endpoint_start_next_buffer(ep);
}
}
@@ -335,6 +406,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
//--------------------------------------------------------------------+
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+// E15 is fixed with RP2350
/* Don't mark IN buffers as available during the last 200us of a full-speed
frame. This avoids a situation seen with the USB2.0 hub on a Raspberry
@@ -354,16 +426,12 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
volatile uint32_t e15_last_sof = 0;
-// check if Errata 15 is needed for this endpoint i.e device bulk-in
-static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) {
- return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN &&
- ep->transfer_type == TUSB_XFER_BULK);
-}
-
// check if we need to apply Errata 15 workaround : i.e
// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame
static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) {
- TU_VERIFY(e15_is_bulkin_ep(ep));
+ if (!ep->e15_bulk_in) {
+ return false;
+ }
/* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point.
* The device state machine cannot recover from receiving an incorrect PID
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h
index d4d29a816..c03dc34b2 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.h
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h
@@ -1,29 +1,44 @@
#ifndef RP2040_COMMON_H_
#define RP2040_COMMON_H_
-#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
-#error TinyUSB device and host mode not supported at the same time
-#endif
-
-#include "common/tusb_common.h"
-
#include "pico.h"
#include "hardware/structs/usb.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#include "hardware/timer.h"
-#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
-#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
+#include "common/tusb_common.h"
+#include "osal/osal.h"
+#include "common/tusb_fifo.h"
+
+#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
+ #error TinyUSB device and host mode not supported at the same time
+#endif
+
+// E5 and E15 only apply to RP2040
+#if defined(PICO_RP2040) && PICO_RP2040 == 1
+ // RP2040 E5: USB device fails to exit RESET state on busy USB bus.
+ #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
+ #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
+ #endif
+
+ // RP2040 E15: USB Device controller will hang if certain bus errors occur during an IN transfer.
+ #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
+ #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX
+ #endif
+#endif
+
+#ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+ #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX 0
#endif
-#if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
-#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX
+#ifndef TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 0
#endif
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
-#undef PICO_RP2040_USB_FAST_IRQ
-#define PICO_RP2040_USB_FAST_IRQ 1
+ #undef PICO_RP2040_USB_FAST_IRQ
+ #define PICO_RP2040_USB_FAST_IRQ 1
#endif
#ifndef PICO_RP2040_USB_FAST_IRQ
@@ -36,6 +51,9 @@
#define __tusb_irq_path_func(x) x
#endif
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
#define usb_hw_set ((usb_hw_t *) hw_set_alias_untyped(usb_hw))
#define usb_hw_clear ((usb_hw_t *) hw_clear_alias_untyped(usb_hw))
@@ -43,63 +61,48 @@
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)
// Hardware information per endpoint
-typedef struct hw_endpoint
-{
- // Is this a valid struct
- bool configured;
-
- // Transfer direction (i.e. IN is rx for host but tx for device)
- // allows us to common up transfer functions
- bool rx;
-
- uint8_t ep_addr;
- uint8_t next_pid;
-
- // Endpoint control register
- io_rw_32 *endpoint_control;
-
- // Buffer control register
- io_rw_32 *buffer_control;
-
- // Buffer pointer in usb dpram
- uint8_t *hw_data_buf;
-
- // User buffer in main memory
- uint8_t *user_buf;
-
- // Current transfer information
- uint16_t remaining_len;
- uint16_t xferred_len;
+typedef struct hw_endpoint {
+ uint8_t ep_addr;
+ uint8_t next_pid;
+ bool active; // transferring data
+ bool is_xfer_fifo; // transfer using fifo
- // Data needed from EP descriptor
- uint16_t wMaxPacketSize;
-
- // Endpoint is in use
- bool active;
-
- // Interrupt, bulk, etc
- uint8_t transfer_type;
-
- // Transfer scheduled but not active
- uint8_t pending;
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ bool e15_bulk_in; // Errata15 device bulk in
+ uint8_t pending; // Transfer scheduled but not active
+#endif
#if CFG_TUH_ENABLED
- // Only needed for host
- uint8_t dev_addr;
-
- // If interrupt endpoint
- uint8_t interrupt_num;
+ bool configured; // Is this a valid struct
+ uint8_t dev_addr;
+ uint8_t interrupt_num; // for host interrupt endpoints
#endif
+ uint16_t wMaxPacketSize;
+ uint8_t *hw_data_buf; // Buffer pointer in usb dpram
+
+ // transfer info
+ union {
+ uint8_t *user_buf; // User buffer in main memory
+ tu_fifo_t *user_fifo;
+ };
+ uint16_t remaining_len;
+ uint16_t xferred_len;
+
} hw_endpoint_t;
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
extern volatile uint32_t e15_last_sof;
#endif
-void rp2040_usb_init(void);
+void rp2usb_init(void);
+
+// if usb hardware is in host mode
+TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) {
+ return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
+}
-void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
+void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len);
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
void hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
@@ -110,34 +113,60 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
}
-void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
+// #if CFG_TUD_ENABLED
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) {
+ uint8_t const epnum = tu_edpt_number(ep->ep_addr);
+ const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr);
+ if (epnum == 0) {
+ // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled
+ return NULL;
+ }
+ return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out;
+}
-TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep)
-{
- return *ep->buffer_control;
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) {
+ const uint8_t epnum = tu_edpt_number(ep->ep_addr);
+ const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr);
+ return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out;
}
+// #endif
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value)
-{
- _hw_endpoint_buffer_control_update32(ep, 0, value);
+#if CFG_TUH_ENABLED
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(struct hw_endpoint *ep) {
+ if (tu_edpt_number(ep->ep_addr) == 0) {
+ return &usbh_dpram->epx_ctrl;
+ }
+ return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
}
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value)
-{
- _hw_endpoint_buffer_control_update32(ep, ~value, value);
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_host(struct hw_endpoint *ep) {
+ if (tu_edpt_number(ep->ep_addr) == 0) {
+ return &usbh_dpram->epx_buf_ctrl;
+ }
+ return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
}
+#endif
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value)
-{
- _hw_endpoint_buffer_control_update32(ep, ~value, 0);
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask);
+
+TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) {
+ hwbuf_ctrl_update(buf_ctrl_reg, 0, value);
}
-static inline uintptr_t hw_data_offset (uint8_t *buf)
-{
- // Remove usb base from buffer pointer
- return (uintptr_t) buf ^ (uintptr_t) usb_dpram;
+TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) {
+ hwbuf_ctrl_update(buf_ctrl_reg, ~value, value);
}
-extern const char *ep_dir_string[];
+TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_clear_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) {
+ hwbuf_ctrl_update(buf_ctrl_reg, ~value, 0);
+}
+
+static inline uintptr_t hw_data_offset(uint8_t *buf) {
+ // Remove usb base from buffer pointer
+ return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
+}
#endif
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index 779c7bc3d..e2a51a5ca 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -30,25 +30,7 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_RUSB2)
#include "device/dcd.h"
-#include "rusb2_type.h"
-
-#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
- #include "rusb2_rx.h"
-#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
- #include "rusb2_ra.h"
- #if defined(RENESAS_CORTEX_M23)
- #define D0FIFO CFIFO
- #define D0FIFOSEL CFIFOSEL
- #define D0FIFOSEL_b CFIFOSEL_b
- #define D1FIFOSEL CFIFOSEL
- #define D1FIFOSEL_b CFIFOSEL_b
- #define D0FIFOCTR CFIFOCTR
- #define D0FIFOCTR_b CFIFOCTR_b
- #endif
-
-#else
- #error "Unsupported MCU"
-#endif
+#include "rusb2_common.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
@@ -161,106 +143,11 @@ static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) {
}
//--------------------------------------------------------------------+
-// Pipe FIFO
-//--------------------------------------------------------------------+
-
-// Write data buffer --> hw fifo
-static void pipe_write_packet(rusb2_reg_t * rusb, void *buf, volatile void *fifo, unsigned len)
-{
- (void) rusb;
-
- volatile uint16_t *ff16;
- volatile uint8_t *ff8;
-
- // Highspeed FIFO is 32-bit
- if ( rusb2_is_highspeed_reg(rusb) ) {
- // TODO 32-bit access for better performance
- ff16 = (volatile uint16_t*) ((uintptr_t) fifo+2);
- ff8 = (volatile uint8_t *) ((uintptr_t) fifo+3);
- }else {
- ff16 = (volatile uint16_t*) fifo;
- ff8 = ((volatile uint8_t*) fifo);
- }
-
- uint8_t const* buf8 = (uint8_t const*) buf;
-
- while (len >= 2) {
- *ff16 = tu_unaligned_read16(buf8);
- buf8 += 2;
- len -= 2;
- }
-
- if (len > 0) {
- *ff8 = *buf8;
- ++buf8;
- }
-}
-
-// Read data buffer <-- hw fifo
-static void pipe_read_packet(rusb2_reg_t * rusb, void *buf, volatile void *fifo, unsigned len)
-{
- (void) rusb;
-
- // TODO 16/32-bit access for better performance
-
- uint8_t *p = (uint8_t*)buf;
- volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
- while (len--) *p++ = *reg;
-}
-
-// Write data sw fifo --> hw fifo
-static void pipe_write_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void *fifo, uint16_t total_len) {
- tu_fifo_buffer_info_t info;
- tu_fifo_get_read_info(f, &info);
-
- uint16_t cnt_lin = tu_min16(total_len, info.linear.len);
- uint16_t cnt_wrap = tu_min16(total_len - cnt_lin, info.wrapped.len);
- uint16_t const cnt_written = cnt_lin + cnt_wrap;
-
- // Ensure only the last write is odd if total_len is odd
- if (cnt_wrap == 0) {
- pipe_write_packet(rusb, info.linear.ptr, fifo, cnt_lin);
- } else {
- pipe_write_packet(rusb, info.linear.ptr, fifo, cnt_lin & ~1);
-
- if (cnt_lin & 1) {
- uint8_t glue[2] = {info.linear.ptr[cnt_lin & ~1], info.wrapped.ptr[0]};
- pipe_write_packet(rusb, glue, fifo, 2);
- cnt_wrap--;
- info.wrapped.ptr++;
- }
-
- pipe_write_packet(rusb, info.wrapped.ptr, fifo, cnt_wrap);
- }
- tu_fifo_advance_read_pointer(f, cnt_written);
-}
-
-// Read data sw fifo <-- hw fifo
-static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void *fifo, uint16_t total_len) {
- tu_fifo_buffer_info_t info;
- tu_fifo_get_write_info(f, &info);
-
- uint16_t count = tu_min16(total_len, info.linear.len);
- pipe_read_packet(rusb, info.linear.ptr, fifo, count);
-
- uint16_t rem = total_len - count;
- if (rem) {
- rem = tu_min16(rem, info.wrapped.len);
- pipe_read_packet(rusb, info.wrapped.ptr, fifo, rem);
- count += rem;
- }
-
- tu_fifo_advance_write_pointer(f, count);
-}
-
-//--------------------------------------------------------------------+
// Pipe Transfer
//--------------------------------------------------------------------+
-
-static bool pipe0_xfer_in(rusb2_reg_t* rusb)
-{
- pipe_state_t *pipe = &_dcd.pipe[0];
- const unsigned rem = pipe->remaining;
+static bool pipe0_xfer_in(rusb2_reg_t *rusb) {
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ const unsigned rem = pipe->remaining;
if (!rem) {
pipe->buf = NULL;
@@ -272,11 +159,25 @@ static bool pipe0_xfer_in(rusb2_reg_t* rusb)
void *buf = pipe->buf;
if (len) {
+ // uint16_t fifo_sel = RUSB2_CFIFOSEL_ISEL_WRITE | FIFOSEL_BIGEND;
+ tu_hwfifo_access_t access_mode;
+ access_mode.param = (uintptr_t)rusb;
+ //
+ if (rusb2_is_highspeed_reg(rusb)) {
+ // fifo_sel |= RUSB2_FIFOSEL_MBW_32BIT;
+ access_mode.data_stride = 4u;
+ } else {
+ // fifo_sel |= RUSB2_FIFOSEL_MBW_16BIT;
+ access_mode.data_stride = 2u;
+ }
+ // rusb->CFIFOSEL = fifo_sel;
+ // while (0 == (rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE)) {}
+
if (pipe->ff) {
- pipe_write_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->CFIFO, len);
+ tu_hwfifo_write_from_fifo(&rusb->CFIFO, (tu_fifo_t *)buf, len, &access_mode);
} else {
- pipe_write_packet(rusb, buf, (volatile void*)&rusb->CFIFO, len);
- pipe->buf = (uint8_t*)buf + len;
+ tu_hwfifo_write(&rusb->CFIFO, buf, len, &access_mode);
+ pipe->buf = (uint8_t *)buf + len;
}
}
@@ -288,10 +189,9 @@ static bool pipe0_xfer_in(rusb2_reg_t* rusb)
return false;
}
-static bool pipe0_xfer_out(rusb2_reg_t* rusb)
-{
- pipe_state_t *pipe = &_dcd.pipe[0];
- const unsigned rem = pipe->remaining;
+static bool pipe0_xfer_out(rusb2_reg_t *rusb) {
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ const unsigned rem = pipe->remaining;
const uint16_t mps = edpt0_max_packet_size(rusb);
const uint16_t vld = rusb->CFIFOCTR_b.DTLN;
@@ -299,11 +199,14 @@ static bool pipe0_xfer_out(rusb2_reg_t* rusb)
void *buf = pipe->buf;
if (len) {
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
+
if (pipe->ff) {
- pipe_read_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->CFIFO, len);
+ tu_hwfifo_read_to_fifo(&rusb->CFIFO, (tu_fifo_t *)buf, len, &access_mode);
} else {
- pipe_read_packet(rusb, buf, (volatile void*)&rusb->CFIFO, len);
- pipe->buf = (uint8_t*)buf + len;
+ tu_hwfifo_read(&rusb->CFIFO, buf, len, &access_mode);
+ pipe->buf = (uint8_t *)buf + len;
}
}
@@ -330,18 +233,27 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
return true;
}
- rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_16BIT | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
- const uint16_t mps = edpt_max_packet_size(rusb, num);
+ const uint16_t fifo_sel = num | FIFOSEL_BIGEND;
+ const bool is_highspeed = rusb2_is_highspeed_reg(rusb);
+ if (is_highspeed) {
+ rusb->D0FIFOSEL = fifo_sel | RUSB2_FIFOSEL_MBW_32BIT;
+ } else {
+ rusb->D0FIFOSEL = fifo_sel | RUSB2_FIFOSEL_MBW_16BIT;
+ }
+
+ const uint16_t mps = edpt_max_packet_size(rusb, num);
pipe_wait_for_ready(rusb, num);
- const uint16_t len = tu_min16(rem, mps);
- void *buf = pipe->buf;
+ uint16_t len = tu_min16(rem, mps);
+ void *buf = pipe->buf;
if (len) {
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
if (pipe->ff) {
- pipe_write_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->D0FIFO, len);
+ tu_hwfifo_write_from_fifo(&rusb->D0FIFO, (tu_fifo_t *)buf, len, &access_mode);
} else {
- pipe_write_packet(rusb, buf, (volatile void*)&rusb->D0FIFO, len);
- pipe->buf = (uint8_t*)buf + len;
+ tu_hwfifo_write(&rusb->D0FIFO, buf, len, &access_mode);
+ pipe->buf = (uint8_t *)buf + len;
}
}
@@ -362,7 +274,14 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
pipe_state_t *pipe = &_dcd.pipe[num];
const uint16_t rem = pipe->remaining;
- rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_8BIT;
+ uint16_t fifo_sel = num | FIFOSEL_BIGEND;
+ if (rusb2_is_highspeed_reg(rusb)) {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_32BIT;
+ } else {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_16BIT;
+ }
+ rusb->D0FIFOSEL = fifo_sel;
+
const uint16_t mps = edpt_max_packet_size(rusb, num);
pipe_wait_for_ready(rusb, num);
@@ -371,11 +290,13 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
void *buf = pipe->buf;
if (len) {
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
if (pipe->ff) {
- pipe_read_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->D0FIFO, len);
+ tu_hwfifo_read_to_fifo(&rusb->D0FIFO, (tu_fifo_t *)buf, len, &access_mode);
} else {
- pipe_read_packet(rusb, buf, (volatile void*)&rusb->D0FIFO, len);
- pipe->buf = (uint8_t*)buf + len;
+ tu_hwfifo_read(&rusb->D0FIFO, buf, len, &access_mode);
+ pipe->buf = (uint8_t *)buf + len;
}
}
@@ -428,28 +349,29 @@ static void process_status_completion(uint8_t rhport)
dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
}
-static bool process_pipe0_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
-{
+static bool process_pipe0_xfer(rusb2_reg_t *rusb, int buffer_type, uint8_t ep_addr, void *buffer,
+ uint16_t total_bytes) {
+ uint16_t fifo_sel =
+ (rusb2_is_highspeed_reg(rusb) ? RUSB2_FIFOSEL_MBW_32BIT : RUSB2_FIFOSEL_MBW_16BIT) | FIFOSEL_BIGEND;
+
/* configure fifo direction and access unit settings */
- if ( ep_addr ) {
- /* IN, 2 bytes */
- rusb->CFIFOSEL = RUSB2_CFIFOSEL_ISEL_WRITE | RUSB2_FIFOSEL_MBW_16BIT |
- (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
- while ( !(rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) ) {}
- } else {
- /* OUT, a byte */
- rusb->CFIFOSEL = RUSB2_FIFOSEL_MBW_8BIT;
- while ( rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE ) {}
+ if (ep_addr != 0) {
+ // Control IN
+ fifo_sel |= RUSB2_CFIFOSEL_ISEL_WRITE;
+ }
+ rusb->CFIFOSEL = fifo_sel;
+ while ((rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) != (fifo_sel & RUSB2_CFIFOSEL_ISEL_WRITE)) {
+ // wait until ISEL_WRITE take effect
}
pipe_state_t *pipe = &_dcd.pipe[0];
- pipe->ff = buffer_type;
- pipe->length = total_bytes;
- pipe->remaining = total_bytes;
+ pipe->ff = buffer_type;
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
- if ( total_bytes ) {
+ if (total_bytes) {
pipe->buf = buffer;
- if ( ep_addr ) {
+ if (ep_addr) {
/* IN */
TU_ASSERT(rusb->DCPCTR_b.BSTS && (rusb->USBREQ & 0x80));
pipe0_xfer_in(rusb);
@@ -525,7 +447,7 @@ static bool process_edpt_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_add
static void process_pipe0_bemp(uint8_t rhport)
{
rusb2_reg_t* rusb = RUSB2_REG(rhport);
- bool completed = pipe0_xfer_in(rusb);
+ bool completed = pipe0_xfer_in(rusb);
if (completed) {
pipe_state_t *pipe = &_dcd.pipe[0];
dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
@@ -621,18 +543,20 @@ static void process_set_address(uint8_t rhport)
{
rusb2_reg_t* rusb = RUSB2_REG(rhport);
const uint16_t addr = rusb->USBADDR_b.USBADDR;
- if (!addr) return;
+ if (!addr) {
+ return;
+ }
const tusb_control_request_t setup_packet = {
#if defined(__CCRX__)
.bmRequestType = { 0 }, /* Note: CCRX needs the braces over this struct member */
-#else
- .bmRequestType = 0,
-#endif
- .bRequest = TUSB_REQ_SET_ADDRESS,
- .wValue = addr,
- .wIndex = 0,
- .wLength = 0,
+ #else
+ .bmRequestType = 0,
+ #endif
+ .bRequest = TUSB_REQ_SET_ADDRESS,
+ .wValue = addr,
+ .wIndex = 0,
+ .wLength = 0,
};
dcd_event_setup_received(rhport, (const uint8_t *) &setup_packet, true);
@@ -899,7 +823,6 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
{
(void) is_isr;
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
- TU_ASSERT(ff->item_size == 1);
rusb2_reg_t* rusb = RUSB2_REG(rhport);
dcd_int_disable(rhport);
@@ -912,7 +835,9 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
- if (!ctr) return;
+ if (!ctr) {
+ return;
+ }
dcd_int_disable(rhport);
const uint32_t pid = *ctr & 0x3;
*ctr = pid | RUSB2_PIPE_CTR_PID_STALL;
@@ -924,7 +849,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
rusb2_reg_t * rusb = RUSB2_REG(rhport);
volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
- if (!ctr) return;
+ if (!ctr) {
+ return;
+ }
dcd_int_disable(rhport);
*ctr = RUSB2_PIPE_CTR_SQCLR_Msk;
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index 6f6d27d0e..4c3315044 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -31,17 +31,9 @@
#include "host/hcd.h"
#include "host/usbh.h"
-#include "rusb2_type.h"
+#include "rusb2_common.h"
-#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
- #include "rusb2_rx.h"
-#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
- #include "rusb2_ra.h"
-#else
- #error "Unsupported MCU"
-#endif
-
-#define TU_RUSB2_HCD_DBG 2
+ #define TU_RUSB2_HCD_DBG 2
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@@ -76,12 +68,10 @@ typedef struct TU_ATTR_PACKED {
TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
TU_ATTR_BIT_FIELD_ORDER_END
-typedef struct
-{
- bool need_reset; /* The device has not been reset after connection. */
+typedef struct {
pipe_state_t pipe[PIPE_COUNT];
- uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
- uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
+ uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
+ uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
} hcd_data_t;
//--------------------------------------------------------------------+
@@ -166,29 +156,6 @@ static inline void pipe_wait_for_ready(rusb2_reg_t* rusb, unsigned num)
while (!rusb->D0FIFOCTR_b.FRDY) {}
}
-static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
-{
- // NOTE: unlike DCD, Highspeed 32-bit FIFO does not need to adjust the fifo address
- volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
- uintptr_t addr = (uintptr_t)buf;
- while (len >= 2) {
- reg->u16 = *(const uint16_t *)addr;
- addr += 2;
- len -= 2;
- }
- if (len) {
- reg->u8 = *(const uint8_t *)addr;
- ++addr;
- }
-}
-
-static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
-{
- uint8_t *p = (uint8_t*)buf;
- volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
- while (len--) *p++ = *reg;
-}
-
static bool pipe0_xfer_in(rusb2_reg_t* rusb)
{
pipe_state_t *pipe = &_hcd.pipe[0];
@@ -199,8 +166,12 @@ static bool pipe0_xfer_in(rusb2_reg_t* rusb)
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
+
rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
- pipe_read_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ // pipe_read_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ tu_hwfifo_read(&rusb->CFIFO, buf, len, &access_mode);
pipe->buf = (uint8_t*)buf + len;
}
if (len < mps) {
@@ -227,7 +198,11 @@ static bool pipe0_xfer_out(rusb2_reg_t* rusb)
const unsigned len = TU_MIN(mps, rem);
void *buf = pipe->buf;
if (len) {
- pipe_write_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
+
+ // pipe_write_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ tu_hwfifo_write(&rusb->CFIFO, buf, len, &access_mode);
pipe->buf = (uint8_t*)buf + len;
}
if (len < mps) {
@@ -242,14 +217,24 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
pipe_state_t *pipe = &_hcd.pipe[num];
const unsigned rem = pipe->remaining;
- rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_8BIT;
+ uint16_t fifo_sel = num | FIFOSEL_BIGEND;
+ if (rusb2_is_highspeed_reg(rusb)) {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_32BIT;
+ } else {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_16BIT;
+ }
+ rusb->D0FIFOSEL = fifo_sel;
+
const unsigned mps = edpt_max_packet_size(rusb, num);
pipe_wait_for_ready(rusb, num);
const unsigned vld = rusb->D0FIFOCTR_b.DTLN;
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
- pipe_read_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ // pipe_read_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
+ tu_hwfifo_read(&rusb->D0FIFO, buf, len, &access_mode);
pipe->buf = (uint8_t*)buf + len;
}
if (len < mps) {
@@ -275,13 +260,23 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
return true;
}
- rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_16BIT | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
+ uint16_t fifo_sel = num | FIFOSEL_BIGEND;
+ if (rusb2_is_highspeed_reg(rusb)) {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_32BIT;
+ } else {
+ fifo_sel |= RUSB2_FIFOSEL_MBW_16BIT;
+ }
+ rusb->D0FIFOSEL = fifo_sel;
+
const unsigned mps = edpt_max_packet_size(rusb, num);
pipe_wait_for_ready(rusb, num);
const unsigned len = TU_MIN(rem, mps);
void *buf = pipe->buf;
if (len) {
- pipe_write_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ // pipe_write_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ tu_hwfifo_access_t access_mode = {.data_stride = (rusb2_is_highspeed_reg(rusb) ? 4u : 2u),
+ .param = (uintptr_t)rusb};
+ tu_hwfifo_write(&rusb->D0FIFO, buf, len, &access_mode);
pipe->buf = (uint8_t*)buf + len;
}
if (len < mps) {
@@ -296,18 +291,19 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
static bool process_pipe0_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
{
(void)dev_addr;
-
rusb2_reg_t* rusb = RUSB2_REG(rhport);
const unsigned dir_in = tu_edpt_dir(ep_addr);
+ uint16_t fifo_sel =
+ (rusb2_is_highspeed_reg(rusb) ? RUSB2_FIFOSEL_MBW_32BIT : RUSB2_FIFOSEL_MBW_16BIT) | FIFOSEL_BIGEND;
+
/* configure fifo direction and access unit settings */
- if (dir_in) { /* IN, a byte */
- rusb->CFIFOSEL = RUSB2_FIFOSEL_MBW_8BIT;
- while (rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) ;
- } else { /* OUT, 2 bytes */
- rusb->CFIFOSEL = RUSB2_CFIFOSEL_ISEL_WRITE | RUSB2_FIFOSEL_MBW_16BIT |
- (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
- while (!(rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE)) ;
+ if (dir_in == TUSB_DIR_OUT) {
+ fifo_sel |= RUSB2_CFIFOSEL_ISEL_WRITE;
+ }
+ rusb->CFIFOSEL = fifo_sel;
+ while ((rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) != (fifo_sel & RUSB2_CFIFOSEL_ISEL_WRITE)) {
+ // wait until ISEL_WRITE take effect
}
pipe_state_t *pipe = &_hcd.pipe[0];
@@ -535,13 +531,8 @@ void hcd_int_disable(uint8_t rhport) {
rusb2_int_disable(rhport);
}
-uint32_t hcd_frame_number(uint8_t rhport)
-{
- rusb2_reg_t* rusb = RUSB2_REG(rhport);
-
- /* The device must be reset at least once after connection
- * in order to start the frame counter. */
- if (_hcd.need_reset) hcd_port_reset(rhport);
+uint32_t hcd_frame_number(uint8_t rhport) {
+ rusb2_reg_t *rusb = RUSB2_REG(rhport);
return rusb->FRMNUM_b.FRNM;
}
@@ -550,32 +541,18 @@ uint32_t hcd_frame_number(uint8_t rhport)
*--------------------------------------------------------------------+*/
bool hcd_port_connect_status(uint8_t rhport) {
rusb2_reg_t* rusb = RUSB2_REG(rhport);
- return rusb->INTSTS1_b.ATTCH ? true : false;
+ const uint16_t line_state = rusb->SYSSTS0 & RUSB2_SYSSTS0_LNST_Msk;
+ return line_state == RUSB2_SYSSTS0_LNST_FS_J || line_state == RUSB2_SYSSTS0_LNST_FS_K;
}
void hcd_port_reset(uint8_t rhport) {
rusb2_reg_t* rusb = RUSB2_REG(rhport);
- rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
- while (rusb->DCPCTR_b.PBUSY) {}
-
- hcd_int_disable(rhport);
- rusb->DVSTCTR0_b.UACT = 0;
- if (rusb->DCPCTR_b.SUREQ) {
- rusb->DCPCTR_b.SUREQCLR = 1;
- }
- hcd_int_enable(rhport);
-
- /* Reset should be asserted 10-20ms. */
rusb->DVSTCTR0_b.USBRST = 1;
- for (volatile int i = 0; i < 2400000; ++i) {}
- rusb->DVSTCTR0_b.USBRST = 0;
-
- rusb->DVSTCTR0_b.UACT = 1;
- _hcd.need_reset = false;
}
void hcd_port_reset_end(uint8_t rhport) {
- (void) rhport;
+ rusb2_reg_t *rusb = RUSB2_REG(rhport);
+ rusb->DVSTCTR0_b.USBRST = 0;
}
tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
@@ -584,7 +561,8 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
case RUSB2_DVSTCTR0_RHST_HS: return TUSB_SPEED_HIGH;
case RUSB2_DVSTCTR0_RHST_FS: return TUSB_SPEED_FULL;
case RUSB2_DVSTCTR0_RHST_LS: return TUSB_SPEED_LOW;
- default: return TUSB_SPEED_INVALID;
+ default:
+ return TUSB_SPEED_INVALID;
}
}
@@ -802,7 +780,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
if (is1 & RUSB2_INTSTS1_ATTCH_Msk) {
rusb->DVSTCTR0_b.UACT = 1;
- _hcd.need_reset = true;
rusb->INTENB1 = (rusb->INTENB1 & ~RUSB2_INTSTS1_ATTCH_Msk) | RUSB2_INTSTS1_DTCH_Msk;
hcd_event_device_attach(rhport, true);
}
diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c
index 856f9714f..8addbe4c6 100644
--- a/src/portable/renesas/rusb2/rusb2_common.c
+++ b/src/portable/renesas/rusb2/rusb2_common.c
@@ -27,20 +27,19 @@
#include "tusb_option.h"
#if defined(TUP_USBIP_RUSB2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+ #include "osal/osal.h"
+ #include "common/tusb_fifo.h"
-#include "rusb2_type.h"
+ #include "rusb2_common.h"
-#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
-#include "rusb2_rx.h"
-
-#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
-#include "rusb2_ra.h"
+ #if TU_CHECK_MCU(OPT_MCU_RAXXX)
+ #include "rusb2_ra.h"
// USBFS_INT_IRQn and USBHS_USB_INT_RESUME_IRQn are generated by FSP
rusb2_controller_t rusb2_controller[] = {
- { .reg_base = R_USB_FS0_BASE, .irqnum = USBFS_INT_IRQn },
+ {.reg_base = R_USB_FS0_BASE, .irqnum = USBFS_INT_IRQn},
#ifdef RUSB2_SUPPORT_HIGHSPEED
- { .reg_base = R_USB_HS0_BASE, .irqnum = USBHS_USB_INT_RESUME_IRQn },
+ {.reg_base = R_USB_HS0_BASE, .irqnum = USBHS_USB_INT_RESUME_IRQn},
#endif
};
@@ -49,10 +48,69 @@ void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum);
void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) {
rusb2_controller[rhport].irqnum = irqnum;
}
+ #endif
-#else
- #error "Unsupported MCU"
-#endif
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+static void hwfifo_set_mbw(rusb2_reg_t *rusb, uintptr_t hwfifo, uint16_t mbw) {
+ volatile uint16_t *fifo_sel;
+ if (hwfifo == (uintptr_t)&rusb->CFIFO) {
+ fifo_sel = &rusb->CFIFOSEL;
+ } else if (hwfifo == (uintptr_t)&rusb->D0FIFO) {
+ fifo_sel = &rusb->D0FIFOSEL;
+ } else if (hwfifo == (uintptr_t)&rusb->D1FIFO) {
+ fifo_sel = &rusb->D1FIFOSEL;
+ } else {
+ return;
+ }
+
+ *fifo_sel = (*fifo_sel & ~RUSB2_CFIFOSEL_MBW_Msk) | mbw;
+}
+
+// write to hwfifo from buffer with access mode
+void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) {
+ rusb2_reg_t *rusb = (rusb2_reg_t *)access_mode->param;
+ const uint8_t *buf8 = (const uint8_t *)src;
+ volatile uint16_t *ff16;
+ volatile uint8_t *ff8;
+ const bool is_highspeed = rusb2_is_highspeed_reg(rusb);
+ if (is_highspeed) {
+ ff16 = (volatile uint16_t *)((uintptr_t)hwfifo + 2);
+ ff8 = (volatile uint8_t *)((uintptr_t)hwfifo + 3);
+ } else {
+ ff16 = (volatile uint16_t *)hwfifo;
+ ff8 = ((volatile uint8_t *)hwfifo);
+ }
+
+ // 32-bit access for highspeed
+ if (is_highspeed) {
+ volatile uint32_t *ff32 = (volatile uint32_t *)hwfifo;
+ while (len >= 4) {
+ *ff32 = tu_unaligned_read32(buf8);
+ buf8 += 4;
+ len -= 4;
+ }
+
+ if (len >= 2) {
+ // switch to 16-bit access
+ hwfifo_set_mbw(rusb, (uintptr_t)hwfifo, RUSB2_FIFOSEL_MBW_16BIT);
+ }
+ }
+
+ // 16-bit access
+ while (len >= 2) {
+ *ff16 = tu_unaligned_read16(buf8);
+ buf8 += 2;
+ len -= 2;
+ }
+
+ // 8-bit access does not need to change MBW
+ if (len > 0) {
+ *ff8 = *buf8;
+ ++buf8;
+ }
+}
#endif
diff --git a/src/portable/renesas/rusb2/rusb2_common.h b/src/portable/renesas/rusb2/rusb2_common.h
new file mode 100644
index 000000000..6b8c3d7f2
--- /dev/null
+++ b/src/portable/renesas/rusb2/rusb2_common.h
@@ -0,0 +1,58 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 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
+ * 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.
+ */
+#pragma once
+
+#include "common/tusb_common.h"
+#include "rusb2_type.h"
+
+#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
+ #include "rusb2_rx.h"
+#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
+ #include "rusb2_ra.h"
+
+ // Hack for D0FIFO definitions on RA Cortex-M23
+ #if defined(RENESAS_CORTEX_M23)
+ #define D0FIFO CFIFO
+ #define D0FIFOSEL CFIFOSEL
+ #define D0FIFOSEL_b CFIFOSEL_b
+ #define D1FIFOSEL CFIFOSEL
+ #define D1FIFOSEL_b CFIFOSEL_b
+ #define D0FIFOCTR CFIFOCTR
+ #define D0FIFOCTR_b CFIFOCTR_b
+ #endif
+
+#else
+ #error "Unsupported MCU"
+#endif
+
+
+//--------------------------------------------------------------------+
+// Common
+//--------------------------------------------------------------------+
+
+enum {
+ FIFOSEL_BIGEND = (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0)
+};
diff --git a/src/portable/renesas/rusb2/rusb2_type.h b/src/portable/renesas/rusb2/rusb2_type.h
index 71837d03c..21f116857 100644
--- a/src/portable/renesas/rusb2/rusb2_type.h
+++ b/src/portable/renesas/rusb2/rusb2_type.h
@@ -41,10 +41,9 @@ extern "C" {
#define _ccrx_evenaccess
#endif
-/*--------------------------------------------------------------------*/
-/* Register Definitions */
-/*--------------------------------------------------------------------*/
-
+//--------------------------------------------------------------------+
+// Register Definitions
+//--------------------------------------------------------------------+
/* Start of definition of packed structs (used by the CCRX toolchain) */
TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
@@ -1669,15 +1668,20 @@ TU_ATTR_BIT_FIELD_ORDER_END
/*--------------------------------------------------------------------*/
/* Register Bit Utils */
/*--------------------------------------------------------------------*/
-#define RUSB2_PIPE_CTR_PID_NAK (0U << RUSB2_PIPE_CTR_PID_Pos) /* NAK response */
-#define RUSB2_PIPE_CTR_PID_BUF (1U << RUSB2_PIPE_CTR_PID_Pos) /* BUF response (depends buffer state) */
-#define RUSB2_PIPE_CTR_PID_STALL (2U << RUSB2_PIPE_CTR_PID_Pos) /* STALL response */
-#define RUSB2_PIPE_CTR_PID_STALL2 (3U << RUSB2_PIPE_CTR_PID_Pos) /* Also STALL response */
+#define RUSB2_SYSSTS0_LNST_SE0 (0)
+#define RUSB2_SYSSTS0_LNST_FS_J (1u << RUSB2_SYSSTS0_LNST_Pos) /* Full-speed J state */
+#define RUSB2_SYSSTS0_LNST_FS_K (2u << RUSB2_SYSSTS0_LNST_Pos) /* Full-speed K state */
+#define RUSB2_SYSSTS0_LNST_LS_SE1 (3u << RUSB2_SYSSTS0_LNST_Pos) /* Low-speed SE1 state */
#define RUSB2_DVSTCTR0_RHST_LS (1U << RUSB2_DVSTCTR0_RHST_Pos) /* Low-speed connection */
#define RUSB2_DVSTCTR0_RHST_FS (2U << RUSB2_DVSTCTR0_RHST_Pos) /* Full-speed connection */
#define RUSB2_DVSTCTR0_RHST_HS (3U << RUSB2_DVSTCTR0_RHST_Pos) /* Full-speed connection */
+#define RUSB2_PIPE_CTR_PID_NAK (0U << RUSB2_PIPE_CTR_PID_Pos) /* NAK response */
+#define RUSB2_PIPE_CTR_PID_BUF (1U << RUSB2_PIPE_CTR_PID_Pos) /* BUF response (depends buffer state) */
+#define RUSB2_PIPE_CTR_PID_STALL (2U << RUSB2_PIPE_CTR_PID_Pos) /* STALL response */
+#define RUSB2_PIPE_CTR_PID_STALL2 (3U << RUSB2_PIPE_CTR_PID_Pos) /* Also STALL response */
+
#define RUSB2_DEVADD_USBSPD_LS (1U << RUSB2_DEVADD_USBSPD_Pos) /* Target Device Low-speed */
#define RUSB2_DEVADD_USBSPD_FS (2U << RUSB2_DEVADD_USBSPD_Pos) /* Target Device Full-speed */
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 64046ce17..a6abc6244 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -108,22 +108,10 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \
- !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0)
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0)
-#include "device/dcd.h"
-
-#if defined(TUP_USBIP_FSDEV_STM32)
- #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
-
-#include "fsdev_type.h"
+ #include "device/dcd.h"
+ #include "fsdev_common.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
@@ -131,25 +119,25 @@
// One of these for every EP IN & OUT, uses a bit of RAM....
typedef struct {
- uint8_t *buffer;
+ uint8_t *buffer;
tu_fifo_t *ff;
- uint16_t total_len;
- uint16_t queued_len;
- uint16_t max_packet_size;
- uint8_t ep_idx; // index for USB_EPnR register
- bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_packet_size;
+ uint8_t ep_idx; // index for USB_EPnR register
+ bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask
} xfer_ctl_t;
// EP allocator
typedef struct {
uint8_t ep_num;
uint8_t ep_type;
- bool allocated[2];
+ bool allocated[2];
} ep_alloc_t;
static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2];
static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT];
-static uint8_t remoteWakeCountdown; // When wake is requested
+static uint8_t remoteWakeCountdown; // When wake is requested
//--------------------------------------------------------------------+
// Prototypes
@@ -163,17 +151,12 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir);
// PMA allocation/access
static uint16_t ep_buf_ptr; ///< Points to first free memory location
static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf);
-static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes);
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes);
-
-static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes);
-static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes);
+static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
static void edpt0_open(uint8_t rhport);
TU_ATTR_ALWAYS_INLINE static inline void edpt0_prepare_setup(void) {
- btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8);
+ btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8);
}
//--------------------------------------------------------------------+
@@ -187,42 +170,22 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
- // Follow the RM mentions to use a special ordering of PDWN and FRES
- for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
- asm("NOP");
- }
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void)rh_init;
- // Perform USB peripheral reset
- FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
- for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
- asm("NOP");
- }
+ fsdev_core_reset();
- FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
-
- // Wait startup time, for F042 and F070, this is <= 1 us.
- for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
- asm("NOP");
- }
FSDEV_REG->CNTR = 0; // Enable USB
-#if !defined(FSDEV_BUS_32BIT)
+ #if !defined(FSDEV_BUS_32BIT)
// BTABLE register does not exist any more on 32-bit bus devices
FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE;
-#endif
+ #endif
- FSDEV_REG->ISTR = 0; // Clear pending interrupts
+ // Enable interrupts for device mode
+ FSDEV_REG->CNTR |=
+ USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_PMAOVRM;
- // Reset endpoints to disabled
- for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
- // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED.
- ep_write(i, 0u, false);
- }
-
- FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM |
- USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_PMAOVRM;
handle_bus_reset(rhport);
// Enable pull-up if supported
@@ -231,6 +194,14 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
+bool dcd_deinit(uint8_t rhport) {
+ (void)rhport;
+
+ fsdev_deinit();
+
+ return true;
+}
+
void dcd_sof_enable(uint8_t rhport, bool en) {
(void)rhport;
@@ -264,8 +235,8 @@ static void handle_bus_reset(uint8_t rhport) {
for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
// Clear EP allocation status
- ep_alloc_status[i].ep_num = 0xFF;
- ep_alloc_status[i].ep_type = 0xFF;
+ ep_alloc_status[i].ep_num = 0xFF;
+ ep_alloc_status[i].ep_type = 0xFF;
ep_alloc_status[i].allocated[0] = false;
ep_alloc_status[i].allocated[1] = false;
}
@@ -273,7 +244,7 @@ static void handle_bus_reset(uint8_t rhport) {
// Reset PMA allocation
ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT;
- edpt0_open(rhport); // open control endpoint (both IN & OUT)
+ edpt0_open(rhport); // open control endpoint (both IN & OUT)
FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function
}
@@ -282,8 +253,8 @@ static void handle_bus_reset(uint8_t rhport) {
static void handle_ctr_tx(uint32_t ep_id) {
uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
- uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN);
+ const uint8_t ep_num = ep_reg & USB_EPADDR_FIELD;
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN);
if (ep_is_iso(ep_reg)) {
// Ignore spurious interrupts that we don't schedule
@@ -293,11 +264,11 @@ static void handle_ctr_tx(uint32_t ep_id) {
return;
}
xfer->iso_in_sending = false;
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1;
-#else
+ #else
uint8_t buf_id = BTABLE_BUF_TX;
-#endif
+ #endif
btable_set_count(ep_id, buf_id, 0);
}
@@ -310,17 +281,17 @@ static void handle_ctr_tx(uint32_t ep_id) {
static void handle_ctr_setup(uint32_t ep_id) {
uint16_t rx_count = btable_get_count(ep_id, BTABLE_BUF_RX);
- uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
- uint8_t setup_packet[8] TU_ATTR_ALIGNED(4);
+ uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
+ uint8_t setup_packet[8] TU_ATTR_ALIGNED(4);
- dcd_read_packet_memory(setup_packet, rx_addr, rx_count);
+ tu_hwfifo_read(PMA_BUF_AT(rx_addr), setup_packet, rx_count, NULL);
// Clear CTR RX if another setup packet arrived before this, it will be discarded
ep_write_clear_ctr(ep_id, TUSB_DIR_OUT);
// Setup packet should always be 8 bytes. If not, we probably missed the packet
if (rx_count == 8) {
- dcd_event_setup_received(0, (uint8_t*) setup_packet, true);
+ dcd_event_setup_received(0, (uint8_t *)setup_packet, true);
// Hardware should reset EP0 RX/TX to NAK and both toggle to 1
} else {
// Missed setup packet !!!
@@ -331,29 +302,30 @@ static void handle_ctr_setup(uint32_t ep_id) {
// Handle CTR interrupt for the RX/OUT direction
static void handle_ctr_rx(uint32_t ep_id) {
- uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
- uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
- bool const is_iso = ep_is_iso(ep_reg);
- xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
+ uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ const uint8_t ep_num = ep_reg & USB_EPADDR_FIELD;
+ const bool is_iso = ep_is_iso(ep_reg);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
uint8_t buf_id;
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
bool const dbl_buf = is_iso;
-#else
+ #else
bool const dbl_buf = false;
-#endif
+ #endif
if (dbl_buf) {
buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1;
} else {
buf_id = BTABLE_BUF_RX;
}
- uint16_t const rx_count = btable_get_count(ep_id, buf_id);
- uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id);
+ const uint16_t rx_count = btable_get_count(ep_id, buf_id);
+ uint16_t pma_addr = (uint16_t)btable_get_addr(ep_id, buf_id);
+ fsdev_pma_buf_t *pma_buf = PMA_BUF_AT(pma_addr);
if (xfer->ff) {
- dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count);
+ tu_hwfifo_read_to_fifo(pma_buf, xfer->ff, rx_count, NULL);
} else {
- dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count);
+ tu_hwfifo_read(pma_buf, xfer->buffer + xfer->queued_len, rx_count, NULL);
}
xfer->queued_len += rx_count;
@@ -371,7 +343,7 @@ static void handle_ctr_rx(uint32_t ep_id) {
} else {
// Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always
if (!is_iso) {
- uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ const uint16_t cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt);
}
ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_OUT); // will change RX Status, reserved other toggle bits
@@ -431,18 +403,18 @@ void dcd_int_handler(uint8_t rhport) {
// loop to handle all pending CTR interrupts
while (FSDEV_REG->ISTR & USB_ISTR_CTR) {
// skip DIR bit, and use CTR TX/RX instead, since there is chance we have both TX/RX completed in one interrupt
- uint32_t const ep_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID;
- uint32_t const ep_reg = ep_read(ep_id);
+ const uint32_t ep_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID;
+ const uint32_t ep_reg = ep_read(ep_id);
if (ep_reg & USB_EP_CTR_RX) {
- #ifdef FSDEV_BUS_32BIT
+ #ifdef FSDEV_BUS_32BIT
/* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
* https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
* From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers
* Description:
- * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses
- * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct.
- * Workaround:
+ * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM
+ * accesses have completed. If the software responds quickly to the interrupt, the full buffer contents may not be
+ * correct. Workaround:
* - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
* should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
* - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code
@@ -453,9 +425,9 @@ void dcd_int_handler(uint8_t rhport) {
*/
volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver
while (cycle_count > 0U) {
- cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
+ cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
}
- #endif
+ #endif
if (ep_reg & USB_EP_SETUP) {
handle_ctr_setup(ep_id); // CTR will be clear after copied setup packet
@@ -483,14 +455,13 @@ void dcd_int_handler(uint8_t rhport) {
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
+void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t *request) {
(void)rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
- request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS) {
- uint8_t const dev_addr = (uint8_t)request->wValue;
- FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr);
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ const uint8_t dev_addr = (uint8_t)request->wValue;
+ FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr);
}
edpt0_prepare_setup();
@@ -501,15 +472,14 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *req
* In case of double buffering, high 16bit is the address of 2nd buffer
* During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
*/
-static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
-{
- uint8_t blsize, num_block;
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) {
+ uint8_t blsize, num_block;
uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block);
- (void) blsize;
- (void) num_block;
+ (void)blsize;
+ (void)num_block;
uint32_t addr = ep_buf_ptr;
- ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
if (dbuf) {
addr |= ((uint32_t)ep_buf_ptr) << 16;
@@ -517,7 +487,7 @@ static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
}
// Verify packet buffer is not overflowed
- TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
+ TU_ASSERT(ep_buf_ptr <= CFG_TUSB_FSDEV_PMA_SIZE, 0xFFFF);
return addr;
}
@@ -525,35 +495,32 @@ static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
/***
* Allocate hardware endpoint
*/
-static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) {
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
// Check if already allocated
- if (ep_alloc_status[i].allocated[dir] &&
- ep_alloc_status[i].ep_type == ep_type &&
+ if (ep_alloc_status[i].allocated[dir] && ep_alloc_status[i].ep_type == ep_type &&
ep_alloc_status[i].ep_num == epnum) {
return i;
}
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
bool const dbl_buf = ep_type == TUSB_XFER_ISOCHRONOUS;
-#else
+ #else
bool const dbl_buf = false;
-#endif
+ #endif
// If EP of current direction is not allocated
// For double-buffered mode both directions needs to be free
- if (!ep_alloc_status[i].allocated[dir] &&
- (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) {
+ if (!ep_alloc_status[i].allocated[dir] && (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) {
// Check if EP number is the same
if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) {
// One EP pair has to be the same type
if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) {
- ep_alloc_status[i].ep_num = epnum;
- ep_alloc_status[i].ep_type = ep_type;
+ ep_alloc_status[i].ep_num = epnum;
+ ep_alloc_status[i].ep_type = ep_type;
ep_alloc_status[i].allocated[dir] = true;
return i;
@@ -567,16 +534,16 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
}
void edpt0_open(uint8_t rhport) {
- (void) rhport;
+ (void)rhport;
dcd_ep_alloc(0x0, TUSB_XFER_CONTROL);
dcd_ep_alloc(0x80, TUSB_XFER_CONTROL);
xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
- xfer_status[0][0].ep_idx = 0;
+ xfer_status[0][0].ep_idx = 0;
xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
- xfer_status[0][1].ep_idx = 0;
+ xfer_status[0][1].ep_idx = 0;
uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
@@ -594,13 +561,13 @@ void edpt0_open(uint8_t rhport) {
ep_write(0, ep_reg, false);
}
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
(void)rhport;
- uint8_t const ep_addr = desc_ep->bEndpointAddress;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- const uint16_t packet_size = tu_edpt_packet_size(desc_ep);
- uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer);
+ const uint8_t ep_addr = desc_ep->bEndpointAddress;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ const uint16_t packet_size = tu_edpt_packet_size(desc_ep);
+ const uint8_t ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer);
TU_ASSERT(ep_idx < FSDEV_EP_COUNT);
uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK;
@@ -624,9 +591,9 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
uint16_t pma_addr = dcd_pma_alloc(packet_size, false);
btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
xfer->max_packet_size = packet_size;
- xfer->ep_idx = ep_idx;
+ xfer->ep_idx = ep_idx;
ep_change_status(&ep_reg, dir, EP_STAT_NAK);
ep_change_dtog(&ep_reg, dir, 0);
@@ -650,8 +617,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
// Reset endpoint
ep_write(i, 0, false);
// Clear EP allocation status
- ep_alloc_status[i].ep_num = 0xFF;
- ep_alloc_status[i].ep_type = 0xFF;
+ ep_alloc_status[i].ep_num = 0xFF;
+ ep_alloc_status[i].ep_type = 0xFF;
ep_alloc_status[i].allocated[0] = false;
ep_alloc_status[i].allocated[1] = false;
}
@@ -665,46 +632,46 @@ void dcd_edpt_close_all(uint8_t rhport) {
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void)rhport;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
- uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS);
-#if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0
- uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
+ #if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0
+ uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
uint16_t pma_addr2 = pma_addr >> 16;
-#else
- uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false);
+ #else
+ uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false);
uint16_t pma_addr2 = pma_addr;
-#endif
+ #endif
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
btable_set_addr(ep_idx, 0, pma_addr);
btable_set_addr(ep_idx, 1, pma_addr2);
-#else
+ #else
btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
- (void) pma_addr2;
-#endif
+ (void)pma_addr2;
+ #endif
- xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
- xfer->ep_idx = ep_idx;
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer->ep_idx = ep_idx;
return true;
}
-bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
(void)rhport;
- uint8_t const ep_addr = desc_ep->bEndpointAddress;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
+ const uint8_t ep_addr = desc_ep->bEndpointAddress;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- uint8_t const ep_idx = xfer->ep_idx;
+ const uint8_t ep_idx = xfer->ep_idx;
xfer->max_packet_size = tu_edpt_packet_size(desc_ep);
uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK;
ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_TX | USB_EP_CTR_RX;
-#if FSDEV_USE_SBUF_ISO != 0
+ #if FSDEV_USE_SBUF_ISO != 0
ep_reg |= USB_EP_KIND;
ep_change_status(&ep_reg, dir, EP_STAT_DISABLED);
@@ -715,12 +682,12 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep)
} else {
ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX);
}
-#else
+ #else
ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED);
ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED);
ep_change_dtog(&ep_reg, dir, 0);
ep_change_dtog(&ep_reg, (tusb_dir_t)(1 - dir), 1);
-#endif
+ #endif
ep_write(ep_idx, ep_reg, true);
@@ -729,28 +696,29 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep)
// Currently, single-buffered, and only 64 bytes at a time (max)
static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
- uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
uint32_t ep_reg = ep_read(ep_ix) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR
- bool const is_iso = ep_is_iso(ep_reg);
+ const bool is_iso = ep_is_iso(ep_reg);
uint8_t buf_id;
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
bool const dbl_buf = is_iso;
-#else
+ #else
bool const dbl_buf = false;
-#endif
+ #endif
if (dbl_buf) {
buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0;
} else {
buf_id = BTABLE_BUF_TX;
}
- uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id);
+ uint16_t addr_ptr = (uint16_t)btable_get_addr(ep_ix, buf_id);
+ fsdev_pma_buf_t *pma_buf = PMA_BUF_AT(addr_ptr);
if (xfer->ff) {
- dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len);
+ tu_hwfifo_write_from_fifo(pma_buf, xfer->ff, len, NULL);
} else {
- dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len);
+ tu_hwfifo_write(pma_buf, &(xfer->buffer[xfer->queued_len]), len, NULL);
}
xfer->queued_len += len;
@@ -765,10 +733,10 @@ static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
}
static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
- (void) rhport;
+ (void)rhport;
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- uint8_t const ep_idx = xfer->ep_idx;
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ const uint8_t ep_idx = xfer->ep_idx;
if (dir == TUSB_DIR_IN) {
dcd_transmit_packet(xfer, ep_idx);
@@ -778,11 +746,11 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
uint16_t cnt = tu_min16(xfer->total_len, xfer->max_packet_size);
-#if FSDEV_USE_SBUF_ISO == 0
+ #if FSDEV_USE_SBUF_ISO == 0
bool const dbl_buf = ep_is_iso(ep_reg);
-#else
+ #else
bool const dbl_buf = false;
-#endif
+ #endif
if (dbl_buf) {
btable_set_rx_bufsize(ep_idx, 0, cnt);
btable_set_rx_bufsize(ep_idx, 1, cnt);
@@ -797,29 +765,29 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
return true;
}
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr) {
- (void) is_isr;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
+ (void)is_isr;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
xfer->queued_len = 0;
return edpt_xfer(rhport, ep_num, dir);
}
-bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes, bool is_isr) {
- (void) is_isr;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) {
+ (void)is_isr;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- xfer->buffer = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
xfer->queued_len = 0;
return edpt_xfer(rhport, ep_num, dir);
@@ -827,10 +795,10 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- uint8_t const ep_idx = xfer->ep_idx;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ const uint8_t ep_idx = xfer->ep_idx;
uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits
ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir);
@@ -842,10 +810,10 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- uint8_t const ep_idx = xfer->ep_idx;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ const uint8_t ep_idx = xfer->ep_idx;
uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits
ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir);
@@ -857,168 +825,22 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
ep_write(ep_idx, ep_reg, true);
}
-//--------------------------------------------------------------------+
-// PMA read/write
-//--------------------------------------------------------------------+
-
-// Write to packet memory area (PMA) from user memory
-// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
-// - Uses unaligned for RAM (since M0 cannot access unaligned address)
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) {
- if (nbytes == 0) return true;
- uint32_t n_write = nbytes / FSDEV_BUS_SIZE;
-
- fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(dst);
- const uint8_t *src8 = src;
-
- while (n_write--) {
- pma_buf->value = fsdevbus_unaligned_read(src8);
- src8 += FSDEV_BUS_SIZE;
- pma_buf++;
- }
-
- // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit
- uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1);
- if (odd) {
- fsdev_bus_t temp = 0;
- for(uint16_t i = 0; i < odd; i++) {
- temp |= *src8++ << (i * 8);
- }
- pma_buf->value = temp;
- }
-
- return true;
+void dcd_int_enable(uint8_t rhport) {
+ fsdev_int_enable(rhport);
}
-// Read from packet memory area (PMA) to user memory.
-// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
-// - Uses unaligned for RAM (since M0 cannot access unaligned address)
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) {
- if (nbytes == 0) return true;
- uint32_t n_read = nbytes / FSDEV_BUS_SIZE;
-
- fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(src);
- uint8_t *dst8 = (uint8_t *)dst;
-
- while (n_read--) {
- fsdevbus_unaligned_write(dst8, (fsdev_bus_t ) pma_buf->value);
- dst8 += FSDEV_BUS_SIZE;
- pma_buf++;
- }
-
- // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit
- uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1);
- if (odd) {
- fsdev_bus_t temp = pma_buf->value;
- while (odd--) {
- *dst8++ = (uint8_t) (temp & 0xfful);
- temp >>= 8;
- }
- }
-
- return true;
+void dcd_int_disable(uint8_t rhport) {
+ fsdev_int_disable(rhport);
}
-// Write to PMA from FIFO
-static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) {
- if (wNBytes == 0) return true;
-
- // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
- tu_fifo_buffer_info_t info;
- tu_fifo_get_read_info(ff, &info);
-
- uint16_t cnt_lin = tu_min16(wNBytes, info.linear.len);
- uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.wrapped.len);
- uint16_t const cnt_total = cnt_lin + cnt_wrap;
-
- // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
- // last lin byte will be combined with wrapped part To ensure PMA is always access aligned
- uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1);
- uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1);
- uint8_t const *src8 = (uint8_t const*) info.linear.ptr;
-
- // write even linear part
- dcd_write_packet_memory(dst, src8, lin_even);
- dst += lin_even;
- src8 += lin_even;
-
- if (lin_odd == 0) {
- src8 = (uint8_t const*) info.wrapped.ptr;
- } else {
- // Combine last linear bytes + first wrapped bytes to form fsdev bus width data
- fsdev_bus_t temp = 0;
- uint16_t i;
- for(i = 0; i < lin_odd; i++) {
- temp |= *src8++ << (i * 8);
- }
-
- src8 = (uint8_t const*) info.wrapped.ptr;
- for(; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) {
- temp |= *src8++ << (i * 8);
- }
-
- dcd_write_packet_memory(dst, &temp, FSDEV_BUS_SIZE);
- dst += FSDEV_BUS_SIZE;
- }
-
- // write the rest of the wrapped part
- dcd_write_packet_memory(dst, src8, cnt_wrap);
-
- tu_fifo_advance_read_pointer(ff, cnt_total);
- return true;
+ #if defined(USB_BCDR_DPPU) || defined(SYSCFG_PMC_USB_PU) || defined(EXTEN_USBD_PU_EN)
+void dcd_connect(uint8_t rhport) {
+ fsdev_connect(rhport);
}
-// Read from PMA to FIFO
-static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) {
- if (wNBytes == 0) return true;
-
- // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
- // Check for first linear part
- tu_fifo_buffer_info_t info;
- tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO
-
- uint16_t cnt_lin = tu_min16(wNBytes, info.linear.len);
- uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.wrapped.len);
- uint16_t cnt_total = cnt_lin + cnt_wrap;
-
- // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
- // last lin byte will be combined with wrapped part To ensure PMA is always access aligned
-
- uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1);
- uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1);
- uint8_t *dst8 = (uint8_t *) info.linear.ptr;
-
- // read even linear part
- dcd_read_packet_memory(dst8, src, lin_even);
- dst8 += lin_even;
- src += lin_even;
-
- if (lin_odd == 0) {
- dst8 = (uint8_t *) info.wrapped.ptr;
- } else {
- // Combine last linear bytes + first wrapped bytes to form fsdev bus width data
- fsdev_bus_t temp;
- dcd_read_packet_memory(&temp, src, FSDEV_BUS_SIZE);
- src += FSDEV_BUS_SIZE;
-
- uint16_t i;
- for (i = 0; i < lin_odd; i++) {
- *dst8++ = (uint8_t) (temp & 0xfful);
- temp >>= 8;
- }
-
- dst8 = (uint8_t *) info.wrapped.ptr;
- for (; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) {
- *dst8++ = (uint8_t) (temp & 0xfful);
- temp >>= 8;
- }
- }
-
- // read the rest of the wrapped part
- dcd_read_packet_memory(dst8, src, cnt_wrap);
-
- tu_fifo_advance_write_pointer(ff, cnt_total);
- return true;
+void dcd_disconnect(uint8_t rhport) {
+ fsdev_disconnect(rhport);
}
+ #endif
#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_at32.h b/src/portable/st/stm32_fsdev/fsdev_at32.h
index deb1de2a8..e75430396 100644
--- a/src/portable/st/stm32_fsdev/fsdev_at32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_at32.h
@@ -35,7 +35,6 @@
#endif
-#define FSDEV_PMA_SIZE (512u)
#define FSDEV_USE_SBUF_ISO 0
#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
@@ -150,8 +149,6 @@
#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"
-
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -168,7 +165,7 @@ enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
#error "Unsupported MCU"
#endif
-void dcd_int_enable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_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
@@ -187,7 +184,7 @@ void dcd_int_enable(uint8_t rhport) {
}
}
-void dcd_int_disable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_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
@@ -206,20 +203,20 @@ void dcd_int_disable(uint8_t rhport) {
}
}
-void dcd_disconnect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
(void) rhport;
/* disable usb phy */
- FSDEV_REG->CNTR |= USB_CNTR_PDWN;
+ *(volatile uint32_t*)(FSDEV_REG_BASE + 0x40) |= USB_CNTR_PDWN;
/* D+ 1.5k pull-up disable, USB->cfg_bit.puo = TRUE; */
- *(uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1);
+ *(volatile uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1);
}
-void dcd_connect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
(void) rhport;
/* enable usb phy */
- FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
+ *(volatile uint32_t*)(FSDEV_REG_BASE + 0x40) &= ~USB_CNTR_PDWN;
/* Dp 1.5k pull-up enable, USB->cfg_bit.puo = 0; */
- *(uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1);
+ *(volatile uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1);
}
#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h
index ceebb6dab..37ea7808e 100644
--- a/src/portable/st/stm32_fsdev/fsdev_ch32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h
@@ -53,7 +53,6 @@
#pragma GCC diagnostic pop
#endif
-#define FSDEV_PMA_SIZE (512u)
#define FSDEV_USE_SBUF_ISO 0
#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
@@ -168,7 +167,6 @@
#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */
#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
-
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -184,26 +182,26 @@ enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
#error "Unsupported MCU"
#endif
-void dcd_int_enable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) {
(void)rhport;
for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
NVIC_EnableIRQ(fsdev_irq[i]);
}
}
-void dcd_int_disable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) {
(void)rhport;
for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
NVIC_DisableIRQ(fsdev_irq[i]);
}
}
-void dcd_disconnect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
(void) rhport;
EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN;
}
-void dcd_connect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
(void) rhport;
EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN;
}
diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c
new file mode 100644
index 000000000..4f127ae86
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_common.c
@@ -0,0 +1,116 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ * Copyright (c) 2025, HiFiPhile (Zixun LI)
+ *
+ * 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 defined(TUP_USBIP_FSDEV) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+
+#include "fsdev_common.h"
+
+//--------------------------------------------------------------------+
+// Global
+//--------------------------------------------------------------------+
+
+// Reset the USB Core
+void fsdev_core_reset(void) {
+ // Perform USB peripheral reset
+ FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
+ asm("NOP");
+ }
+
+ FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
+
+ // Wait startup time, for F042 and F070, this is <= 1 us.
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
+ asm("NOP");
+ }
+
+ // Clear pending interrupts
+ FSDEV_REG->ISTR = 0;
+}
+
+// De-initialize the USB Core
+void fsdev_deinit(void) {
+ // Disable all interrupts and force USB reset
+ FSDEV_REG->CNTR = USB_CNTR_FRES;
+
+ // Clear pending interrupts
+ FSDEV_REG->ISTR = 0;
+
+ // Put USB peripheral in power down mode
+ FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
+ asm("NOP");
+ }
+}
+
+//--------------------------------------------------------------------+
+// BTable Helper
+//--------------------------------------------------------------------+
+
+// Aligned buffer size according to hardware
+uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) {
+ /* The STM32 full speed USB peripheral supports only a limited set of
+ * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
+ uint16_t block_in_bytes;
+ if (size > 62) {
+ block_in_bytes = 32;
+ *blsize = 1;
+ *num_block = tu_div_ceil(size, 32);
+ } else {
+ block_in_bytes = 2;
+ *blsize = 0;
+ *num_block = tu_div_ceil(size, 2);
+ }
+
+ return (*num_block) * block_in_bytes;
+}
+
+// Set RX buffer size
+void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) {
+ uint8_t blsize, num_block;
+ (void) pma_align_buffer_size(wCount, &blsize, &num_block);
+
+ /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
+ uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10);
+ if (bl_nb == 0) {
+ // zlp but 0 is invalid value, set blsize to 1 (32 bytes)
+ // Note: lower value can cause PMAOVR on setup with ch32v203
+ bl_nb = 1 << 15;
+ }
+
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb;
+#endif
+}
+
+#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_common.h
index cf36576bb..b749a92ff 100644
--- a/src/portable/st/stm32_fsdev/fsdev_type.h
+++ b/src/portable/st/stm32_fsdev/fsdev_common.h
@@ -1,8 +1,9 @@
/*
* The MIT License (MIT)
*
- * Copyright(c) N Conrad
- * Copyright(c) 2024, hathach (tinyusb.org)
+ * Copyright (c) N Conrad
+ * Copyright (c) 2024, hathach (tinyusb.org)
+ * Copyright (c) 2025, HiFiPhile (Zixun LI)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,53 +26,65 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef TUSB_FSDEV_TYPE_H
-#define TUSB_FSDEV_TYPE_H
+#ifndef TUSB_FSDEV_COMMON_H
+#define TUSB_FSDEV_COMMON_H
-#ifdef __cplusplus
- extern "C" {
+#include "common/tusb_common.h"
+
+#if CFG_TUD_ENABLED
+ #include "device/dcd.h"
+#endif
+
+#if CFG_TUH_ENABLED
+ #include "host/hcd.h"
+#endif
+
+#if defined(TUP_USBIP_FSDEV_STM32)
+ #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
-#include "stdint.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
// Both of these MUST be a multiple of 2, and are in byte units.
#ifndef FSDEV_BTABLE_BASE
-#define FSDEV_BTABLE_BASE 0U
+ #define FSDEV_BTABLE_BASE 0U
#endif
TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes");
-// FSDEV_PMA_SIZE is PMA buffer size in bytes.
+// CFG_TUSB_FSDEV_PMA_SIZE is PMA buffer size in bytes.
// - 512-byte devices, access with a stride of two words (use every other 16-bit address)
-// - 1024-byte devices, access with a stride of one word (use every 16-bit address)
+// - 1024-byte devices, access with a stride of one word (use every 16-bit address) or 32-bit address
// - 2048-byte devices, access with 32-bit address
-
-// For purposes of accessing the packet
-#if FSDEV_PMA_SIZE == 512
- // 1x16 bit / word access scheme
- #define FSDEV_PMA_STRIDE 2
- #define pma_access_scheme TU_ATTR_ALIGNED(4)
-#elif FSDEV_PMA_SIZE == 1024
- // 2x16 bit / word access scheme
- #define FSDEV_PMA_STRIDE 1
- #define pma_access_scheme
-#elif FSDEV_PMA_SIZE == 2048
+#if CFG_TUSB_FSDEV_PMA_SIZE == 2048 || TU_CHECK_MCU(OPT_MCU_STM32U0)
// 32 bit access scheme
#define FSDEV_BUS_32BIT
- #define FSDEV_PMA_STRIDE 1
+ #define FSDEV_PMA_STRIDE 1
#define pma_access_scheme
+#elif CFG_TUSB_FSDEV_PMA_SIZE == 1024
+ // 2x16 bit / word access scheme
+ #define FSDEV_PMA_STRIDE 1
+ #define pma_access_scheme
+#elif CFG_TUSB_FSDEV_PMA_SIZE == 512
+ // 1x16 bit / word access scheme
+ #define FSDEV_PMA_STRIDE 2
+ #define pma_access_scheme TU_ATTR_ALIGNED(4)
#endif
// The fsdev_bus_t type can be used for both register and PMA access necessities
#ifdef FSDEV_BUS_32BIT
- typedef uint32_t fsdev_bus_t;
- #define fsdevbus_unaligned_read(_addr) tu_unaligned_read32(_addr)
- #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write32(_addr, _value)
+typedef uint32_t fsdev_bus_t;
#else
- typedef uint16_t fsdev_bus_t;
- #define fsdevbus_unaligned_read(_addr) tu_unaligned_read16(_addr)
- #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write16(_addr, _value)
+typedef uint16_t fsdev_bus_t;
#endif
enum {
@@ -105,74 +118,77 @@ typedef union {
} ep32[FSDEV_EP_COUNT][2];
} fsdev_btable_t;
-TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct");
-TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");
+TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT * 8 * FSDEV_PMA_STRIDE, "size is not correct");
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT * 8 <= CFG_TUSB_FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");
-#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(FSDEV_BTABLE_BASE)))
+#define FSDEV_BTABLE ((volatile fsdev_btable_t *)(FSDEV_PMA_BASE + FSDEV_PMA_STRIDE * (FSDEV_BTABLE_BASE)))
typedef struct {
volatile pma_access_scheme fsdev_bus_t value;
} fsdev_pma_buf_t;
-#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(_addr)))
+#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t *)(FSDEV_PMA_BASE + FSDEV_PMA_STRIDE * (_addr)))
//--------------------------------------------------------------------+
// Registers Typedef
//--------------------------------------------------------------------+
// volatile 32-bit aligned
-#define _va32 volatile TU_ATTR_ALIGNED(4)
+#define _va32 volatile TU_ATTR_ALIGNED(4)
typedef struct {
struct {
_va32 fsdev_bus_t reg;
- }ep[FSDEV_EP_COUNT];
+ } ep[FSDEV_EP_COUNT];
- _va32 uint32_t RESERVED7[8]; // Reserved
- _va32 fsdev_bus_t CNTR; // 40: Control register
- _va32 fsdev_bus_t ISTR; // 44: Interrupt status register
- _va32 fsdev_bus_t FNR; // 48: Frame number register
- _va32 fsdev_bus_t DADDR; // 4C: Device address register
- _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only)
- _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only)
- _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only)
+ _va32 uint32_t RESERVED7[8]; // Reserved
+ _va32 fsdev_bus_t CNTR; // 40: Control register
+ _va32 fsdev_bus_t ISTR; // 44: Interrupt status register
+ _va32 fsdev_bus_t FNR; // 48: Frame number register
+ _va32 fsdev_bus_t DADDR; // 4C: Device address register
+ _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only)
+ _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only)
+ _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only)
} fsdev_regs_t;
TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset");
TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct");
-#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE)
+#define FSDEV_REG ((fsdev_regs_t *)FSDEV_REG_BASE)
#ifndef USB_EPTX_STAT
-#define USB_EPTX_STAT 0x0030U
+ #define USB_EPTX_STAT 0x0030U
#endif
#ifndef USB_EPRX_STAT
-#define USB_EPRX_STAT 0x3000U
+ #define USB_EPRX_STAT 0x3000U
#endif
#ifndef USB_EPTX_STAT_Pos
-#define USB_EPTX_STAT_Pos 4u
+ #define USB_EPTX_STAT_Pos 4u
#endif
#ifndef USB_EP_DTOG_TX_Pos
-#define USB_EP_DTOG_TX_Pos 6u
+ #define USB_EP_DTOG_TX_Pos 6u
#endif
#ifndef USB_EP_CTR_TX_Pos
-#define USB_EP_CTR_TX_Pos 7u
+ #define USB_EP_CTR_TX_Pos 7u
#endif
typedef enum {
EP_STAT_DISABLED = 0,
- EP_STAT_STALL = 1,
- EP_STAT_NAK = 2,
- EP_STAT_VALID = 3
-}ep_stat_t;
+ EP_STAT_STALL = 1,
+ EP_STAT_NAK = 2,
+ EP_STAT_VALID = 3
+} ep_stat_t;
+
+#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
-#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
-#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+#define CH_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0)))
+#define CH_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0)))
//--------------------------------------------------------------------+
// Endpoint Helper
@@ -186,13 +202,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) {
TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) {
if (need_exclusive) {
- dcd_int_disable(0);
+ fsdev_int_disable(0);
}
- FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value;
+ FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t)value;
if (need_exclusive) {
- dcd_int_enable(0);
+ fsdev_int_enable(0);
}
}
@@ -204,11 +220,11 @@ TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb
ep_write(ep_id, reg, false);
}
-TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) {
+TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t *reg, tusb_dir_t dir, ep_stat_t state) {
*reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
}
-TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) {
+TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t *reg, tusb_dir_t dir, uint8_t state) {
*reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
}
@@ -217,6 +233,35 @@ TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) {
}
//--------------------------------------------------------------------+
+// Channel Helper
+// - Direction is opposite to endpoint direction
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ch_read(uint32_t ch_id) {
+ return ep_read(ch_id);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ch_write(uint32_t ch_id, uint32_t value, bool need_exclusive) {
+ ep_write(ch_id, value, need_exclusive);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ch_write_clear_ctr(uint32_t ch_id, tusb_dir_t dir) {
+ uint32_t reg = FSDEV_REG->ep[ch_id].reg;
+ reg |= USB_EP_CTR_TX | USB_EP_CTR_RX;
+ reg &= USB_EPREG_MASK;
+ reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
+ ep_write(ch_id, reg, false);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ch_change_status(uint32_t *reg, tusb_dir_t dir, ep_stat_t state) {
+ *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ch_change_dtog(uint32_t *reg, tusb_dir_t dir, uint8_t state) {
+ *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
+}
+
+//--------------------------------------------------------------------+
// BTable Helper
//--------------------------------------------------------------------+
@@ -230,8 +275,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uin
TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) {
#ifdef FSDEV_BUS_32BIT
- uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
- count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
#else
FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr;
@@ -250,58 +295,30 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, ui
TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) {
#ifdef FSDEV_BUS_32BIT
- uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
- count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
#else
- uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
- cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
+ uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+ cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt;
#endif
}
-/* Aligned buffer size according to hardware */
-TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) {
- /* The STM32 full speed USB peripheral supports only a limited set of
- * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
- uint16_t block_in_bytes;
- if (size > 62) {
- block_in_bytes = 32;
- *blsize = 1;
- *num_block = tu_div_ceil(size, 32);
- } else {
- block_in_bytes = 2;
- *blsize = 0;
- *num_block = tu_div_ceil(size, 2);
- }
-
- return (*num_block) * block_in_bytes;
-}
+// Reset the USB Core
+void fsdev_core_reset(void);
-TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) {
- uint8_t blsize, num_block;
- (void) pma_align_buffer_size(wCount, &blsize, &num_block);
+// De-initialize the USB Core
+void fsdev_deinit(void);
- /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
- uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10);
- if (bl_nb == 0) {
- // zlp but 0 is invalid value, set blsize to 1 (32 bytes)
- // Note: lower value can cause PMAOVR on setup with ch32v203
- bl_nb = 1 << 15;
- }
-
-#ifdef FSDEV_BUS_32BIT
- uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
- count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu);
- FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
-#else
- FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb;
-#endif
+// Aligned buffer size according to hardware
+uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_block);
-}
+// Set RX buffer size
+void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount);
#ifdef __cplusplus
- }
+}
#endif
-#endif
+#endif /* TUSB_FSDEV_COMMON_H */
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index e7a57aca0..02dba05a6 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -32,10 +32,23 @@
#ifndef TUSB_FSDEV_STM32_H
#define TUSB_FSDEV_STM32_H
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0
+#if CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #include "stm32c0xx.h"
+ #define FSDEV_HAS_SBUF_ISO 1
+ #define USB USB_DRD_FS
+ #define USB_EP_CTR_RX USB_CHEP_VTRX
+ #define USB_EP_CTR_TX USB_CHEP_VTTX
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F0
#include "stm32f0xx.h"
- #define FSDEV_PMA_SIZE (1024u)
- #define FSDEV_REG_BASE USB_BASE
+ #define FSDEV_REG_BASE USB_BASE
#define FSDEV_HAS_SBUF_ISO 0
// F0x2 models are crystal-less
// All have internal D+ pull-up
@@ -44,29 +57,24 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
- #define FSDEV_PMA_SIZE (512u)
#define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *B, and *C: 2 x 16 bits/word
// F1 names this differently from the rest
- #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
+ #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
-#elif defined(STM32F302xB) || defined(STM32F302xC) || \
- defined(STM32F303xB) || defined(STM32F303xC) || \
- defined(STM32F373xC)
+#elif defined(STM32F302xB) || defined(STM32F302xC) || defined(STM32F303xB) || defined(STM32F303xC) || \
+ defined(STM32F373xC)
#include "stm32f3xx.h"
- #define FSDEV_PMA_SIZE (512u)
#define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *B, and *C: 1 x 16 bits/word
// PMA dedicated to USB (no sharing with CAN)
-#elif defined(STM32F302x6) || defined(STM32F302x8) || \
- defined(STM32F302xD) || defined(STM32F302xE) || \
- defined(STM32F303xD) || defined(STM32F303xE)
+#elif defined(STM32F302x6) || defined(STM32F302x8) || defined(STM32F302xD) || defined(STM32F302xE) || \
+ defined(STM32F303xD) || defined(STM32F303xE)
#include "stm32f3xx.h"
- #define FSDEV_PMA_SIZE (1024u)
#define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
@@ -74,28 +82,32 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
#include "stm32l0xx.h"
- #define FSDEV_PMA_SIZE (1024u)
#define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
- #define FSDEV_PMA_SIZE (512u)
#define FSDEV_HAS_SBUF_ISO 0
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- #include "stm32g4xx.h"
- #define FSDEV_PMA_SIZE (1024u)
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
+ #include "stm32l4xx.h"
+ #define FSDEV_HAS_SBUF_ISO 0
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+ #include "stm32l5xx.h"
#define FSDEV_HAS_SBUF_ISO 0
+ #ifndef USB_PMAADDR
+ #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
+ #endif
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
#include "stm32g0xx.h"
- #define FSDEV_PMA_SIZE (2048u)
- #define FSDEV_HAS_SBUF_ISO 1
- #define USB USB_DRD_FS
+ #define FSDEV_HAS_SBUF_ISO 1
+ #define USB USB_DRD_FS
- #define USB_EP_CTR_RX USB_EP_VTRX
- #define USB_EP_CTR_TX USB_EP_VTTX
- #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
#define USB_EPREG_MASK USB_CHEP_REG_MASK
#define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
#define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
@@ -110,78 +122,20 @@
#define USB_ISTR_EP_ID USB_ISTR_IDN
#define USB_EPADDR_FIELD USB_CHEP_ADDR
#define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
- #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
-#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
- #include "stm32c0xx.h"
- #define FSDEV_PMA_SIZE (2048u)
- #define FSDEV_HAS_SBUF_ISO 1
- #define USB USB_DRD_FS
- #define USB_EP_CTR_RX USB_CHEP_VTRX
- #define USB_EP_CTR_TX USB_CHEP_VTTX
- #define USB_EPREG_MASK USB_CHEP_REG_MASK
- #define USB_CNTR_FRES USB_CNTR_USBRST
- #define USB_CNTR_RESUME USB_CNTR_L2RES
- #define USB_ISTR_EP_ID USB_ISTR_IDN
- #define USB_EPADDR_FIELD USB_CHEP_ADDR
- #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
- #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
+ #include "stm32g4xx.h"
+ #define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
- #define FSDEV_PMA_SIZE (2048u)
#define FSDEV_HAS_SBUF_ISO 1
- #define USB USB_DRD_FS
+ #define USB USB_DRD_FS
- #define USB_EP_CTR_RX USB_EP_VTRX
- #define USB_EP_CTR_TX USB_EP_VTTX
- #define USB_EP_T_FIELD USB_CHEP_UTYPE
- #define USB_EPREG_MASK USB_CHEP_REG_MASK
- #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
- #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
- #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
- #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
- #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
- #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
- #define USB_EPRX_STAT USB_CH_RX_VALID
- #define USB_EPKIND_MASK USB_EP_KIND_MASK
- #define USB_CNTR_FRES USB_CNTR_USBRST
- #define USB_CNTR_RESUME USB_CNTR_L2RES
- #define USB_ISTR_EP_ID USB_ISTR_IDN
- #define USB_EPADDR_FIELD USB_CHEP_ADDR
- #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
- #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- #include "stm32wbxx.h"
- #define FSDEV_PMA_SIZE (1024u)
- #define FSDEV_HAS_SBUF_ISO 0
- /* ST provided header has incorrect value of USB_PMAADDR */
- #define FSDEV_PMA_BASE USB1_PMAADDR
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
- #include "stm32l4xx.h"
- #define FSDEV_PMA_SIZE (1024u)
- #define FSDEV_HAS_SBUF_ISO 0
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
- #include "stm32l5xx.h"
- #define FSDEV_PMA_SIZE (1024u)
- #define FSDEV_HAS_SBUF_ISO 0
-
- #ifndef USB_PMAADDR
- #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
- #endif
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
- #include "stm32u5xx.h"
- #define FSDEV_PMA_SIZE (2048u)
- #define FSDEV_HAS_SBUF_ISO 1
- #define USB USB_DRD_FS
-
- #define USB_EP_CTR_RX USB_EP_VTRX
- #define USB_EP_CTR_TX USB_EP_VTTX
- #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
#define USB_EPREG_MASK USB_CHEP_REG_MASK
#define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
#define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
@@ -200,7 +154,6 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
#include "stm32u0xx.h"
- #define FSDEV_PMA_SIZE (1024u)
#define FSDEV_BUS_32BIT
#define FSDEV_HAS_SBUF_ISO 1
#define USB USB_DRD_FS
@@ -226,10 +179,9 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
#include "stm32u3xx.h"
- #define FSDEV_PMA_SIZE (2048u)
#define FSDEV_BUS_32BIT
#define FSDEV_HAS_SBUF_ISO 1 // This is assumed to work but has not been tested...
- #define USB USB_DRD_FS
+ #define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_EP_VTRX
#define USB_EP_CTR_TX USB_EP_VTTX
@@ -240,15 +192,45 @@
#define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
#define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
#define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
- #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
- #define USB_EPRX_STAT USB_CH_RX_VALID
- #define USB_EPKIND_MASK USB_EP_KIND_MASK
- #define USB_CNTR_FRES USB_CNTR_USBRST
- #define USB_CNTR_RESUME USB_CNTR_L2RES
- #define USB_ISTR_EP_ID USB_ISTR_IDN
- #define USB_EPADDR_FIELD USB_CHEP_ADDR
- #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
- #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ #include "stm32u5xx.h"
+ #define FSDEV_HAS_SBUF_ISO 1
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+ #include "stm32wbxx.h"
+ #define FSDEV_HAS_SBUF_ISO 0
+ /* ST provided header has incorrect value of USB_PMAADDR */
+ #define FSDEV_PMA_BASE USB1_PMAADDR
#else
#error You are using an untested or unimplemented STM32 variant. Please update the driver.
@@ -299,7 +281,7 @@
// - Enable double buffering on devices with >1KB Packet Memory Area (PMA)
// to improve isochronous transfer reliability and performance
// - Disable on devices with limited PMA to conserve memory space
- #if FSDEV_PMA_SIZE > 1024u
+ #if CFG_TUSB_FSDEV_PMA_SIZE > 1024u
#define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1
#else
#define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0
@@ -371,7 +353,7 @@ static const IRQn_Type fsdev_irq[] = {
};
enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
-void dcd_int_enable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) {
(void)rhport;
// forces write to RAM before allowing ISR to execute
@@ -394,7 +376,7 @@ void dcd_int_enable(uint8_t rhport) {
}
}
-void dcd_int_disable(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) {
(void)rhport;
#if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
@@ -419,28 +401,27 @@ void dcd_int_disable(uint8_t rhport) {
// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
#if defined(USB_BCDR_DPPU)
-void dcd_disconnect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
(void)rhport;
USB->BCDR &= ~(USB_BCDR_DPPU);
}
-void dcd_connect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
(void)rhport;
USB->BCDR |= USB_BCDR_DPPU;
}
#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
-void dcd_disconnect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
(void)rhport;
SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
}
-void dcd_connect(uint8_t rhport) {
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
(void)rhport;
SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
}
#endif
-
#endif /* TUSB_FSDEV_STM32_H */
diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c
new file mode 100644
index 000000000..1813ef70b
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c
@@ -0,0 +1,882 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 HiFiPhile (Zixun LI)
+ *
+ * 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.
+ */
+
+/**********************************************
+ * This driver provides USB Host controller support for STM32 MCUs with "USB A"/"PCD"/"HCD" peripheral.
+ * This covers these MCU families:
+ *
+ * C0 2048 byte buffer; 32-bit bus; host mode
+ * G0 2048 byte buffer; 32-bit bus; host mode
+ * U3 2048 byte buffer; 32-bit bus; host mode
+ * H5 2048 byte buffer; 32-bit bus; host mode
+ * U535, U545 2048 byte buffer; 32-bit bus; host mode
+ *
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV) && \
+ TU_CHECK_MCU(OPT_MCU_STM32C0, OPT_MCU_STM32G0, OPT_MCU_STM32H5, OPT_MCU_STM32U5)
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "fsdev_common.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// Debug level for FSDEV
+#define FSDEV_DEBUG 3
+
+// Max number of endpoints application can open, can be larger than FSDEV_EP_COUNT
+#ifndef CFG_TUH_FSDEV_ENDPOINT_MAX
+ #define CFG_TUH_FSDEV_ENDPOINT_MAX 16u
+#endif
+
+TU_VERIFY_STATIC(CFG_TUH_FSDEV_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32H5
+ #define CPU_FREQUENCY_MHZ 250U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ #define CPU_FREQUENCY_MHZ 160U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
+ #define CPU_FREQUENCY_MHZ 96U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #define CPU_FREQUENCY_MHZ 64U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #define CPU_FREQUENCY_MHZ 48U
+#else
+ #error "CPU_FREQUENCY_MHZ not defined for this STM32 MCU"
+#endif
+
+enum {
+ HCD_XFER_ERROR_MAX = 3,
+ HCD_XFER_NAK_MAX = 15,
+ HCD_XFER_NAK_DEFAULT = 3,
+};
+
+// Host driver struct for each opened endpoint
+typedef struct {
+ uint8_t *buffer;
+ uint16_t buflen;
+ uint16_t queued_len;
+ uint16_t max_packet_size;
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t ep_type;
+ uint8_t interval;
+ struct TU_ATTR_PACKED {
+ uint8_t ls_pre : 1;
+ uint8_t allocated : 1;
+ uint8_t next_setup : 1;
+ uint8_t pid : 1;
+ };
+} hcd_endpoint_t;
+
+// Channel direction state
+typedef struct {
+ hcd_endpoint_t* edpt;
+ struct TU_ATTR_PACKED {
+ uint8_t allocated : 1;
+ uint8_t retry : 3;
+ uint8_t nak : 4; // Max NAK count in current frame
+ };
+} hcd_channel_dir_t;
+
+// Additional info for each channel when it is active
+typedef struct {
+ uint8_t dev_addr;
+ uint8_t ep_num;
+ uint8_t ep_type;
+ hcd_channel_dir_t out, in;
+} hcd_channel_t;
+
+static struct {
+ hcd_channel_t channel[FSDEV_EP_COUNT];
+ hcd_endpoint_t edpt[CFG_TUH_FSDEV_ENDPOINT_MAX];
+ bool connected;
+} _hcd_data;
+
+static tuh_configure_fsdev_t _tuh_cfg = {
+ .max_nak = HCD_XFER_NAK_DEFAULT,
+};
+
+//--------------------------------------------------------------------+
+// Prototypes
+//--------------------------------------------------------------------+
+
+static uint8_t endpoint_alloc(void);
+static uint8_t endpoint_find(uint8_t dev_addr, uint8_t ep_addr);
+static uint32_t hcd_pma_alloc(uint8_t channel, tusb_dir_t dir, uint16_t len);
+static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type);
+static bool edpt_xfer_kickoff(uint8_t ep_id);
+static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir);
+static void edpoint_close(uint8_t ep_id);
+static void port_status_handler(uint8_t rhport, bool in_isr);
+static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
+static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
+static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
+static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline void endpoint_dealloc(hcd_endpoint_t* edpt) {
+ edpt->allocated = 0;
+}
+
+static inline void channel_dealloc(hcd_channel_t* ch, tusb_dir_t dir) {
+ if (dir == TUSB_DIR_OUT) {
+ ch->out.allocated = 0;
+ } else {
+ ch->in.allocated = 0;
+ }
+}
+
+// Write channel state in specified direction
+static inline void channel_write_status(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir, ep_stat_t state, bool need_exclusive) {
+ ch_reg &= USB_EPREG_MASK | CH_STAT_MASK(dir);
+ ch_change_status(&ch_reg, dir, state);
+ ch_write(ch_id, ch_reg, need_exclusive);
+}
+
+static inline uint16_t channel_get_rx_count(uint8_t ch_id) {
+ /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
+ * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
+ * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers
+ * Description:
+ * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses
+ * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct.
+ * Workaround:
+ * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
+ * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
+ *
+ * Note: this errata may also apply to G0, U5, H5 etc.
+ *
+ * We choose the delay count based on max CPU frequency (in MHz) to ensure the delay is at least the required time.
+ */
+
+ uint32_t ch_reg = ch_read(ch_id);
+ if (FSDEV_REG->ISTR & USB_ISTR_LS_DCONN || ch_reg & USB_CHEP_LSEP) {
+ // Low speed mode: 6.4 us delay -> about 2 cycles per MHz
+ volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ * 2U;
+ while (cycle_count > 0U) {
+ cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
+ }
+ } else {
+ // Full speed mode: 800 ns delay -> about 0.25 cycles per MHz
+ volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U;
+ while (cycle_count > 0U) {
+ cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
+ }
+ }
+
+ return btable_get_count(ch_id, BTABLE_BUF_RX);
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// Optional HCD configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ TU_VERIFY(cfg_id == TUH_CFGID_FSDEV && cfg_param != NULL);
+
+ tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
+ _tuh_cfg.max_nak = tu_min8(cfg->fsdev.max_nak, HCD_XFER_NAK_MAX);
+ return true;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+
+ fsdev_core_reset();
+
+ FSDEV_REG->CNTR = USB_CNTR_HOST; // Enable USB in Host mode
+
+ tu_memclr(&_hcd_data, sizeof(_hcd_data));
+
+ // Clear pending interrupts
+ // Normally no interrupts should be pending here since we just reset the core,
+ // but device mode suspend needs to cleared by WKUP flag
+ FSDEV_REG->ISTR = 0;
+
+ // Enable interrupts for host mode
+ FSDEV_REG->CNTR |= USB_CNTR_DCON | USB_CNTR_CTRM | USB_CNTR_SOFM | USB_CNTR_ERRM | USB_CNTR_PMAOVRM;
+
+ // Initialize port state
+ _hcd_data.connected = false;
+
+ fsdev_connect(rhport);
+
+ // If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt
+ if (FSDEV_REG->ISTR & USB_ISTR_DCON_STAT) {
+ // Wait DP/DM stabilize time
+ volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U;
+ while (cycle_count > 0U) {
+ cycle_count--;
+ }
+ port_status_handler(rhport, false);
+ }
+
+ return true;
+}
+
+bool hcd_deinit(uint8_t rhport) {
+ (void)rhport;
+
+ fsdev_disconnect(rhport);
+
+ fsdev_deinit();
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Interrupt Helper Functions
+//--------------------------------------------------------------------+
+
+static inline void sof_handler(void) {
+ // Reset NAK counters for all active channels
+ for (uint8_t ch_id = 0; ch_id < FSDEV_EP_COUNT; ch_id++) {
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ if (channel->out.allocated) {
+ channel->out.nak = 0;
+ }
+ if (channel->in.allocated) {
+ channel->in.nak = 0;
+ }
+ }
+}
+
+static void port_status_handler(uint8_t rhport, bool in_isr) {
+ uint32_t const fnr_reg = FSDEV_REG->FNR;
+ uint32_t const istr_reg = FSDEV_REG->ISTR;
+ // SE0 detected USB Disconnected state
+ if ((fnr_reg & (USB_FNR_RXDP | USB_FNR_RXDM)) == 0U) {
+ _hcd_data.connected = false;
+ hcd_event_device_remove(rhport, in_isr);
+ return;
+ }
+
+ if (!_hcd_data.connected) {
+ // J-state or K-state detected & LastState=Disconnected
+ if (((fnr_reg & USB_FNR_RXDP) != 0U) || ((istr_reg & USB_ISTR_LS_DCONN) != 0U)) {
+ _hcd_data.connected = true;
+ hcd_event_device_attach(rhport, in_isr);
+ }
+ } else {
+ // J-state or K-state detected & lastState=Connected: a Missed disconnection is detected
+ if (((fnr_reg & USB_FNR_RXDP) != 0U) || ((istr_reg & USB_ISTR_LS_DCONN) != 0U)) {
+ _hcd_data.connected = false;
+ hcd_event_device_remove(rhport, in_isr);
+ }
+ }
+}
+
+// Handle ACK response
+static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
+ uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
+ uint8_t const daddr = (ch_reg & USB_CHEP_DEVADDR_Msk) >> USB_CHEP_DEVADDR_Pos;
+
+ uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
+ if (ep_id == TUSB_INDEX_INVALID_8) {
+ return;
+ }
+
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ hcd_channel_t *channel = &_hcd_data.channel[ch_id];
+
+ if (dir == TUSB_DIR_OUT) {
+ // OUT/TX direction
+ if (edpt->buflen != edpt->queued_len) {
+ // More data to send
+ uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
+ uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_TX);
+ tu_hwfifo_write(PMA_BUF_AT(pma_addr), &(edpt->buffer[edpt->queued_len]), len, NULL);
+ btable_set_count(ch_id, BTABLE_BUF_TX, len);
+ edpt->queued_len += len;
+ channel_write_status(ch_id, ch_reg, TUSB_DIR_OUT, EP_STAT_VALID, false);
+ channel->out.nak = 0;
+ } else {
+ // Transfer complete
+ channel_dealloc(channel, TUSB_DIR_OUT);
+ edpt->pid = (ch_reg & USB_CHEP_DTOG_TX) ? 1 : 0;
+ hcd_event_xfer_complete(daddr, ep_num, edpt->queued_len, XFER_RESULT_SUCCESS, true);
+ }
+ } else {
+ // IN/RX direction
+ uint16_t const rx_count = channel_get_rx_count(ch_id);
+ uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_RX);
+ tu_hwfifo_read(PMA_BUF_AT(pma_addr), edpt->buffer + edpt->queued_len, rx_count, NULL);
+ edpt->queued_len += rx_count;
+
+ if ((rx_count < edpt->max_packet_size) || (edpt->queued_len >= edpt->buflen)) {
+ // Transfer complete (short packet or all bytes received)
+ channel_dealloc(channel, TUSB_DIR_IN);
+ edpt->pid = (ch_reg & USB_CHEP_DTOG_RX) ? 1 : 0;
+ hcd_event_xfer_complete(daddr, ep_num | TUSB_DIR_IN_MASK, edpt->queued_len, XFER_RESULT_SUCCESS, true);
+ } else {
+ // More data expected
+ uint16_t const cnt = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
+ btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, cnt);
+ channel_write_status(ch_id, ch_reg, TUSB_DIR_IN, EP_STAT_VALID, false);
+ channel->in.nak = 0;
+ }
+ }
+}
+
+// Handle NAK response
+static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
+ uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
+ uint8_t const daddr = (ch_reg & USB_CHEP_DEVADDR_Msk) >> USB_CHEP_DEVADDR_Pos;
+
+ uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
+ if (ep_id == TUSB_INDEX_INVALID_8) return;
+
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ // Retry non-periodic transfer immediately if NAK count not exceeded
+ // Periodic transfer will be retried by next frame automatically
+ if (edpt->ep_type == TUSB_XFER_CONTROL || edpt->ep_type == TUSB_XFER_BULK) {
+ hcd_channel_dir_t* channel_dir =
+ (dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in);
+ if (channel_dir->nak < HCD_XFER_NAK_MAX) {
+ channel_dir->nak++;
+ }
+ if (channel_dir->nak < _tuh_cfg.max_nak || _tuh_cfg.max_nak == 0) {
+ channel_write_status(ch_id, ch_reg, dir, EP_STAT_VALID, false);
+ }
+ }
+}
+
+// Handle STALL response
+static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
+ uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
+ uint8_t const daddr = (ch_reg & USB_CHEP_DEVADDR_Msk) >> USB_CHEP_DEVADDR_Pos;
+
+ uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
+ if (ep_id == TUSB_INDEX_INVALID_8) return;
+
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ channel_dealloc(channel, dir);
+
+ channel_write_status(ch_id, ch_reg, dir, EP_STAT_DISABLED, false);
+
+ hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0),
+ edpt->queued_len, XFER_RESULT_STALLED, true);
+}
+
+// Handle error response
+static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
+ uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
+ uint8_t const daddr = (ch_reg & USB_CHEP_DEVADDR_Msk) >> USB_CHEP_DEVADDR_Pos;
+
+ uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
+ if (ep_id == TUSB_INDEX_INVALID_8) return;
+
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+
+ ch_reg &= USB_EPREG_MASK | CH_STAT_MASK(dir);
+ ch_reg &= ~(dir == TUSB_DIR_OUT ? USB_CH_ERRTX : USB_CH_ERRRX);
+
+ hcd_channel_dir_t* channel_dir =
+ (dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in);
+ if (channel_dir->retry < HCD_XFER_ERROR_MAX) {
+ // Retry
+ channel_dir->retry++;
+ ch_change_status(&ch_reg, dir, EP_STAT_VALID);
+ } else {
+ // Failed after retries
+ channel_dealloc(channel, dir);
+ ch_change_status(&ch_reg, dir, EP_STAT_DISABLED);
+ hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0),
+ edpt->queued_len, XFER_RESULT_FAILED, true);
+ }
+ ch_write(ch_id, ch_reg, false);
+}
+
+// Handle CTR interrupt for the TX/OUT direction
+static inline void handle_ctr_tx(uint32_t ch_id) {
+ uint32_t ch_reg = ch_read(ch_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ TU_VERIFY(channel->out.allocated == 1,);
+
+ if ((ch_reg & USB_CH_ERRTX) == 0U) {
+ // No error
+ if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_ACK_SBUF) {
+ ch_handle_ack(ch_id, ch_reg, TUSB_DIR_OUT);
+ } else if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_NAK) {
+ ch_handle_nak(ch_id, ch_reg, TUSB_DIR_OUT);
+ } else if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_STALL) {
+ ch_handle_stall(ch_id, ch_reg, TUSB_DIR_OUT);
+ }
+ } else {
+ ch_handle_error(ch_id, ch_reg, TUSB_DIR_OUT);
+ }
+}
+
+// Handle CTR interrupt for the RX/IN direction
+static inline void handle_ctr_rx(uint32_t ch_id) {
+ uint32_t ch_reg = ch_read(ch_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ TU_VERIFY(channel->in.allocated == 1,);
+
+ if ((ch_reg & USB_CH_ERRRX) == 0U) {
+ // No error
+ if ((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_ACK_SBUF) {
+ ch_handle_ack(ch_id, ch_reg, TUSB_DIR_IN);
+ } else if ((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_NAK) {
+ ch_handle_nak(ch_id, ch_reg, TUSB_DIR_IN);
+ } else if ((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_STALL){
+ ch_handle_stall(ch_id, ch_reg, TUSB_DIR_IN);
+ }
+ } else {
+ ch_handle_error(ch_id, ch_reg, TUSB_DIR_IN);
+ }
+}
+
+// Interrupt Handler
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ uint32_t int_status = FSDEV_REG->ISTR;
+
+ // Start of Frame
+ if (int_status & USB_ISTR_SOF) {
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
+ sof_handler();
+ }
+
+ // Port Change Detected (Connection/Disconnection)
+ if (int_status & USB_ISTR_DCON) {
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_DCON;
+ port_status_handler(rhport, in_isr);
+ }
+
+ // Handle transfer complete (CTR)
+ while (FSDEV_REG->ISTR & USB_ISTR_CTR) {
+ uint32_t const ch_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID;
+ uint32_t const ch_reg = ch_read(ch_id);
+
+ if (ch_reg & USB_EP_CTR_RX) {
+ ch_write_clear_ctr(ch_id, TUSB_DIR_IN);
+ handle_ctr_rx(ch_id);
+ }
+
+ if (ch_reg & USB_EP_CTR_TX) {
+ ch_write_clear_ctr(ch_id, TUSB_DIR_OUT);
+ handle_ctr_tx(ch_id);
+ }
+ }
+
+ if (int_status & USB_ISTR_ERR) {
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ERR;
+ // TODO: Handle error
+ }
+
+ if (int_status & USB_ISTR_PMAOVR) {
+ TU_BREAKPOINT();
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_PMAOVR;
+ }
+}
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport) {
+ fsdev_int_enable(rhport);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport) {
+ fsdev_int_disable(rhport);
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+ return FSDEV_REG->FNR & USB_FNR_FN;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+ return _hcd_data.connected;
+}
+
+// Reset USB bus on the port
+void hcd_port_reset(uint8_t rhport) {
+ (void) rhport;
+ FSDEV_REG->CNTR |= USB_CNTR_FRES;
+}
+
+// Complete bus reset sequence
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+ FSDEV_REG->CNTR &= ~USB_CNTR_FRES;
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+ if ((FSDEV_REG->ISTR & USB_ISTR_LS_DCONN) != 0U) {
+ return TUSB_SPEED_LOW;
+ } else {
+ return TUSB_SPEED_FULL;
+ }
+}
+
+// HCD closes all opened endpoints belonging to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+
+ // Close all endpoints for this device
+ for(uint32_t i = 0; i < CFG_TUH_FSDEV_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->allocated == 1 && edpt->dev_addr == dev_addr) {
+ edpoint_close(i);
+ }
+ }
+
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc) {
+ (void) rhport;
+
+ uint8_t const ep_addr = ep_desc->bEndpointAddress;
+ uint16_t const packet_size = tu_edpt_packet_size(ep_desc);
+ uint8_t const ep_type = ep_desc->bmAttributes.xfer;
+
+ uint8_t const ep_id = endpoint_alloc();
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ edpt->dev_addr = dev_addr;
+ edpt->ep_addr = ep_addr;
+ edpt->ep_type = ep_type;
+ edpt->max_packet_size = packet_size;
+ edpt->interval = ep_desc->bInterval;
+ edpt->pid = 0;
+ edpt->ls_pre = (hcd_port_speed_get(rhport) == TUSB_SPEED_FULL && tuh_speed_get(dev_addr) == TUSB_SPEED_LOW) ? 1 : 0;
+
+ return true;
+}
+
+bool hcd_edpt_close(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+
+ uint8_t const ep_id = endpoint_find(dev_addr, ep_addr);
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+
+ edpoint_close(ep_id);
+
+ return true;
+}
+
+// Submit a transfer
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
+ (void) rhport;
+
+ TU_LOG(FSDEV_DEBUG, "hcd_edpt_xfer addr=%u ep=0x%02X len=%u\r\n", dev_addr, ep_addr, buflen);
+
+ uint8_t const ep_id = endpoint_find(dev_addr, ep_addr);
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->buffer = buffer;
+ edpt->buflen = buflen;
+ edpt->queued_len = 0;
+
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ if (ep_num == 0) {
+ // update ep_dir since control endpoint can switch direction
+ edpt->ep_addr = ep_addr;
+ }
+
+ return edpt_xfer_kickoff(ep_id);
+}
+
+// Abort a queued transfer
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+
+ uint8_t const ep_id = endpoint_find(dev_addr, ep_addr);
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ hcd_channel_t* channel = &_hcd_data.channel[i];
+ uint8_t const allocated = (dir == TUSB_DIR_OUT) ? channel->out.allocated : channel->in.allocated;
+
+ if (allocated == 1 &&
+ channel->dev_addr == dev_addr &&
+ channel->ep_num == tu_edpt_number(ep_addr)) {
+ channel_dealloc(channel, dir);
+ uint32_t ch_reg = ch_read(i) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ channel_write_status(i, ch_reg, dir, EP_STAT_DISABLED, true);
+ }
+ }
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+
+ uint8_t const ep_id = endpoint_find(dev_addr, 0);
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ edpt->next_setup = true;
+ edpt->pid = 0;
+
+ return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8);
+}
+
+// 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) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ uint8_t const ep_id = endpoint_find(dev_addr, 0);
+ TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8);
+
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ edpt->pid = 0;
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Helper Functions
+//--------------------------------------------------------------------+
+
+static uint8_t endpoint_alloc(void) {
+ for (uint32_t i = 0; i < CFG_TUH_FSDEV_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->allocated == 0) {
+ edpt->allocated = 1;
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+static uint8_t endpoint_find(uint8_t dev_addr, uint8_t ep_addr) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ for (uint32_t i = 0; i < (uint32_t)CFG_TUH_FSDEV_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ tusb_dir_t const dir = tu_edpt_dir(edpt->ep_addr);
+ uint8_t const num = tu_edpt_number(edpt->ep_addr);
+ // Match both ep_num and ep_dir, or match ep_num 0 (control endpoint)
+ if (edpt->allocated == 1 && edpt->dev_addr == dev_addr && num == ep_num &&
+ (dir == ep_dir || ep_num == 0)) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// close an opened endpoint
+static void edpoint_close(uint8_t ep_id) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ endpoint_dealloc(edpt);
+
+ // disable active channel belong to this endpoint
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ hcd_channel_t* channel = &_hcd_data.channel[i];
+ uint32_t ch_reg = ch_read(i) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ if (channel->out.allocated == 1 && channel->out.edpt == edpt) {
+ channel_dealloc(channel, TUSB_DIR_OUT);
+ channel_write_status(i, ch_reg, TUSB_DIR_OUT, EP_STAT_DISABLED, true);
+ }
+ if (channel->in.allocated == 1 && channel->in.edpt == edpt) {
+ channel_dealloc(channel, TUSB_DIR_IN);
+ channel_write_status(i, ch_reg, TUSB_DIR_IN, EP_STAT_DISABLED, true);
+ }
+ }
+}
+
+// Allocate PMA buffer
+static uint32_t hcd_pma_alloc(uint8_t channel, tusb_dir_t dir, uint16_t len) {
+ (void) len;
+ // Simple static allocation as we are unlikely to handle ISO endpoints in host mode
+ // We just give each channel two buffers of max packet size (64 bytes) for IN and OUT
+
+ uint16_t addr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT;
+ addr += channel * TUSB_EPSIZE_BULK_FS * 2 + (dir == TUSB_DIR_IN ? TUSB_EPSIZE_BULK_FS : 0);
+
+ TU_ASSERT(addr <= CFG_TUSB_FSDEV_PMA_SIZE, 0xFFFF);
+
+ return addr;
+}
+
+// Allocate hardware channel
+static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ // Find channel allocate for same ep_num but other direction
+ tusb_dir_t const other_dir = (dir == TUSB_DIR_IN) ? TUSB_DIR_OUT : TUSB_DIR_IN;
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ uint8_t const allocated_dir = (dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated;
+ uint8_t const allocated_other = (other_dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated;
+ if (allocated_dir == 0 &&
+ allocated_other == 1 &&
+ _hcd_data.channel[i].dev_addr == dev_addr &&
+ _hcd_data.channel[i].ep_num == ep_num &&
+ _hcd_data.channel[i].ep_type == ep_type) {
+ if (dir == TUSB_DIR_OUT) {
+ _hcd_data.channel[i].out.allocated = 1;
+ _hcd_data.channel[i].out.retry = 0;
+ } else {
+ _hcd_data.channel[i].in.allocated = 1;
+ _hcd_data.channel[i].in.retry = 0;
+ }
+ return i;
+ }
+ }
+
+ // Find free channel
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ if (_hcd_data.channel[i].out.allocated == 0 && _hcd_data.channel[i].in.allocated == 0) {
+ _hcd_data.channel[i].dev_addr = dev_addr;
+ _hcd_data.channel[i].ep_num = ep_num;
+ _hcd_data.channel[i].ep_type = ep_type;
+ if (dir == TUSB_DIR_OUT) {
+ _hcd_data.channel[i].out.allocated = 1;
+ _hcd_data.channel[i].out.retry = 0;
+ } else {
+ _hcd_data.channel[i].in.allocated = 1;
+ _hcd_data.channel[i].in.retry = 0;
+ }
+ return i;
+ }
+ }
+
+ // Allocation failed
+ return TUSB_INDEX_INVALID_8;
+}
+
+// kick-off transfer with an endpoint
+static bool edpt_xfer_kickoff(uint8_t ep_id) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ uint8_t ch_id = channel_alloc(edpt->dev_addr, edpt->ep_addr, edpt->ep_type);
+ TU_ASSERT(ch_id != TUSB_INDEX_INVALID_8); // all channel are in used
+
+ tusb_dir_t const dir = tu_edpt_dir(edpt->ep_addr);
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ if (dir == TUSB_DIR_OUT) {
+ channel->out.edpt = edpt;
+ } else {
+ channel->in.edpt = edpt;
+ }
+
+ return channel_xfer_start(ch_id, dir);
+}
+
+static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir) {
+ hcd_channel_t* channel = &_hcd_data.channel[ch_id];
+ hcd_endpoint_t* edpt = (dir == TUSB_DIR_OUT) ? channel->out.edpt : channel->in.edpt;
+
+ uint32_t ch_reg = ch_read(ch_id) & ~USB_EPREG_MASK;
+ ch_reg |= tu_edpt_number(edpt->ep_addr) | edpt->dev_addr << USB_CHEP_DEVADDR_Pos |
+ USB_EP_CTR_TX | USB_EP_CTR_RX;
+
+ // Set type
+ switch (edpt->ep_type) {
+ case TUSB_XFER_BULK:
+ ch_reg |= USB_EP_BULK;
+ break;
+ case TUSB_XFER_INTERRUPT:
+ ch_reg |= USB_EP_INTERRUPT;
+ break;
+
+ case TUSB_XFER_CONTROL:
+ ch_reg |= USB_EP_CONTROL;
+ break;
+
+ default:
+ // Note: ISO endpoint is unsupported
+ TU_ASSERT(false);
+ }
+
+ /* Create a packet memory buffer area. */
+ uint16_t pma_addr = hcd_pma_alloc(ch_id, dir, edpt->max_packet_size);
+ btable_set_addr(ch_id, dir == TUSB_DIR_OUT ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
+
+ if (dir == TUSB_DIR_OUT) {
+ uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
+ tu_hwfifo_write(PMA_BUF_AT(pma_addr), &(edpt->buffer[edpt->queued_len]), len, NULL);
+ btable_set_count(ch_id, BTABLE_BUF_TX, len);
+
+ edpt->queued_len += len;
+ } else {
+ btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, edpt->max_packet_size);
+ }
+
+ if (edpt->ls_pre == 1) {
+ ch_reg |= USB_CHEP_LSEP;
+ } else {
+ ch_reg &= ~USB_CHEP_LSEP;
+ }
+
+ // Setup DATA/STATUS phase start with DATA1
+ if (tu_edpt_number(edpt->ep_addr) == 0) {
+ edpt->pid = 1;
+ }
+
+ if (edpt->next_setup) {
+ edpt->next_setup = false;
+ ch_reg |= USB_EP_SETUP;
+ edpt->pid = 0;
+ }
+
+ ch_change_status(&ch_reg, dir, EP_STAT_VALID);
+ ch_change_dtog(&ch_reg, dir, edpt->pid);
+ ch_reg &= USB_EPREG_MASK | CH_STAT_MASK(dir) | CH_DTOG_MASK(dir);
+ ch_write(ch_id, ch_reg, true);
+
+ return true;
+}
+
+#endif
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index b413121a5..9fac0bc1c 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -36,9 +36,6 @@
#include <device/dcd.h>
#include "musb_def.h"
-//#include "bsp/board_api.h"
-extern uint32_t board_millis(void); // TODO remove
-
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index ba40498e6..8685ec6dc 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -77,9 +77,7 @@ CFG_TUD_MEM_SECTION static struct {
TUD_EPBUF_DEF(setup_packet, 8);
} _dcd_usbbuf;
-static tud_configure_dwc2_t _tud_cfg = {
- .bm_double_buffered = 0
-};
+static tud_configure_dwc2_t _tud_cfg = CFG_TUD_CONFIGURE_DWC2_DEFAULT;
TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc2) {
#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
@@ -98,10 +96,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_enabled(dwc2_dep_t* dep) {
return (dep->ctl & EPCTL_EPENA) != 0;
}
-//--------------------------------------------------------------------
-// DMA
-//--------------------------------------------------------------------
-#if CFG_TUD_MEM_DCACHE_ENABLE
+ #if CFG_TUD_DWC2_SLAVE_ENABLE
+static uint16_t epin_write_tx_fifo(dwc2_regs_t *dwc2, uint8_t epnum);
+ #endif
+
+ //--------------------------------------------------------------------
+ // DMA
+ //--------------------------------------------------------------------
+ #if CFG_TUD_MEM_DCACHE_ENABLE
bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
TU_VERIFY(addr && data_size);
return dwc2_dcache_clean(addr, data_size);
@@ -345,40 +347,6 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
-static uint16_t epin_write_tx_fifo(uint8_t rhport, uint8_t epnum) {
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- dwc2_dep_t* const epin = &dwc2->ep[0][epnum];
- xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
-
- dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
- const uint16_t remain_packets = tsiz.packet_count;
-
- uint16_t total_bytes_written = 0;
- // Process every single packet (only whole packets can be written to fifo)
- for (uint16_t i = 0; i < remain_packets; i++) {
- tsiz.value = epin->tsiz;
- const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
- const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
-
- // Check if dtxfsts has enough space available
- if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
- break;
- }
-
- // Push packet to Tx-FIFO
- if (xfer->ff) {
- volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
- tu_fifo_read_n_access_mode(xfer->ff, (void *)(uintptr_t)tx_fifo, xact_bytes, TU_FIFO_FIXED_ADDR_RW32);
- total_bytes_written += xact_bytes;
- } else {
- dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
- xfer->buffer += xact_bytes;
- total_bytes_written += xact_bytes;
- }
- }
- return total_bytes_written;
-}
-
// Since this function returns void, it is not possible to return a boolean success message
// We must make sure that this function is not called when the EP is disabled
// Must be called from critical section
@@ -423,6 +391,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
}
}
+ #if CFG_TUD_DWC2_DMA_ENABLE
const bool is_dma = dma_device_enabled(dwc2);
if(is_dma) {
if (dir == TUSB_DIR_IN && total_bytes != 0) {
@@ -434,11 +403,14 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
if (epnum == 0) {
xfer->buffer += total_bytes;
}
- } else {
+ } else
+ #endif
+ {
+ #if CFG_TUD_DWC2_SLAVE_ENABLE
dep->diepctl = depctl.value; // enable endpoint
if (dir == TUSB_DIR_IN && total_bytes != 0) {
- const uint16_t xferred_bytes = epin_write_tx_fifo(rhport, epnum);
+ const uint16_t xferred_bytes = epin_write_tx_fifo(dwc2, epnum);
// Enable TXFE interrupt if there are still data to be sent
// EP0 only sends one packet at a time, so no need to check for EP0
@@ -446,6 +418,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
dwc2->diepempmsk |= (1u << epnum);
}
}
+ #endif
}
}
@@ -469,14 +442,14 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_memclr(&_dcd_data, sizeof(_dcd_data));
// Core Initialization
- const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
+ const bool is_hs_phy = dwc2_core_is_highspeed_phy(dwc2, TUD_OPT_HIGH_SPEED);
const bool is_dma = dma_device_enabled(dwc2);
- TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+ TU_ASSERT(dwc2_core_init(rhport, is_hs_phy, is_dma));
//------------- 7.1 Device Initialization -------------//
// Set device max speed
uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
- if (is_highspeed) {
+ if (is_hs_phy) {
// dcfg Highspeed's mask is 0
// XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
@@ -497,13 +470,16 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Force device mode
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
- // Clear A override, force B Valid
- dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
+ // OTG Ctrl
+ uint32_t gotgctl = dwc2->gotgctl & ~GOTGCTL_AVALOEN; // Clear A-override
+ if (!_tud_cfg.vbus_sensing) {
+ gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; // force B Valid if not sensing VBus
+ }
+ dwc2->gotgctl = gotgctl;
-#if CFG_TUSB_MCU == OPT_MCU_STM32N6
- // No hardware detection of Vbus B-session is available on the STM32N6
- dwc2->stm32_gccfg |= STM32_GCCFG_VBVALOVAL;
-#endif
+ #ifdef TUP_USBIP_DWC2_STM32
+ dwc2_stm32_gccfg_cfg(dwc2, _tud_cfg.vbus_sensing, false);
+ #endif
// Enable required interrupts
dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
@@ -516,6 +492,12 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
+bool dcd_deinit(uint8_t rhport) {
+ dcd_disconnect(rhport);
+ dwc2_core_deinit(rhport);
+ return true;
+}
+
void dcd_int_enable(uint8_t rhport) {
dwc2_dcd_int_enable(rhport);
}
@@ -551,34 +533,38 @@ void dcd_remote_wakeup(uint8_t rhport) {
}
void dcd_connect(uint8_t rhport) {
- (void) rhport;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
#ifdef TUP_USBIP_DWC2_ESP32
- usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
- conf.pad_pull_override = 0;
- conf.dp_pullup = 0;
- conf.dp_pulldown = 0;
- conf.dm_pullup = 0;
- conf.dm_pulldown = 0;
- USB_WRAP.otg_conf = conf;
+ // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY
+ if (rhport == 0) {
+ usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
+ conf.pad_pull_override = 0;
+ conf.dp_pullup = 0;
+ conf.dp_pulldown = 0;
+ conf.dm_pullup = 0;
+ conf.dm_pulldown = 0;
+ USB_WRAP.otg_conf = conf;
+ }
#endif
dwc2->dctl &= ~DCTL_SDIS;
}
void dcd_disconnect(uint8_t rhport) {
- (void) rhport;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
#ifdef TUP_USBIP_DWC2_ESP32
- usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
- conf.pad_pull_override = 1;
- conf.dp_pullup = 0;
- conf.dp_pulldown = 1;
- conf.dm_pullup = 0;
- conf.dm_pulldown = 1;
- USB_WRAP.otg_conf = conf;
+ // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY
+ if (rhport == 0) {
+ usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
+ conf.pad_pull_override = 1;
+ conf.dp_pullup = 0;
+ conf.dp_pulldown = 1;
+ conf.dm_pullup = 0;
+ conf.dm_pulldown = 1;
+ USB_WRAP.otg_conf = conf;
+ }
#endif
dwc2->dctl |= DCTL_SDIS;
@@ -689,9 +675,6 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// into the USB buffer!
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes, bool is_isr) {
(void) is_isr;
- // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
- TU_ASSERT(ff->item_size == 1);
-
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
@@ -808,7 +791,7 @@ static void handle_bus_reset(uint8_t rhport) {
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
}
static void handle_enum_done(uint8_t rhport) {
@@ -854,6 +837,39 @@ TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) {
#endif
#if CFG_TUD_DWC2_SLAVE_ENABLE
+static uint16_t epin_write_tx_fifo(dwc2_regs_t *dwc2, uint8_t epnum) {
+ dwc2_dep_t *const epin = &dwc2->ep[0][epnum];
+ xfer_ctl_t *const xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
+
+ dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
+ const uint16_t remain_packets = tsiz.packet_count;
+
+ uint16_t total_bytes_written = 0;
+ // Process every single packet (only whole packets can be written to fifo)
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ tsiz.value = epin->tsiz;
+ const uint16_t remain_bytes = (uint16_t)tsiz.xfer_size;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
+
+ // Check if dtxfsts has enough space available
+ if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
+ }
+
+ // Push packet to Tx-FIFO
+ volatile uint32_t *tx_fifo = dwc2->fifo[epnum];
+ if (xfer->ff) {
+ tu_hwfifo_write_from_fifo(tx_fifo, xfer->ff, xact_bytes, NULL);
+ total_bytes_written += xact_bytes;
+ } else {
+ tu_hwfifo_write(tx_fifo, xfer->buffer, xact_bytes, NULL);
+ xfer->buffer += xact_bytes;
+ total_bytes_written += xact_bytes;
+ }
+ }
+ return total_bytes_written;
+}
+
// Process shared receive FIFO, this interrupt is only used in Slave mode
static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
@@ -893,9 +909,9 @@ static void handle_rxflvl_irq(uint8_t rhport) {
if (byte_count != 0) {
// Read packet off RxFIFO
if (xfer->ff != NULL) {
- tu_fifo_write_n_access_mode(xfer->ff, (const void *)(uintptr_t)rx_fifo, byte_count, TU_FIFO_FIXED_ADDR_RW32);
+ tu_hwfifo_read_to_fifo(rx_fifo, xfer->ff, byte_count, NULL);
} else {
- dfifo_read_packet(dwc2, xfer->buffer, byte_count);
+ tu_hwfifo_read(rx_fifo, xfer->buffer, byte_count, NULL);
xfer->buffer += byte_count;
}
}
@@ -967,7 +983,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
// - 64 bytes or
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
if (diepint_bm.txfifo_empty && tu_bit_test(dwc2->diepempmsk, epnum)) {
- epin_write_tx_fifo(rhport, epnum);
+ epin_write_tx_fifo(dwc2, epnum);
// Turn off TXFE if all bytes are written.
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
@@ -1175,6 +1191,7 @@ void dcd_int_handler(uint8_t rhport) {
const uint32_t otg_int = dwc2->gotgint;
if (otg_int & GOTGINT_SEDET) {
+ dwc2->gintmsk &= ~GINTMSK_OTGINT;
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
diff --git a/src/portable/synopsys/dwc2/dwc2_at32.h b/src/portable/synopsys/dwc2/dwc2_at32.h
index 513495eb1..fa6d10c12 100644
--- a/src/portable/synopsys/dwc2/dwc2_at32.h
+++ b/src/portable/synopsys/dwc2/dwc2_at32.h
@@ -61,6 +61,11 @@
#define OTG1_FIFO_SIZE 1280
#define OTG1_IRQn OTGFS1_IRQn
#define DWC2_OTG1_REG_BASE 0x50000000UL
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F45X
+ #include <at32f45x.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
#endif
#ifdef __cplusplus
@@ -107,6 +112,12 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t *dwc2, uint8_
}
}
+// MCU specific PHY deinit, disable PHY power
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
+ (void) hs_phy_type;
+ dwc2->stm32_gccfg &= ~(STM32_GCCFG_PWRDWN | STM32_GCCFG_DCDEN | STM32_GCCFG_PDEN);
+}
+
// 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;
diff --git a/src/portable/synopsys/dwc2/dwc2_bcm.h b/src/portable/synopsys/dwc2/dwc2_bcm.h
index df6d4a852..00842bba2 100644
--- a/src/portable/synopsys/dwc2/dwc2_bcm.h
+++ b/src/portable/synopsys/dwc2/dwc2_bcm.h
@@ -73,6 +73,13 @@ static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
// nothing to do
}
+// MCU specific PHY deinit, disable PHY power
+static inline void dwc2_phy_deinit(dwc2_regs_t * dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ (void) hs_phy_type;
+ // nothing to do
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
{
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index 980574e12..33eabaeab 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -39,14 +39,11 @@ static void reset_core(dwc2_regs_t* dwc2) {
while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {
}
- // load gsnpsid (it is not readable after reset is asserted)
- const uint32_t gsnpsid = dwc2->gsnpsid;
-
- // reset core
- dwc2->grstctl |= GRSTCTL_CSRST;
+ const uint32_t gsnpsid = dwc2->gsnpsid; // preload gsnpsid which is not readable while resetting
+ dwc2->grstctl |= GRSTCTL_CSRST; // reset core
if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
- // prior v4.20a: CSRST is self-clearing and the core clears this bit after all the necessary logic is reset in
+ // prior v4.20a: CSRST is self-clearing, and the core clears this bit after all the necessary logic is reset in
// the core, which can take several clocks, depending on the current state of the core. Once this bit has been
// cleared, the software must wait at least 3 PHY clocks before accessing the PHY domain (synchronization delay).
while (dwc2->grstctl & GRSTCTL_CSRST) {}
@@ -60,6 +57,7 @@ static void reset_core(dwc2_regs_t* dwc2) {
while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
}
+// Dedicated FS PHY is internal with a clock 48Mhz.
static void phy_fs_init(dwc2_regs_t* dwc2) {
TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n");
@@ -86,6 +84,12 @@ static void phy_fs_init(dwc2_regs_t* dwc2) {
dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
}
+/* dwc2 has 2 highspeed PHYs options
+ * - UTMI+ is internal highspeed PHY, can be clocked at 30 Mhz (8-bit) or 60 Mhz (16-bit).
+ * - ULPI is external highspeed PHY, clocked at 60Mhz with 8-bit interface.
+ *
+ * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
+ */
static void phy_hs_init(dwc2_regs_t* dwc2) {
uint32_t gusbcfg = dwc2->gusbcfg;
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
@@ -179,32 +183,20 @@ static bool check_dwc2(dwc2_regs_t* dwc2) {
//--------------------------------------------------------------------
//
//--------------------------------------------------------------------
-bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
- (void)dwc2;
-#if CFG_TUD_ENABLED
- if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
- return false;
- }
-#endif
-#if CFG_TUH_ENABLED
- if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) {
- return false;
- }
-#endif
+bool dwc2_core_is_highspeed_phy(dwc2_regs_t* dwc2, bool prefer_hs_phy) {
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ const bool has_hs_phy = (ghwcfg2.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED);
- const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
- return ghwcfg2.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
+ if (prefer_hs_phy) {
+ return has_hs_phy;
+ } else {
+ const bool has_fs_phy = (ghwcfg2.fs_phy_type != GHWCFG2_FSPHY_NOT_SUPPORTED);
+ // false if has fs phy, otherwise true since hs phy is the only available phy
+ return !has_fs_phy && has_hs_phy;
+ }
}
-/* dwc2 has several PHYs option
- * - UTMI+ is internal highspeed PHY, clock can be 30 Mhz (8-bit) or 60 Mhz (16-bit)
- * - ULPI is external highspeed PHY, clock is 60Mhz with only 8-bit interface
- * - Dedicated FS PHY is internal with clock 48Mhz.
- *
- * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
- *
-*/
-bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
+bool dwc2_core_init(uint8_t rhport, bool is_hs_phy, bool is_dma) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// Check Synopsys ID register, failed if controller clock/power is not enabled
@@ -213,7 +205,7 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
// disable global interrupt
dwc2->gahbcfg &= ~GAHBCFG_GINT;
- if (is_highspeed) {
+ if (is_hs_phy) {
phy_hs_init(dwc2);
} else {
phy_fs_init(dwc2);
@@ -251,6 +243,24 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
return true;
}
+void dwc2_core_deinit(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Disable global interrupt
+ dwc2->gahbcfg &= ~GAHBCFG_GINT;
+
+ // Reset core: this also flushes FIFOs and clears all interrupt registers
+ reset_core(dwc2);
+
+ // Stop PHY clock and gate HCLK for power saving (per databook chapter 14)
+ dwc2->pcgcctl |= PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK;
+
+ // MCU-specific PHY deinit (disable PHY power)
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ const uint8_t hs_phy_type = (dwc2->gusbcfg & GUSBCFG_PHYSEL) ? GHWCFG2_HSPHY_NOT_SUPPORTED : ghwcfg2.hs_phy_type;
+ dwc2_phy_deinit(dwc2, hs_phy_type);
+}
+
// void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr) {
// (void) in_isr;
// dwc2_regs_t * const dwc2 = DWC2_REG(rhport);
@@ -264,58 +274,4 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
//
// }
-//--------------------------------------------------------------------
-// DFIFO
-//--------------------------------------------------------------------
-// Read a single data packet from receive DFIFO
-void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) {
- const volatile uint32_t* rx_fifo = dwc2->fifo[0];
-
- // Reading full available 32 bit words from fifo
- uint16_t word_count = len >> 2;
- while (word_count--) {
- tu_unaligned_write32(dst, *rx_fifo);
- dst += 4;
- }
-
- // Read the remaining 1-3 bytes from fifo
- const uint8_t bytes_rem = len & 0x03;
- if (bytes_rem != 0) {
- const uint32_t tmp = *rx_fifo;
- dst[0] = tu_u32_byte0(tmp);
- if (bytes_rem > 1) {
- dst[1] = tu_u32_byte1(tmp);
- }
- if (bytes_rem > 2) {
- dst[2] = tu_u32_byte2(tmp);
- }
- }
-}
-
-// Write a single data packet to DFIFO
-void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) {
- volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
-
- // Pushing full available 32 bit words to fifo
- uint16_t word_count = len >> 2;
- while (word_count--) {
- *tx_fifo = tu_unaligned_read32(src);
- src += 4;
- }
-
- // Write the remaining 1-3 bytes into fifo
- const uint8_t bytes_rem = len & 0x03;
- if (bytes_rem) {
- uint32_t tmp_word = src[0];
- if (bytes_rem > 1) {
- tmp_word |= (src[1] << 8);
- }
- if (bytes_rem > 2) {
- tmp_word |= (src[2] << 16);
- }
-
- *tx_fifo = tmp_word;
- }
-}
-
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index 428304ba9..9f28ab2e0 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -42,6 +42,7 @@
// - _dwc2_controller[]: array of controllers
// - DWC2_EP_MAX: largest EP counts of all controllers
// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
+// - dwc2_phy_deinit(dwc2, hs_phy_type): phy deinit to disable PHY power, only deinit the phy used by core
// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
// - dwc2_remote_wakeup_delay
@@ -84,8 +85,10 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base;
}
-bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role);
-bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma);
+// check if highspeed phy should be used
+bool dwc2_core_is_highspeed_phy(dwc2_regs_t* dwc2, bool prefer_hs_phy);
+bool dwc2_core_init(uint8_t rhport, bool is_hs_phy, bool is_dma);
+void dwc2_core_deinit(uint8_t rhport);
void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
//--------------------------------------------------------------------+
diff --git a/src/portable/synopsys/dwc2/dwc2_efm32.h b/src/portable/synopsys/dwc2/dwc2_efm32.h
index 0e3570cbb..e1cb7c769 100644
--- a/src/portable/synopsys/dwc2/dwc2_efm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_efm32.h
@@ -72,6 +72,14 @@ static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
USB->ROUTE = USB_ROUTE_PHYPEN;
}
+// MCU specific PHY deinit, disable PHY power
+static inline void dwc2_phy_deinit(dwc2_regs_t * dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ (void) hs_phy_type;
+ // Disable PHY pin
+ USB->ROUTE = 0;
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
{
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index a4e0d1770..ff9f216bd 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -121,6 +121,13 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_
}
+// MCU specific PHY deinit, disable PHY power
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+ // PHY managed by ESP-IDF
+}
+
// 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;
diff --git a/src/portable/synopsys/dwc2/dwc2_gd32.h b/src/portable/synopsys/dwc2/dwc2_gd32.h
index 0375fffe4..ccbf93a76 100644
--- a/src/portable/synopsys/dwc2/dwc2_gd32.h
+++ b/src/portable/synopsys/dwc2/dwc2_gd32.h
@@ -85,6 +85,13 @@ static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
// nothing to do
}
+// MCU specific PHY deinit, disable PHY power
+static inline void dwc2_phy_deinit(dwc2_regs_t * dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ (void) hs_phy_type;
+ // nothing to do
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
{
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index f655e4dba..f83007b8c 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | nRF54 | 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 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
-| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54430A | 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 | 4.30a | 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 | 0xAA555000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x228BFC72 | 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 | noHNP noSRP | 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 | 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 | 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 | UTMI+ | 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 | n/a | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
-| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 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 | 1 | 0 |
-| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 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 | 0 |
-| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 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 | 8 | 2 |
-| - token_q_depth | 8 | 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 | 0 |
-| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x0BEAC0E8 | 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 | 8 | 5 | 0 |
-| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 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 | 1 | 0 |
-| - i2c_enable | 0 | 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 | 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 | 0 |
-| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 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 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 3050 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
-| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x1E10AA60 | 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 | 0 |
-| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 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 | 1 | 0 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 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 | 0 |
-| - reserved8 | 0 | 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 | 1 | 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 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 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 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 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/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 | 0 |
-| - iddg_filter | 1 | 1 | 0 | 1 | 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 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 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 | 1 | 0 |
-| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 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 | 0 | 1 | 0 | 1 | 1 | 0 |
-| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 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 | nRF54 | ST F407/429 HS | ST F207/F407/411/429 FS | ST L476 FS | ST F412/76x FS | ST F76x HS | ST H743/H750 | ST F723/L4P5 FS | ST F723 HS | ST H7RS FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:-------------|:-----------------|:--------------------------|:-------------|:-----------------|:-------------|:---------------|:------------------|:-------------|:-------------|:---------------------|:-------------|:------------|
+| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001100 | 0x00001200 | 0x00002000 | 0x00002000 | 0x00002100 | 0x00002300 | 0x00003000 | 0x00003100 | 0x00004000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54430A | 0x4F54281A | 0x4F54281A | 0x4F54310A | 0x4F54320A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54330A | 0x4F54411A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 4.30a | 2.81a | 2.81a | 3.10a | 3.20a | 3.20a | 3.30a | 3.30a | 3.30a | 4.11a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0xAA555000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x228BFC72 | 0x229ED590 | 0x229DCD20 | 0x229ED520 | 0x229ED520 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x229FE1D0 | 0x229ED522 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | Slave only | Slave only | DMA internal | DMA internal | Slave only | 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 | n/a | hub | hub | n/a | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | UTMI+ | ULPI | n/a | n/a | n/a | ULPI | ULPI | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | n/a | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 15 | 5 | 3 | 5 | 5 | 8 | 8 | 5 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 15 | 11 | 7 | 11 | 11 | 15 | 15 | 11 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 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 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 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 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 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 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 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 | 0 | 0 |
+| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x0BEAC0E8 | 0x03F403E8 | 0x020001E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03EED2E8 | 0x020081E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 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 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 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 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 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 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 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 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 3050 | 1012 | 512 | 512 | 512 | 1006 | 952 | 512 | 1006 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x1E10AA60 | 0x17F00030 | 0x0FF08030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0x23F00030 | 0x1610B230 | 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 | 0 | 0 |
+| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 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 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 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 | 0 | 0 |
+| - reserved8 | 0 | 0 | 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 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 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 | 1 | 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 | 0 | 1 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 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/16 bit | 8 bit | 8 bit | 8/16 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 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 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 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 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 | 7 | 5 | 3 | 5 | 5 | 8 | 8 | 5 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index 8fbbc00a0..e6601f482 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -16,14 +16,16 @@ dwc2_reg_value = {
'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030],
'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
'nRF54': [0, 0x4F54430A, 0xAA555000, 0x228BFC72, 0x0BEAC0E8, 0x1E10AA60],
- 'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
+ # ST sort by GUID
'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030],
+ 'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
+ 'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
'ST F412/76x FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
- 'ST F723/L4P5 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
- 'ST F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x03EED2E8, 0x23F00030],
'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
- 'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F723/L4P5 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x03EED2E8, 0x23F00030],
+ 'ST H7RS FS': [0x4000, 0x4F54411A, 0, 0x229ED522, 0x20081E8, 0x1610B230],
'ST U5A5/H7RS/N6 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
diff --git a/src/portable/synopsys/dwc2/dwc2_nrf.h b/src/portable/synopsys/dwc2/dwc2_nrf.h
index b93571f16..51f2d684f 100644
--- a/src/portable/synopsys/dwc2/dwc2_nrf.h
+++ b/src/portable/synopsys/dwc2/dwc2_nrf.h
@@ -52,6 +52,12 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_
(void)hs_phy_type;
}
+// MCU specific PHY deinit, disable PHY power
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index 516eb021b..259ad21b9 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -85,8 +85,11 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
- #define USB_OTG_HS_PERIPH_BASE USB1_OTG_HS_BASE
- #define OTG_HS_IRQn USB1_OTG_HS_IRQn
+ #define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_BASE
+ #define OTG_FS_IRQn USB1_OTG_HS_IRQn
+
+ #define USB_OTG_HS_PERIPH_BASE USB2_OTG_HS_BASE
+ #define OTG_HS_IRQn USB2_OTG_HS_IRQn
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
@@ -261,6 +264,22 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
}
}
+// MCU specific PHY deinit, disable PHY power
+static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
+ // Disable on-chip FS PHY
+ dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN;
+ } else {
+ // Disable HS PHY
+ #ifdef USB_HS_PHYC
+ dwc2->stm32_gccfg &= ~STM32_GCCFG_PHYHSEN;
+ // Disable PLL and LDO
+ USB_HS_PHYC->USB_HS_PHYC_PLL &= ~USB_HS_PHYC_PLL_PLLEN;
+ USB_HS_PHYC->USB_HS_PHYC_LDO &= ~USB_HS_PHYC_LDO_ENABLE;
+ #endif
+ }
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// used to set turnaround time for fullspeed, nothing to do in highspeed mode
@@ -300,6 +319,66 @@ static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
}
}
+//------------- GCCFG configuration -------------//
+static inline void dwc2_stm32_gccfg_cfg(dwc2_regs_t* dwc2, bool vbus_sensing, bool is_host) {
+ if (is_host) {
+ vbus_sensing = false;
+ }
+
+ uint32_t gccfg = dwc2->stm32_gccfg;
+ if (dwc2->guid < 0x2000) {
+ // use VBUSASEN/VBUSBSEN/NOVBUSSENS bits
+ if (is_host) {
+ gccfg &= ~(STM32_GCCFG_NOVBUSSENS | STM32_GCCFG_VBUSBSEN | STM32_GCCFG_VBUSASEN);
+ } else {
+ if (vbus_sensing) {
+ gccfg &= ~STM32_GCCFG_NOVBUSSENS;
+ gccfg |= STM32_GCCFG_VBUSBSEN;
+ } else {
+ gccfg |= STM32_GCCFG_NOVBUSSENS;
+ gccfg &= ~(STM32_GCCFG_VBUSBSEN | STM32_GCCFG_VBUSASEN);
+ }
+ }
+ } else if (dwc2->guid < 0x5000) {
+ // the later version uses VBDEN with battery charging detection
+ if (vbus_sensing) {
+ gccfg |= STM32_GCCFG_VBDEN;
+ } else {
+ gccfg &= ~STM32_GCCFG_VBDEN;
+ }
+ } else {
+ // from 0x5000 ST seems to use femtoPHY for UTMI+ HS PHY. Which use VBVALEXTOEN and VBVALOVAL for software override
+ // external VBUS sensing
+ // Note: N6 does not support hardware VBUS sensing, so the software override is always active. Therefore, VBDEN and
+ // VBVALEXTOEN are not available
+#if CFG_TUSB_MCU == OPT_MCU_STM32N6
+ if (is_host) {
+ gccfg |= STM32_GCCFG_PULLDOWNEN;
+ gccfg &= ~(STM32_GCCFG_VBVALOVAL);
+ } else {
+ gccfg &= ~STM32_GCCFG_PULLDOWNEN;
+ gccfg |= STM32_GCCFG_VBVALOVAL;
+ }
+#else
+ if (is_host) {
+ gccfg |= STM32_GCCFG_PULLDOWNEN;
+ gccfg &= ~(STM32_GCCFG_VBDEN | STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL);
+ } else {
+ gccfg &= ~STM32_GCCFG_PULLDOWNEN;
+ if (vbus_sensing) {
+ gccfg |= STM32_GCCFG_VBDEN;
+ gccfg &= ~(STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL);
+ } else {
+ gccfg &= ~STM32_GCCFG_VBDEN;
+ gccfg |= STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL;
+ }
+ }
+#endif
+ }
+
+ dwc2->stm32_gccfg = gccfg;
+}
+
//------------- DCache -------------//
#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
@@ -321,8 +400,13 @@ static mem_region_t uncached_regions[] = {
// DTCM (although USB DMA can't transfer to/from DTCM)
{.start = 0x20000000, .end = 0x2002FFFF},
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
- // DTCM
+ // DTCM
{.start = 0x20000000, .end = 0x2000FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
+ // DTCM NS
+ {.start = 0x20000000, .end = 0x2003FFFF},
+ // DTCM S
+ {.start = 0x30000000, .end = 0x3003FFFF},
#else
#error "Cache maintenance is not supported yet"
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index 7693ce02a..596bd0b34 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -1650,24 +1650,38 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define STM32_GCCFG_PHYHSEN_Msk (0x1UL << STM32_GCCFG_PHYHSEN_Pos) // 0x00800000
#define STM32_GCCFG_PHYHSEN STM32_GCCFG_PHYHSEN_Msk // HS PHY enable
-// TODO stm32u5a5 SDEN is 22nd bit, conflict with 20th bit above
-//#define STM32_GCCFG_SDEN_Pos (22U)
-//#define STM32_GCCFG_SDEN_Msk (0x1U << STM32_GCCFG_SDEN_Pos) // 0x00400000
-//#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (PD) mode enable
+// GUID < 0x2000: VBUSASEN, VBUSBSEN, NOVBUSSENS bits
+#define STM32_GCCFG_VBUSASEN_Pos (18U)
+#define STM32_GCCFG_VBUSASEN_Msk (0x1UL << STM32_GCCFG_VBUSASEN_Pos) // 0x00040000
+#define STM32_GCCFG_VBUSASEN STM32_GCCFG_VBUSASEN_Msk // Enable A-device (host) VBUS sensing
-// TODO stm32u5a5 VBVALOVA is 23rd bit, conflict with PHYHSEN bit above
-#define STM32_GCCFG_VBVALOVAL_Pos (23U)
-#define STM32_GCCFG_VBVALOVAL_Msk (0x1U << STM32_GCCFG_VBVALOVAL_Pos) // 0x00800000
-#define STM32_GCCFG_VBVALOVAL STM32_GCCFG_VBVALOVAL_Msk // Value of VBUSVLDEXT0 femtoPHY input
+#define STM32_GCCFG_VBUSBSEN_Pos (19U)
+#define STM32_GCCFG_VBUSBSEN_Msk (0x1UL << STM32_GCCFG_VBUSBSEN_Pos) // 0x00080000
+#define STM32_GCCFG_VBUSBSEN STM32_GCCFG_VBUSBSEN_Msk // Enable B-device (peripheral) VBUS sensing
-#define STM32_GCCFG_VBVALEXTOEN_Pos (24U)
-#define STM32_GCCFG_VBVALEXTOEN_Msk (0x1U << STM32_GCCFG_VBVALEXTOEN_Pos) // 0x01000000
-#define STM32_GCCFG_VBVALEXTOEN STM32_GCCFG_VBVALEXTOEN_Msk // Enables of VBUSVLDEXT0 femtoPHY input override
+#define STM32_GCCFG_NOVBUSSENS_Pos (21U)
+#define STM32_GCCFG_NOVBUSSENS_Msk (0x1UL << STM32_GCCFG_NOVBUSSENS_Pos) // 0x00200000
+#define STM32_GCCFG_NOVBUSSENS STM32_GCCFG_NOVBUSSENS_Msk // VBUS sensing disable option
+// GUID < 0x2000: end
-#define STM32_GCCFG_PULLDOWNEN_Pos (25U)
-#define STM32_GCCFG_PULLDOWNEN_Msk (0x1U << STM32_GCCFG_PULLDOWNEN_Pos) // 0x02000000
-#define STM32_GCCFG_PULLDOWNEN STM32_GCCFG_PULLDOWNEN_Msk // Enables of femtoPHY pulldown resistors, used when ID PAD is disabled
+// TODO: stm32u5a5 SDEN is 22nd bit, conflict with 20th bit above
+// #define STM32_GCCFG_SDEN_Pos (22U)
+// #define STM32_GCCFG_SDEN_Msk (0x1U << STM32_GCCFG_SDEN_Pos) // 0x00400000
+// #define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (PD) mode enable
+// GUID >= 0x5000 use femtoPHY: VBVALOVA, VBVALEXTOEN, PULLDOWNEN
+#define STM32_GCCFG_VBVALOVAL_Pos (23U)
+#define STM32_GCCFG_VBVALOVAL_Msk (0x1U << STM32_GCCFG_VBVALOVAL_Pos) // 0x00800000
+#define STM32_GCCFG_VBVALOVAL STM32_GCCFG_VBVALOVAL_Msk // Value of VBUSVLDEXT0 femtoPHY input
+
+#define STM32_GCCFG_VBVALEXTOEN_Pos (24U)
+#define STM32_GCCFG_VBVALEXTOEN_Msk (0x1U << STM32_GCCFG_VBVALEXTOEN_Pos) // 0x01000000
+#define STM32_GCCFG_VBVALEXTOEN STM32_GCCFG_VBVALEXTOEN_Msk // Enables of VBUSVLDEXT0 femtoPHY input override
+
+#define STM32_GCCFG_PULLDOWNEN_Pos (25U)
+#define STM32_GCCFG_PULLDOWNEN_Msk (0x1U << STM32_GCCFG_PULLDOWNEN_Pos) // 0x02000000
+#define STM32_GCCFG_PULLDOWNEN STM32_GCCFG_PULLDOWNEN_Msk // Enables of femtoPHY pulldown resistors, used when ID PAD is disabled
+// GUID >= 0x5000: end
/******************** Bit definition for DEACHINTMSK register ********************/
#define DEACHINTMSK_IEP1INTM_Pos (1U)
diff --git a/src/portable/synopsys/dwc2/dwc2_xmc.h b/src/portable/synopsys/dwc2/dwc2_xmc.h
index 63419abf7..aca3873df 100644
--- a/src/portable/synopsys/dwc2/dwc2_xmc.h
+++ b/src/portable/synopsys/dwc2/dwc2_xmc.h
@@ -71,6 +71,13 @@ static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
//USB->ROUTE = USB_ROUTE_PHYPEN;
}
+// MCU specific PHY deinit, disable PHY power
+static inline void dwc2_phy_deinit(dwc2_regs_t * dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ (void) hs_phy_type;
+ // nothing to do
+}
+
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
{
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index fc748c85f..e12e44a41 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -111,7 +111,8 @@ typedef struct {
hcd_endpoint_t edpt[CFG_TUH_DWC2_ENDPOINT_MAX];
} hcd_data_t;
-hcd_data_t _hcd_data;
+static hcd_data_t _hcd_data;
+static tuh_configure_dwc2_t _tuh_cfg = {.use_hs_phy = TUH_OPT_HIGH_SPEED};
//--------------------------------------------------------------------
//
@@ -350,7 +351,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t pa
* TX periodic (PTX)
* - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt)
*/
-static void dfifo_host_init(uint8_t rhport) {
+static void dfifo_host_init(uint8_t rhport, bool is_hs_phy) {
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
@@ -363,10 +364,9 @@ static void dfifo_host_init(uint8_t rhport) {
}
// fixed allocation for now, improve later:
- // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
- bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
- uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4;
- uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
+ // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
+ uint32_t nptx_largest = is_hs_phy ? TUSB_EPSIZE_BULK_HS / 4 : TUSB_EPSIZE_BULK_FS / 4;
+ uint32_t ptx_largest = is_hs_phy ? TUSB_EPSIZE_ISO_HS_MAX / 4 : 256 / 4;
uint16_t nptxfsiz = 2 * nptx_largest;
uint16_t rxfsiz = 2 * (ptx_largest + 2) + ghwcfg2.num_host_ch;
@@ -392,29 +392,23 @@ static void dfifo_host_init(uint8_t rhport) {
// optional hcd configuration, called by tuh_configure()
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
(void) rhport;
- (void) cfg_id;
- (void) cfg_param;
-
+ TU_VERIFY(cfg_id == TUH_CFGID_DWC2 && cfg_param != NULL);
+ tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
+ _tuh_cfg = cfg->dwc2;
return true;
}
// Initialize controller to host mode
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
tu_memclr(&_hcd_data, sizeof(_hcd_data));
// Core Initialization
- const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ const bool is_hs_phy = dwc2_core_is_highspeed_phy(dwc2, _tuh_cfg.use_hs_phy);
const bool is_dma = dma_host_enabled(dwc2);
- TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+ TU_ASSERT(dwc2_core_init(rhport, is_hs_phy, is_dma));
//------------- 3.1 Host Initialization -------------//
-
- // work at max supported speed
- dwc2->hcfg &= ~HCFG_FSLS_ONLY;
-
// Enable HFIR reload
if (dwc2->gsnpsid >= DWC2_CORE_REV_2_92a) {
dwc2->hfir |= HFIR_RELOAD_CTRL;
@@ -422,20 +416,26 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// force host mode and wait for mode switch
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
-#if CFG_TUSB_MCU == OPT_MCU_STM32N6
- // No hardware detection of Vbus B-session is available on the STM32N6
- dwc2->stm32_gccfg &= ~STM32_GCCFG_VBVALOVAL;
-#endif
while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
- // configure fixed-allocated fifo scheme
- dfifo_host_init(rhport);
+ #ifdef TUP_USBIP_DWC2_STM32
+ dwc2_stm32_gccfg_cfg(dwc2, false, true);
+ #endif
+
+ if (is_hs_phy && (rh_init->speed == TUSB_SPEED_HIGH || rh_init->speed == TUSB_SPEED_AUTO)) {
+ dwc2->hcfg &= ~HCFG_FSLS_ONLY; // max speed
+ } else {
+ dwc2->hcfg |= HCFG_FSLS_ONLY; // disable high speed mode
+ }
+
+ // configure a fixed-allocated fifo scheme
+ dfifo_host_init(rhport, is_hs_phy);
dwc2->hprt = HPRT_W1_MASK; // clear all write-1-clear bits
dwc2->hprt = HPRT_POWER; // turn on VBUS
// Enable required interrupts
- dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT | GINTSTS_DISCINT;
+ dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_HPRTINT | GINTSTS_HCINT | GINTSTS_DISCINT;
// NPTX can hold at least 2 packet, change interrupt level to half-empty
uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
@@ -445,6 +445,17 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
+bool hcd_deinit(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Turn off VBUS
+ dwc2->hprt = HPRT_W1_MASK; // clear w1c bits without side effects
+ // HPRT_POWER is not set -> VBUS off
+
+ dwc2_core_deinit(rhport);
+ return true;
+}
+
// Enable USB interrupt
void hcd_int_enable (uint8_t rhport) {
dwc2_int_set(rhport, TUSB_ROLE_HOST, true);
@@ -856,7 +867,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
if (byte_count > 0) {
- dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count);
+ tu_hwfifo_read(dwc2->fifo[0], edpt->buffer + xfer->xferred_bytes, byte_count, NULL);
xfer->xferred_bytes += byte_count;
xfer->fifo_bytes = byte_count;
}
@@ -869,7 +880,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
break;
case GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR:
- TU_ASSERT(0, ); // maybe try to change DToggle
+ // handle in channel interrupt
break;
case GRXSTS_PKTSTS_HOST_CHANNEL_HALTED:
@@ -907,7 +918,7 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
return true;
}
- dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->fifo_bytes, xact_bytes);
+ tu_hwfifo_write(dwc2->fifo[ch_id], edpt->buffer + xfer->fifo_bytes, xact_bytes, NULL);
xfer->fifo_bytes += xact_bytes;
}
}
@@ -1009,8 +1020,11 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_DATATOGGLE_ERR) {
+ channel->hcintmsk &= ~HCINT_DATATOGGLE_ERR;
xfer->err_count = 0;
- TU_ASSERT(false);
+ hcsplt.split_compl = 0; // restart with start-split
+ channel->hcsplt = hcsplt.value;
+ channel_disable(dwc2, channel);
} else {
// nothing to do
}
@@ -1346,6 +1360,17 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
}
// Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
+// Databook Table 2-2: System Clock Speeds
+// +-----------+------------------+----------+-----------+-------------------+
+// | PHY | PHY Clock (MHz) | Width | HCFG.Sel | HFIR (clk cycles) |
+// +-----------+------------------+----------+-----------+-------------------+
+// | HS UTMI+ | 30 | 16-bit | 30_60 | HS:3749 FS:29999 |
+// | HS UTMI+ | 60 | 8-bit | 30_60 | HS:7499 FS:59999 |
+// | HS ULPI | 60 | 8-bit | 30_60 | HS:7499 FS:59999 |
+// | FS (dead.) | 48 | internal | 48 | FS:47999 |
+// | LS via FS | 48 (6 effective) | internal | 6 | LS:47999 |
+// +-----------+------------------+----------+-----------+-------------------+
+// HFIR = (interval_us * phy_clock) - 1, where interval is 125us (HS) or 1000us (FS/LS)
static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
@@ -1438,16 +1463,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
// TU_LOG1_HEX(gintsts);
- if (gintsts & GINTSTS_CONIDSTSCHNG) {
- // Connector ID status change
- dwc2->gintsts = GINTSTS_CONIDSTSCHNG;
-
- //if (dwc2->gotgctl)
- // dwc2->hprt = HPRT_POWER; // power on port to turn on VBUS
- //dwc2->gintmsk |= GINTMSK_PRTIM;
- // TODO wait for SRP if OTG
- }
-
if (gintsts & GINTSTS_SOF) {
const bool more_sof = handle_sof_irq(rhport, in_isr);
if (!more_sof) {