summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c109
-rw-r--r--src/portable/analog/max3421/hcd_max3421.h63
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_imxrt.h25
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c33
-rw-r--r--src/portable/chipidea/ci_hs/hcd_ci_hs.c9
-rw-r--r--src/portable/ehci/ehci.c272
-rw-r--r--src/portable/ehci/ehci.h278
-rw-r--r--src/portable/mentor/musb/hcd_musb.c30
-rw-r--r--src/portable/microchip/pic/README.md51
-rw-r--r--src/portable/microchip/pic/dcd_pic.c117
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c14
-rw-r--r--src/portable/nxp/khci/dcd_khci.c2
-rw-r--r--src/portable/nxp/khci/hcd_khci.c6
-rw-r--r--src/portable/nxp/lpc17_40/hcd_lpc17_40.c1
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c5
-rw-r--r--src/portable/ohci/ohci.c13
-rw-r--r--src/portable/raspberrypi/pio_usb/hcd_pio_usb.c11
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c57
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c52
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c2
-rw-r--r--src/portable/renesas/rusb2/hcd_rusb2.c12
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c180
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c17
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py2
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h11
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h953
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c332
-rw-r--r--src/portable/template/hcd_template.c53
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c3
31 files changed, 1628 insertions, 1207 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index c5e924266..971dbd62e 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -28,9 +28,9 @@
#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
-#include <stdatomic.h>
#include "host/hcd.h"
#include "host/usbh.h"
+#include "host/usbh_pvt.h"
//--------------------------------------------------------------------+
//
@@ -233,7 +233,7 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
- atomic_flag busy; // busy transferring
+ bool busy_lock; // busy transferring
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@@ -253,28 +253,6 @@ static tuh_configure_max3421_t _tuh_cfg = {
};
//--------------------------------------------------------------------+
-// API: SPI transfer with MAX3421E
-// - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application
-// - reg_read(), reg_write(): is implemented by this driver, can be used by application
-//--------------------------------------------------------------------+
-
-// API to control MAX3421 SPI CS
-extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
-
-// API to transfer data with MAX3421 SPI
-// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
-extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
-
-// API to enable/disable MAX3421 INTR pin interrupt
-extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
-
-// API to read MAX3421's register. Implemented by TinyUSB
-uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
-
-// API to write MAX3421's register. Implemented by TinyUSB
-bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
-
-//--------------------------------------------------------------------+
// SPI Commands and Helper
//--------------------------------------------------------------------+
@@ -349,7 +327,9 @@ TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data
}
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
- if ( _hcd_data.peraddr == data ) return; // no need to change address
+ if (_hcd_data.peraddr == data) {
+ return; // no need to change address
+ }
_hcd_data.peraddr = data;
reg_write(rhport, PERADDR_ADDR, data, in_isr);
@@ -395,7 +375,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint
static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
uint8_t hirq;
- uint8_t const reg = RCVVFIFO_ADDR;
+ const uint8_t reg = RCVVFIFO_ADDR;
max3421_spi_lock(rhport, in_isr);
@@ -411,7 +391,7 @@ static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool
//--------------------------------------------------------------------+
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
- uint8_t const is_out = 1-ep_dir;
+ const uint8_t is_out = 1-ep_dir;
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
// control endpoint is bi-direction (skip check)
@@ -632,6 +612,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
if (daddr == 0 && ep_num == 0) {
ep = &_hcd_data.ep[0];
}else {
+ if (NULL != find_ep_not_addr0(daddr, ep_num, ep_dir)) {
+ return true; // already opened
+ }
ep = allocate_ep();
TU_ASSERT(ep);
ep->daddr = daddr;
@@ -645,6 +628,21 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
+ max3421_ep_t * ep = find_ep_not_addr0(daddr, ep_num, ep_dir);
+
+ if (!ep) {
+ return false; // not opened
+ }
+
+ tu_memclr(ep, sizeof(max3421_ep_t));
+
+ return true;
+}
+
/* The microcontroller repeatedly writes the SNDFIFO register R2 to load the FIFO with up to 64 data bytes.
* Then the microcontroller writes the SNDBC register, which this does three things:
* 1. Tells the MAX3421E SIE (Serial Interface Engine) how many bytes in the FIFO to send.
@@ -731,8 +729,8 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@@ -748,8 +746,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_generic(rhport, ep, true, false);
}
@@ -785,8 +792,17 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_setup(rhport, ep, false);
}
@@ -852,8 +868,8 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
}
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
- uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
- uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
+ const uint8_t ep_dir = 1 - ep->hxfr_bm.is_out;
+ const uint8_t ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
// save data toggle
if (ep_dir) {
@@ -871,7 +887,9 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
xact_generic(rhport, next_ep, true, in_isr);
}else {
// no more pending
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
}
@@ -910,7 +928,9 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xact_generic(rhport, next_ep, true, in_isr);
} else {
// no more pending in this frame -> clear busy
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
return;
@@ -1001,8 +1021,8 @@ void print_hirq(uint8_t hirq) {
// Interrupt handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
- if (!hirq) return;
-// print_hirq(hirq);
+ if (!hirq) { return; }
+ // print_hirq(hirq);
if (hirq & HIRQ_FRAME_IRQ) {
_hcd_data.frame_count++;
@@ -1021,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
- if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
- xact_generic(rhport, ep_retry, true, in_isr);
+ if (ep_retry != NULL) {
+ bool has_xfer = false;
+
+ usbh_spin_lock(in_isr);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(in_isr);
+
+ if (has_xfer) {
+ xact_generic(rhport, ep_retry, true, in_isr);
+ }
}
}
diff --git a/src/portable/analog/max3421/hcd_max3421.h b/src/portable/analog/max3421/hcd_max3421.h
new file mode 100644
index 000000000..4631fa21a
--- /dev/null
+++ b/src/portable/analog/max3421/hcd_max3421.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+#ifndef TUSB_HCD_MAX3421_H
+#define TUSB_HCD_MAX3421_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// SPI transfer API with MAX3421E are implemented by application
+// - spi_cs_api(), spi_xfer_api(), int_api()
+//--------------------------------------------------------------------+
+
+// API to control MAX3421 SPI CS
+extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
+
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
+
+// API to enable/disable MAX3421 INTR pin interrupt
+extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
+
+//--------------------------------------------------------------------+
+// API for read/write MAX3421 registers
+// are implemented by this driver, can be used by application
+//--------------------------------------------------------------------+
+
+// API to read MAX3421's register. Implemented by TinyUSB
+uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
+
+// API to write MAX3421's register. Implemented by TinyUSB
+bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
index c59c107ff..75d1d55b8 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
@@ -56,14 +56,23 @@ static const ci_hs_controller_t _ci_controller[] =
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
//------------- DCD -------------//
-#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)
//------------- HCD -------------//
-#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)
//------------- DCache -------------//
+#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
+#if __CORTEX_M == 7
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) {
+ size = (size & ~(CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) + CFG_TUD_MEM_DCACHE_LINE_SIZE;
+ }
+ return size;
+}
+
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_is_cache_mem(uintptr_t addr) {
return !(0x20000000 <= addr && addr < 0x20100000);
}
@@ -72,6 +81,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean(void const* addr, ui
const uintptr_t addr32 = (uintptr_t) addr;
if (imxrt_is_cache_mem(addr32)) {
TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
}
return true;
@@ -84,6 +94,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_invalidate(void const* add
// *very* careful when we do it. If we're not aligned, then we risk resetting
// values back to their RAM state.
TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
}
return true;
@@ -93,9 +104,15 @@ TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean_invalidate(void cons
const uintptr_t addr32 = (uintptr_t) addr;
if (imxrt_is_cache_mem(addr32)) {
TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
}
return true;
}
+#elif __CORTEX_M == 4
+#error "Secondary M4 core's cache controller is not supported yet."
+#endif
+#endif
+
#endif
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index d9f7ca8ea..244f5a2d4 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -34,17 +34,19 @@
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
#include "ci_hs_imxrt.h"
- 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(void const* 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(void const* 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);
- }
+bool dcd_dcache_clean_invalidate(void const* 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"
@@ -237,7 +239,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
usbmode |= USBMODE_CM_DEVICE;
dcd_reg->USBMODE = usbmode;
+#ifdef CFG_TUD_CI_HS_VBUS_CHARGE
+ dcd_reg->OTGSC = OTGSC_VBUS_CHARGE | OTGSC_OTG_TERMINATION;
+#else
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
+#endif
#if !TUD_OPT_HIGH_SPEED
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
@@ -311,9 +317,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
- // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
- // address to 32-byte boundaries. Buffer must be word aligned
- dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
+ dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes);
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
@@ -479,7 +483,9 @@ 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
// 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)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
@@ -525,8 +531,6 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
page++;
}
}
-
- dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
}
else
{
@@ -541,6 +545,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
return true;
}
+#endif
//--------------------------------------------------------------------+
// ISR
diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
index 14f8acb45..b5324a754 100644
--- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
@@ -35,6 +35,7 @@
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "host/hcd.h"
+#include "host/usbh.h"
#include "portable/ehci/ehci_api.h"
#include "ci_hs_type.h"
@@ -42,6 +43,7 @@
#include "ci_hs_imxrt.h"
+#if CFG_TUH_MEM_DCACHE_ENABLE
bool hcd_dcache_clean(void const* addr, uint32_t data_size) {
return imxrt_dcache_clean(addr, data_size);
}
@@ -53,6 +55,7 @@ bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) {
bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
return imxrt_dcache_clean_invalidate(addr, data_size);
}
+#endif
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
@@ -90,12 +93,6 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
hcd_reg->USBMODE = USBMODE_CM_HOST;
#endif
- // FIXME force full speed, still have issue with Highspeed enumeration
- // probably due to physical connection bouncing when plug/unplug
- // 1. Have issue when plug/unplug devices, maybe the port is not reset properly
- // 2. Also does not seems to detect disconnection
- hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
-
return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
}
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index 01bbf62bf..da9f49d29 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -34,6 +34,7 @@
#include "osal/osal.h"
#include "host/hcd.h"
+#include "host/usbh.h"
#include "ehci_api.h"
#include "ehci.h"
@@ -62,8 +63,7 @@
#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB)
#define QTD_MAX QHD_MAX
-typedef struct
-{
+typedef struct {
ehci_link_t period_framelist[FRAMELIST_SIZE];
// TODO only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
@@ -139,6 +139,12 @@ static ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd);
static void qhd_remove_qtd(ehci_qhd_t *qhd);
+TU_ATTR_ALWAYS_INLINE static inline bool qhd_is_periodic(ehci_qhd_t const *qhd) {
+ return qhd->int_smask != 0;
+}
+TU_ATTR_ALWAYS_INLINE static inline uint8_t qhd_ep_addr(ehci_qhd_t const *qhd) {
+ return tu_edpt_addr(qhd->ep_number, qhd->pid);
+}
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr);
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_find_free (void);
@@ -146,9 +152,10 @@ static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes)
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms);
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport);
-TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next (ehci_link_t const *p_link);
-static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr);
+TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t *current, ehci_link_t *entry, uint8_t type);
+TU_ATTR_ALWAYS_INLINE static inline void list_remove(ehci_link_t* head, ehci_link_t* prev, ehci_qhd_t* qhd);
+static void list_remove_qhd_by_addr(ehci_link_t *list_head, uint8_t dev_addr, uint8_t ep_addr);
static void ehci_disable_schedule(ehci_registers_t* regs, bool is_period) {
// maybe have a timeout for status
@@ -175,15 +182,12 @@ static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) {
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
-
-uint32_t hcd_frame_number(uint8_t rhport)
-{
+uint32_t hcd_frame_number(uint8_t rhport) {
(void) rhport;
return (ehci_data.uframe_number + ehci_data.regs->frame_index) >> 3;
}
-void hcd_port_reset(uint8_t rhport)
-{
+void hcd_port_reset(uint8_t rhport) {
(void) rhport;
ehci_registers_t* regs = ehci_data.regs;
@@ -204,8 +208,7 @@ void hcd_port_reset(uint8_t rhport)
regs->portsc = portsc;
}
-void hcd_port_reset_end(uint8_t rhport)
-{
+void hcd_port_reset_end(uint8_t rhport) {
(void) rhport;
ehci_registers_t* regs = ehci_data.regs;
@@ -221,32 +224,29 @@ void hcd_port_reset_end(uint8_t rhport)
regs->portsc = portsc;
}
-bool hcd_port_connect_status(uint8_t rhport)
-{
+bool hcd_port_connect_status(uint8_t rhport) {
(void) rhport;
return ehci_data.regs->portsc_bm.current_connect_status;
}
-tusb_speed_t hcd_port_speed_get(uint8_t rhport)
-{
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
(void) rhport;
return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
}
// Close all opened endpoint belong to this device
-void hcd_device_close(uint8_t rhport, uint8_t daddr)
-{
+void hcd_device_close(uint8_t rhport, uint8_t daddr) {
// skip dev0
if (daddr == 0) {
return;
}
- // Remove from async list
- list_remove_qhd_by_daddr((ehci_link_t *) list_get_async_head(rhport), daddr);
+ // Remove from async list all endpoints of this device
+ list_remove_qhd_by_addr((ehci_link_t *) list_get_async_head(rhport), daddr, TUSB_INDEX_INVALID_8);
- // Remove from all interval period list
- for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) {
- list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], daddr);
+ // Remove from all interval period list of this device
+ for (uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) {
+ list_remove_qhd_by_addr((ehci_link_t *) &ehci_data.period_head_arr[i], daddr, TUSB_INDEX_INVALID_8);
}
// Async doorbell (EHCI 4.8.2 for operational details)
@@ -358,12 +358,10 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
}
#if 0
-static void ehci_stop(uint8_t rhport)
-{
+static void ehci_stop(uint8_t rhport) {
(void) rhport;
ehci_registers_t* regs = ehci_data.regs;
-
regs->command_bm.run_stop = 0;
// USB Spec: controller has to stop within 16 uframe = 2 frames
@@ -375,41 +373,46 @@ static void ehci_stop(uint8_t rhport)
// 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, tusb_desc_endpoint_t const * ep_desc) {
// TODO not support ISO yet
TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
//------------- Prepare Queue Head -------------//
- ehci_qhd_t *p_qhd = (ep_desc->bEndpointAddress == 0) ? qhd_control(dev_addr) : qhd_find_free();
+ ehci_qhd_t *p_qhd;
+ if (ep_desc->bEndpointAddress == 0) {
+ p_qhd = qhd_control(dev_addr);
+ } else {
+ if (NULL != qhd_get_from_addr(dev_addr, ep_desc->bEndpointAddress)) {
+ return true; // already opened
+ }
+ p_qhd = qhd_find_free();
+ }
TU_ASSERT(p_qhd);
-
qhd_init(p_qhd, dev_addr, ep_desc);
- // control of dev0 is always present as async head
- if ( dev_addr == 0 ) return true;
+ // control of dev0 always exists as async head
+ if (dev_addr == 0) {
+ return true;
+ }
// Insert to list
ehci_link_t * list_head = NULL;
-
- switch (ep_desc->bmAttributes.xfer)
- {
+ switch (ep_desc->bmAttributes.xfer) {
case TUSB_XFER_CONTROL:
case TUSB_XFER_BULK:
- list_head = (ehci_link_t*) list_get_async_head(rhport);
- break;
+ list_head = (ehci_link_t *) list_get_async_head(rhport);
+ break;
case TUSB_XFER_INTERRUPT:
list_head = list_get_period_head(rhport, p_qhd->interval_ms);
- break;
+ break;
case TUSB_XFER_ISOCHRONOUS:
// TODO iso is not supported
- break;
+ break;
- default: break;
+ default:
+ break;
}
TU_ASSERT(list_head);
@@ -421,8 +424,23 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
-{
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ ehci_qhd_t* qhd = qhd_get_from_addr(daddr, ep_addr);
+ TU_VERIFY(qhd != NULL);
+
+ ehci_link_t * list_head;
+ if (qhd_is_periodic(qhd)) {
+ // interrupt endpoint
+ list_head = list_get_period_head(rhport, qhd->interval_ms);;
+ } else {
+ list_head = (ehci_link_t *) list_get_async_head(rhport);
+ }
+
+ list_remove_qhd_by_addr(list_head, daddr, ep_addr);
+ return true;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
(void) rhport;
ehci_qhd_t* qhd = &ehci_data.control[dev_addr].qhd;
@@ -444,14 +462,14 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
return true;
}
-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;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ TU_VERIFY(qhd != NULL);
ehci_qtd_t* qtd;
if (epnum == 0) {
@@ -540,8 +558,7 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
// This isr mean it is safe to modify previously removed queue head from async list.
// In tinyusb, queue head is only removed when device is unplugged.
TU_ATTR_ALWAYS_INLINE static inline
-void async_advance_isr(uint8_t rhport)
-{
+void async_advance_isr(uint8_t rhport) {
(void) rhport;
ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
@@ -612,8 +629,7 @@ void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
}
TU_ATTR_ALWAYS_INLINE static inline
-void proccess_async_xfer_isr(ehci_qhd_t * const list_head)
-{
+void proccess_async_xfer_isr(ehci_qhd_t * const list_head) {
ehci_qhd_t *qhd = list_head;
do {
@@ -623,8 +639,7 @@ void proccess_async_xfer_isr(ehci_qhd_t * const list_head)
}
TU_ATTR_ALWAYS_INLINE static inline
-void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms)
-{
+void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms) {
uint32_t const period_1ms_addr = (uint32_t) list_get_period_head(rhport, 1u);
ehci_link_t next_link = *list_get_period_head(rhport, interval_ms);
@@ -726,51 +741,55 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next(ehci_link_t const *p_
return (ehci_link_t*) tu_align32(p_link->address);
}
-TU_ATTR_ALWAYS_INLINE static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
-{
- new->address = current->address;
- current->address = ((uint32_t) new) | (new_type << 1);
+TU_ATTR_ALWAYS_INLINE static inline void list_insert(ehci_link_t *current, ehci_link_t *entry, uint8_t type) {
+ entry->address = current->address;
+ current->address = ((uint32_t) entry) | (type << 1);
}
-// Remove all queue head belong to this device address
-static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) {
- ehci_link_t* prev = list_head;
+// Remove a queue head from the list.
+// Per EHCI 4.8.2 the removed qhd's next is linked to list head (which always reachable by Host Controller)
+// TODO support iTD/siTD
+TU_ATTR_ALWAYS_INLINE static inline void list_remove(ehci_link_t* head, ehci_link_t* prev, ehci_qhd_t* qhd) {
+ // TODO deactivate all TD, wait for QHD to inactive before removal
+ prev->address = qhd->next.address;
+
+ // link the removed qhd's next to list head
+ qhd->next.address = ((uint32_t) head) | (EHCI_QTYPE_QHD << 1);
+
+ if (qhd_is_periodic(qhd)) {
+ // period list queue element is guarantee to be free in the next frame (1 ms)
+ qhd->used = 0;
+ } else {
+ // async list use async advance handshake. Mark as removing, will completely re-usable when async advance isr occurs
+ qhd->removing = 1;
+ }
+
+ hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
+ hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
+}
+
+// Remove queue head belong to this device address
+static void list_remove_qhd_by_addr(ehci_link_t *list_head, uint8_t dev_addr, uint8_t ep_addr) {
+ ehci_link_t *prev = list_head;
while (prev && !prev->terminate) {
- ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev);
+ ehci_qhd_t *qhd = (ehci_qhd_t *) (uintptr_t) list_next(prev);
// done if loop back to head
- if ( (uintptr_t) qhd == (uintptr_t) list_head) {
+ if ((uintptr_t) qhd == (uintptr_t) list_head) {
break;
}
- if ( qhd->dev_addr == dev_addr ) {
- // TODO deactivate all TD, wait for QHD to inactive before removal
- prev->address = qhd->next.address;
-
- // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
- qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
-
- if ( qhd->int_smask )
- {
- // period list queue element is guarantee to be free in the next frame (1 ms)
- qhd->used = 0;
- }else
- {
- // async list use async advance handshake
- // mark as removing, will completely re-usable when async advance isr occurs
- qhd->removing = 1;
- }
-
- hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
- hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
- }else {
+ // ep_addr is 0xff means all endpoints of this device address
+ if (qhd->dev_addr == dev_addr &&
+ (ep_addr == TUSB_INDEX_INVALID_8 || qhd_ep_addr(qhd) == ep_addr)) {
+ list_remove(list_head, prev, qhd);
+ } else {
prev = list_next(prev);
}
}
}
-
//--------------------------------------------------------------------+
// Queue Header helper
//--------------------------------------------------------------------+
@@ -782,8 +801,10 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) {
// Find a free queue head
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_find_free(void) {
- for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
- if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
+ for (uint32_t i = 0; i < QHD_MAX; i++) {
+ if (!ehci_data.qhd_pool[i].used) {
+ return &ehci_data.qhd_pool[i];
+ }
}
return NULL;
}
@@ -800,10 +821,9 @@ static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
}
ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
-
- for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
- if ( (qhd_pool[i].dev_addr == dev_addr) &&
- ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) ) {
+ for (uint32_t i = 0; i < QHD_MAX; i++) {
+ if ((qhd_pool[i].dev_addr == dev_addr) &&
+ ep_addr == qhd_ep_addr(&qhd_pool[i])) {
return &qhd_pool[i];
}
}
@@ -812,15 +832,14 @@ static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
}
// Init queue head with endpoint descriptor
-static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
-{
+static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) {
// address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise)
if (dev_addr != 0) {
tu_memclr(p_qhd, sizeof(ehci_qhd_t));
}
- hcd_devtree_info_t devtree_info;
- hcd_devtree_get_info(dev_addr, &devtree_info);
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
uint8_t const xfer_type = ep_desc->bmAttributes.xfer;
uint8_t const interval = ep_desc->bInterval;
@@ -828,45 +847,49 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
p_qhd->dev_addr = dev_addr;
p_qhd->fl_inactive_next_xact = 0;
p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress);
- p_qhd->ep_speed = devtree_info.speed;
+ p_qhd->ep_speed = bus_info.speed;
p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0;
- p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head
+ p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static async list head
p_qhd->max_packet_size = tu_edpt_packet_size(ep_desc);
p_qhd->fl_ctrl_ep_flag = ((xfer_type == TUSB_XFER_CONTROL) && (p_qhd->ep_speed != TUSB_SPEED_HIGH)) ? 1 : 0;
p_qhd->nak_reload = 0;
- // Bulk/Control -> smask = cmask = 0
- // TODO Isochronous
- if (TUSB_XFER_INTERRUPT == xfer_type)
- {
- if (TUSB_SPEED_HIGH == p_qhd->ep_speed)
- {
- TU_ASSERT( interval <= 16, );
- if ( interval < 4) // sub millisecond interval
- {
- p_qhd->interval_ms = 0;
- p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
- (interval == 2) ? TU_BIN8(10101010) : TU_BIN8(01000100);
- }else
- {
- p_qhd->interval_ms = (uint8_t) tu_min16( 1 << (interval-4), 255 );
- p_qhd->int_smask = TU_BIT(interval % 8);
+ switch (xfer_type) {
+ case TUSB_XFER_CONTROL:
+ case TUSB_XFER_BULK:
+ p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ if (TUSB_SPEED_HIGH == p_qhd->ep_speed) {
+ TU_ASSERT(interval <= 16, );
+ if (interval < 4) {
+ // sub millisecond interval
+ p_qhd->interval_ms = 0;
+ p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
+ (interval == 2) ? TU_BIN8(10101010): TU_BIN8(01000100);
+ } else {
+ p_qhd->interval_ms = (uint8_t) tu_min16(1 << (interval - 4), 255);
+ p_qhd->int_smask = TU_BIT(interval % 8);
+ }
+ } else {
+ TU_ASSERT(0 != interval, );
+ // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes
+ p_qhd->int_smask = 0x01;
+ p_qhd->fl_int_cmask = TU_BIN8(11100);
+ p_qhd->interval_ms = interval;
}
- }else
- {
- TU_ASSERT( 0 != interval, );
- // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes
- p_qhd->int_smask = 0x01;
- p_qhd->fl_int_cmask = TU_BIN8(11100);
- p_qhd->interval_ms = interval;
- }
- }else
- {
- p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
+ break;
+
+ case TUSB_XFER_ISOCHRONOUS:
+ // TODO not support ISO yet
+ break;
+
+ default: break;
}
- p_qhd->fl_hub_addr = devtree_info.hub_addr;
- p_qhd->fl_hub_port = devtree_info.hub_port;
+ p_qhd->fl_hub_addr = bus_info.hub_addr;
+ p_qhd->fl_hub_port = bus_info.hub_port;
p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
//------------- HCD Management Data -------------//
@@ -880,8 +903,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
p_qhd->qtd_overlay.next.terminate = 1;
p_qhd->qtd_overlay.alternate.terminate = 1;
- if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT)
- {
+ if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT) {
p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11
}
}
diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h
index 457adc1d3..87659701b 100644
--- a/src/portable/ehci/ehci.h
+++ b/src/portable/ehci/ehci.h
@@ -49,15 +49,14 @@
// TODO merge OHCI with EHCI
enum {
- EHCI_MAX_ITD = 4,
+ EHCI_MAX_ITD = 4,
EHCI_MAX_SITD = 16
};
//--------------------------------------------------------------------+
// EHCI Data Structure
//--------------------------------------------------------------------+
-enum
-{
+enum {
EHCI_QTYPE_ITD = 0 ,
EHCI_QTYPE_QHD ,
EHCI_QTYPE_SITD ,
@@ -65,8 +64,7 @@ enum
};
/// EHCI PID
-enum
-{
+enum {
EHCI_PID_OUT = 0 ,
EHCI_PID_IN ,
EHCI_PID_SETUP
@@ -74,187 +72,182 @@ enum
/// Link pointer
typedef union {
- uint32_t address;
- struct {
- uint32_t terminate : 1;
- uint32_t type : 2;
- };
-}ehci_link_t;
+ uint32_t address;
+ struct {
+ uint32_t terminate : 1;
+ uint32_t type : 2;
+ };
+} ehci_link_t;
TU_VERIFY_STATIC( sizeof(ehci_link_t) == 4, "size is not correct" );
/// Queue Element Transfer Descriptor
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
-typedef struct
-{
- // Word 0: Next QTD Pointer
- ehci_link_t next;
-
- // Word 1: Alternate Next QTD Pointer (not used)
- union{
- ehci_link_t alternate;
- struct {
- uint32_t : 5;
- uint32_t used : 1;
- uint32_t : 10;
- uint32_t expected_bytes : 16;
- };
- };
+typedef struct {
+ // Word 0 Next QTD Pointer
+ ehci_link_t next;
- // Word 2: qTQ Token
- volatile uint32_t ping_err : 1 ; ///< For Highspeed: 0 Out, 1 Ping. Full/Slow used as error indicator
- volatile uint32_t non_hs_split_state : 1 ; ///< Used by HC to track the state of split transaction
- volatile uint32_t non_hs_missed_uframe : 1 ; ///< HC misses a complete split transaction
- volatile uint32_t xact_err : 1 ; ///< Error (Timeout, CRC, Bad PID ... )
- volatile uint32_t babble_err : 1 ; ///< Babble detected, also set Halted bit to 1
- volatile uint32_t buffer_err : 1 ; ///< Data overrun/underrun error
- volatile uint32_t halted : 1 ; ///< Serious error or STALL received
- volatile uint32_t active : 1 ; ///< Start transfer, clear by HC when complete
+ // Word 1 Alternate Next QTD Pointer (not used)
+ union {
+ ehci_link_t alternate;
+ struct {
+ uint32_t : 5;
+ uint32_t used : 1;
+ uint32_t : 10;
+ uint32_t expected_bytes : 16;
+ };
+ };
- uint32_t pid : 2 ; ///< 0: OUT, 1: IN, 2 Setup
- volatile uint32_t err_count : 2 ; ///< Error Counter of consecutive errors
- volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list
- uint32_t int_on_complete : 1 ; ///< Interrupt on complete
- volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction
- volatile uint32_t data_toggle : 1 ; ///< Data Toggle bit
+ // Word 2 qTQ Token
+ volatile uint32_t ping_err : 1; // For Highspeed: 0 Out, 1 Ping. Full/Slow used as error indicator
+ volatile uint32_t non_hs_split_state : 1; // Used by HC to track the state of split transaction
+ volatile uint32_t non_hs_missed_uframe : 1; // HC misses a complete split transaction
+ volatile uint32_t xact_err : 1; // Error (Timeout, CRC, Bad PID ... )
+ volatile uint32_t babble_err : 1; // Babble detected, also set Halted bit to 1
+ volatile uint32_t buffer_err : 1; // Data overrun/underrun error
+ volatile uint32_t halted : 1; // Serious error or STALL received
+ volatile uint32_t active : 1; // Start transfer, clear by HC when complete
+ uint32_t pid : 2; // 0: OUT, 1: IN, 2 Setup
+ volatile uint32_t err_count : 2; // Error Counter of consecutive errors
+ volatile uint32_t current_page : 3; // Index into the qTD buffer pointer list
+ uint32_t int_on_complete : 1; // Interrupt on complete
+ volatile uint32_t total_bytes : 15; // Transfer bytes, decreased during transaction
+ volatile uint32_t data_toggle : 1; // Data Toggle bit
- /// 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];
+ // 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];
} ehci_qtd_t;
TU_VERIFY_STATIC( sizeof(ehci_qtd_t) == 32, "size is not correct" );
/// Queue Head
-typedef struct TU_ATTR_ALIGNED(32)
-{
- // Word 0: Next QHD
- ehci_link_t next;
+typedef struct TU_ATTR_ALIGNED(32) {
+ // Word 0 Next QHD
+ ehci_link_t next;
- // Word 1: Endpoint Characteristics
- uint32_t dev_addr : 7 ; ///< device address
- uint32_t fl_inactive_next_xact : 1 ; ///< Only valid for Periodic with Full/Slow speed
- uint32_t ep_number : 4 ; ///< EP number
- uint32_t ep_speed : 2 ; ///< 0: Full, 1: Low, 2: High
- uint32_t data_toggle_control : 1 ; ///< 0: use DT in qHD, 1: use DT in qTD
- uint32_t head_list_flag : 1 ; ///< Head of the queue
- uint32_t max_packet_size : 11 ; ///< Max packet size
- uint32_t fl_ctrl_ep_flag : 1 ; ///< 1 if is Full/Low speed control endpoint
- uint32_t nak_reload : 4 ; ///< Used by HC
+ // Word 1 Endpoint Characteristics
+ uint32_t dev_addr : 7; // device address
+ uint32_t fl_inactive_next_xact : 1; // Only valid for Periodic with Full/Slow speed
+ uint32_t ep_number : 4; // EP number
+ uint32_t ep_speed : 2; // Full (0), Low (1), High (2)
+ uint32_t data_toggle_control : 1; // 0 use DT in qHD, 1 use DT in qTD
+ uint32_t head_list_flag : 1; // Head of the queue
+ uint32_t max_packet_size : 11; // Max packet size
+ uint32_t fl_ctrl_ep_flag : 1; // 1 if is Full/Low speed control endpoint
+ uint32_t nak_reload : 4; // Used by HC
- // Word 2: Endpoint Capabilities
- uint32_t int_smask : 8 ; ///< Interrupt Schedule Mask
- uint32_t fl_int_cmask : 8 ; ///< Split Completion Mask for Full/Slow speed
- uint32_t fl_hub_addr : 7 ; ///< Hub Address for Full/Slow speed
- uint32_t fl_hub_port : 7 ; ///< Hub Port for Full/Slow speed
- uint32_t mult : 2 ; ///< Transaction per micro frame
+ // Word 2 Endpoint Capabilities
+ uint32_t int_smask : 8; // Interrupt Schedule Mask
+ uint32_t fl_int_cmask : 8; // Split Completion Mask for Full/Slow speed
+ uint32_t fl_hub_addr : 7; // Hub Address for Full/Slow speed
+ uint32_t fl_hub_port : 7; // Hub Port for Full/Slow speed
+ uint32_t mult : 2; // Transaction per micro frame
- // Word 3: Current qTD Pointer
- volatile uint32_t qtd_addr;
+ // Word 3 Current qTD Pointer
+ volatile uint32_t qtd_addr;
- // Word 4-11: Transfer Overlay
- volatile ehci_qtd_t qtd_overlay;
+ // Word 4-11 Transfer Overlay
+ volatile ehci_qtd_t qtd_overlay;
- //--------------------------------------------------------------------+
+ //--------------------------------------------------------------------+
/// Due to the fact QHD is 32 bytes aligned but occupies only 48 bytes
- /// thus there are 16 bytes padding free that we can make use of.
+ /// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
- uint8_t used;
- uint8_t removing; // removed from asyn list, waiting for async advance
- uint8_t pid;
- uint8_t interval_ms; // polling interval in frames (or millisecond)
+ uint8_t used;
+ uint8_t removing;// removed from asyn list, waiting for async advance
+ uint8_t pid;
+ uint8_t interval_ms;// polling interval in frames (or millisecond)
- uint8_t TU_RESERVED[4];
+ uint8_t TU_RESERVED[4];
// Attached TD management, note usbh will only queue 1 TD per QHD.
// buffer for dcache invalidate since td's buffer is modified by HC and finding initial buffer address is not trivial
uint32_t attached_buffer;
- ehci_qtd_t * volatile attached_qtd;
+ ehci_qtd_t *volatile attached_qtd;
} ehci_qhd_t;
-
TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
/// Highspeed Isochronous Transfer Descriptor (section 3.3)
typedef struct TU_ATTR_ALIGNED(32) {
- // Word 0: Next Link Pointer
- ehci_link_t next;
+ // Word 0: Next Link Pointer
+ ehci_link_t next;
- // Word 1-8: iTD Transaction Status and Control List
- struct {
- // iTD Control
- volatile uint32_t offset : 12 ; ///< This field is a value that is an offset, expressed in bytes, from the beginning of a buffer.
- volatile uint32_t page_select : 3 ; ///< These bits are set by software to indicate which of the buffer page pointers the offset field in this slot should be concatenated to produce the starting memory address for this transaction. The valid range of values for this field is 0 to 6
- uint32_t int_on_complete : 1 ; ///< If this bit is set to a one, it specifies that when this transaction completes, the Host Controller should issue an interrupt at the next interrupt threshold
- volatile uint32_t length : 12 ; ///< For an OUT, this field is the number of data bytes the host controller will send during the transaction. The host controller is not required to update this field to reflect the actual number of bytes transferred during the transfer
- ///< For an IN, the initial value of the field is the number of bytes the host expects the endpoint to deliver. During the status update, the host controller writes back the number of bytes successfully received. The value in this register is the actual byte count
- // iTD Status
- volatile uint32_t error : 1 ; ///< Set to a one by the Host Controller during status update in the case where the host did not receive a valid response from the device (Timeout, CRC, Bad PID, etc.). This bit may only be set for isochronous IN transactions.
- volatile uint32_t babble_err : 1 ; ///< Set to a 1 by the Host Controller during status update when a babble is detected during the transaction
- volatile uint32_t buffer_err : 1 ; ///< Set to a 1 by the Host Controller during status update to indicate that the Host Controller is unable to keep up with the reception of incoming data (overrun) or is unable to supply data fast enough during transmission (underrun).
- volatile uint32_t active : 1 ; ///< Set to 1 by software to enable the execution of an isochronous transaction by the Host Controller
- } xact[8];
+ // Word 1-8: iTD Transaction Status and Control List
+ struct {
+ // iTD Control
+ volatile uint32_t offset : 12; // offset in bytes, from the beginning of a buffer.
+ volatile uint32_t page_select : 3; // buffer page pointers the offset field in this slot should be concatenated to produce the starting memory address for this transaction. The valid range of values for this field is 0 to 6
+ uint32_t int_on_complete : 1; // If this bit is set to a one, it specifies that when this transaction completes, the Host Controller should issue an interrupt at the next interrupt threshold
+ volatile uint32_t length : 12; // For an OUT, this field is the number of data bytes the host controller will send during the transaction. The host controller is not required to update this field to reflect the actual number of bytes transferred during the transfer
+ // For an IN, the initial value of the field is the number of bytes the host expects the endpoint to deliver. During the status update, the host controller writes back the number of bytes successfully received. The value in this register is the actual byte count
+ // iTD Status
+ volatile uint32_t error : 1; // Set to a one by the Host Controller during status update in the case where the host did not receive a valid response from the device (Timeout, CRC, Bad PID, etc.). This bit may only be set for isochronous IN transactions.
+ volatile uint32_t babble_err : 1; // Set to a 1 by the Host Controller during status update when a babble is detected during the transaction
+ volatile uint32_t buffer_err : 1; // Set to a 1 by the Host Controller during status update to indicate that the Host Controller is unable to keep up with the reception of incoming data (overrun) or is unable to supply data fast enough during transmission (underrun).
+ volatile uint32_t active : 1; // Set to 1 by software to enable the execution of an isochronous transaction by the Host Controller
+ } xact[8];
- // Word 9-15 Buffer Page Pointer List (Plus)
- uint32_t BufferPointer[7];
+ // Word 9-15 Buffer Page Pointer List (Plus)
+ uint32_t BufferPointer[7];
-// // FIXME: Store meta data into buffer pointer reserved for saving memory
-// /*---------- HCD Area ----------*/
-// uint32_t used;
-// uint32_t IhdIdx;
-// uint32_t reserved[6];
+ // FIXME: Store meta data into buffer pointer reserved for saving memory
+ //---------- HCD Area ----------
+ // uint32_t used;
+ // uint32_t IhdIdx;
+ // uint32_t reserved[6];
} ehci_itd_t;
-
TU_VERIFY_STATIC( sizeof(ehci_itd_t) == 64, "size is not correct" );
/// Split (Full-Speed) Isochronous Transfer Descriptor
-typedef struct TU_ATTR_ALIGNED(32)
-{
+typedef struct TU_ATTR_ALIGNED(32) {
// Word 0: Next Link Pointer
- ehci_link_t next;
+ ehci_link_t next;
- // Word 1: siTD Endpoint Characteristics
- uint32_t dev_addr : 7; ///< This field selects the specific device serving as the data source or sink.
- uint32_t : 1; ///< reserved
- uint32_t ep_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
- uint32_t : 4; ///< This field is reserved and should be set to zero.
- uint32_t hub_addr : 7; ///< This field holds the device address of the transaction translators’ hub.
- uint32_t : 1; ///< reserved
- uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
- uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
+ // Word 1: siTD Endpoint Characteristics
+ uint32_t dev_addr : 7; ///< This field selects the specific device serving as the data source or sink.
+ uint32_t : 1; ///< reserved
+ uint32_t ep_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
+ uint32_t : 4; ///< This field is reserved and should be set to zero.
+ uint32_t hub_addr : 7; ///< This field holds the device address of the transaction translators’ hub.
+ uint32_t : 1; ///< reserved
+ uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
+ uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
- // Word 2: Micro-frame Schedule Control
- uint8_t int_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
- uint8_t fl_int_cmask; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
- uint16_t reserved ; ///< reserved
+ // Word 2: Micro-frame Schedule Control
+ uint8_t int_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
+ uint8_t fl_int_cmask; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
+ uint16_t reserved ; ///< reserved
- // Word 3: siTD Transfer Status and Control
- // Status [7:0] TODO identical to qTD Token'status --> refactor later
- volatile uint32_t : 1 ; // reserved
- volatile uint32_t split_state : 1 ;
- volatile uint32_t missed_uframe : 1 ;
- volatile uint32_t xact_err : 1 ;
- volatile uint32_t babble_err : 1 ;
- volatile uint32_t buffer_err : 1 ;
- volatile uint32_t error : 1 ;
- volatile uint32_t active : 1 ;
- // Micro-frame Schedule Control
- volatile uint32_t cmask_progress : 8 ; ///< This field is used by the host controller to record which split-completes have been executed. See Section 4.12.3.3.2 for behavioral requirements.
- volatile uint32_t total_bytes : 10 ; ///< This field is initialized by software to the total number of bytes expected in this transfer. Maximum value is 1023
- volatile uint32_t : 4 ; ///< reserved
- volatile uint32_t page_select : 1 ; ///< Used to indicate which data page pointer should be concatenated with the CurrentOffsetfield to construct a data buffer pointer
- uint32_t int_on_complete : 1 ; ///< Do not interrupt when transaction is complete. 1 = Do interrupt when transaction is complete
- uint32_t : 0 ; // padding to the end of current storage unit
+ // Word 3: siTD Transfer Status and Control
+ // Status [7:0] TODO identical to qTD Token'status --> refactor later
+ volatile uint32_t : 1 ; // reserved
+ volatile uint32_t split_state : 1 ;
+ volatile uint32_t missed_uframe : 1 ;
+ volatile uint32_t xact_err : 1 ;
+ volatile uint32_t babble_err : 1 ;
+ volatile uint32_t buffer_err : 1 ;
+ volatile uint32_t error : 1 ;
+ volatile uint32_t active : 1 ;
+ // Micro-frame Schedule Control
+ volatile uint32_t cmask_progress : 8 ; ///< This field is used by the host controller to record which split-completes have been executed. See Section 4.12.3.3.2 for behavioral requirements.
+ volatile uint32_t total_bytes : 10 ; ///< This field is initialized by software to the total number of bytes expected in this transfer. Maximum value is 1023
+ volatile uint32_t : 4 ; ///< reserved
+ volatile uint32_t page_select : 1 ; ///< Used to indicate which data page pointer should be concatenated with the CurrentOffsetfield to construct a data buffer pointer
+ uint32_t int_on_complete : 1 ; ///< Do not interrupt when transaction is complete. 1 = Do interrupt when transaction is complete
+ uint32_t : 0 ; // padding to the end of current storage unit
- /// Word 4-5: Buffer Pointer List
- uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
+ /// Word 4-5: Buffer Pointer List
+ uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
- /*---------- Word 6 ----------*/
- ehci_link_t back;
+ /*---------- Word 6 ----------*/
+ ehci_link_t back;
- /// SITD is 32-byte aligned but occupies only 28 --> 4 bytes for storing extra data
- uint8_t used;
- uint8_t ihd_idx;
- uint8_t reserved2[2];
+ /// SITD is 32-byte aligned but occupies only 28 --> 4 bytes for storing extra data
+ uint8_t used;
+ uint8_t ihd_idx;
+ uint8_t reserved2[2];
} ehci_sitd_t;
TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
@@ -315,8 +308,7 @@ enum {
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
};
-typedef volatile struct
-{
+typedef volatile struct {
union {
uint32_t command; // 0x00
diff --git a/src/portable/mentor/musb/hcd_musb.c b/src/portable/mentor/musb/hcd_musb.c
index 1c0740193..dcc023e0b 100644
--- a/src/portable/mentor/musb/hcd_musb.c
+++ b/src/portable/mentor/musb/hcd_musb.c
@@ -36,6 +36,7 @@ _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
#endif
#include "host/hcd.h"
+#include "host/usbh.h"
#include "musb_type.h"
@@ -695,16 +696,16 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
_hcd.pipe0.length = 8;
_hcd.pipe0.remaining = 0;
- hcd_devtree_info_t devtree;
- hcd_devtree_get_info(dev_addr, &devtree);
- switch (devtree.speed) {
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
+ switch (bus_info.speed) {
default: return false;
case TUSB_SPEED_LOW: USB0->TYPE0 = USB_TYPE0_SPEED_LOW; break;
case TUSB_SPEED_FULL: USB0->TYPE0 = USB_TYPE0_SPEED_FULL; break;
case TUSB_SPEED_HIGH: USB0->TYPE0 = USB_TYPE0_SPEED_HIGH; break;
}
- USB0->TXHUBADDR0 = devtree.hub_addr;
- USB0->TXHUBPORT0 = devtree.hub_port;
+ USB0->TXHUBADDR0 = bus_info.hub_addr;
+ USB0->TXHUBPORT0 = bus_info.hub_port;
USB0->TXFUNCADDR0 = dev_addr;
USB0->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_SETUP;
return true;
@@ -744,9 +745,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
pipe->remaining = 0;
uint8_t pipe_type = 0;
- hcd_devtree_info_t devtree;
- hcd_devtree_get_info(dev_addr, &devtree);
- switch (devtree.speed) {
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
+ switch (bus_info.speed) {
default: return false;
case TUSB_SPEED_LOW: pipe_type |= USB_TXTYPE1_SPEED_LOW; break;
case TUSB_SPEED_FULL: pipe_type |= USB_TXTYPE1_SPEED_FULL; break;
@@ -763,8 +764,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
if (dir_tx) {
fadr->TXFUNCADDR = dev_addr;
- fadr->TXHUBADDR = devtree.hub_addr;
- fadr->TXHUBPORT = devtree.hub_port;
+ fadr->TXHUBADDR = bus_info.hub_addr;
+ fadr->TXHUBPORT = bus_info.hub_port;
regs->TXMAXP = mps;
regs->TXTYPE = pipe_type | epn;
regs->TXINTERVAL = ep_desc->bInterval;
@@ -775,8 +776,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
USB0->TXIE |= TU_BIT(pipenum);
} else {
fadr->RXFUNCADDR = dev_addr;
- fadr->RXHUBADDR = devtree.hub_addr;
- fadr->RXHUBPORT = devtree.hub_port;
+ fadr->RXHUBADDR = bus_info.hub_addr;
+ fadr->RXHUBPORT = bus_info.hub_port;
regs->RXMAXP = mps;
regs->RXTYPE = pipe_type | epn;
regs->RXINTERVAL = ep_desc->bInterval;
@@ -804,6 +805,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) 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)
{
(void)rhport;
diff --git a/src/portable/microchip/pic/README.md b/src/portable/microchip/pic/README.md
new file mode 100644
index 000000000..7ba6a4a96
--- /dev/null
+++ b/src/portable/microchip/pic/README.md
@@ -0,0 +1,51 @@
+# Microchip PIC Chipidea FS Driver
+
+This driver adds support for Microchip PIC microcontrollers with full-speed Chipidea USB peripheral to the TinyUSB stack. It supports the following families:
+
+- PIC32MX (untested)
+- PIC32MM
+- PIC32MK (untested)
+- PIC24FJ
+- PIC24EP (untested)
+- dsPIC33EP (untested)
+
+Currently only the device mode is supported.
+
+
+## Important Notes
+
+### Handling of shared VBUS & GPIO pin
+
+Some PICs have the USB VBUS pin bonded with a GPIO pin in the chip package. This driver does **NOT** handle the potential conflict between the VBUS and GPIO functionalities.
+
+Developers must ensure that the GPIO pin is tristated when the VBUS pin is managed by the USB peripheral in order to prevent damaging the chip.
+
+This design choice allows developers the flexibility to use the GPIO functionality for controlling VBUS in device mode if desired.
+
+
+## TODO
+
+### Handle USB remote wakeup timing correctly
+
+The Chipidea FS IP doesn't handle the RESUME signal automatically and it must be managed in software. It needs to be asserted for exactly 10ms, and this is impossible to do without per-device support due to BSP differences. For now, a simple for-based loop is used.
+
+### 8-bit PIC support
+
+The 8-bit PICs also uses the Chipidea FS IP. Technically it's possible to support them as well.
+
+Possible difficulties:
+- Memory size constraints (1KB/8KB ballpark)
+- A third BDT layout (now we have two)
+- Different compiler-specific directives
+- Compiler bugs if you use SDCC
+
+
+## Author
+[ReimuNotMoe](https://github.com/ReimuNotMoe) at SudoMaker, Ltd.
+
+
+## Credits
+
+This driver is based on:
+- Microchip's USB driver (usb_device.c)
+- TinyUSB's NXP KHCI driver (dcd_khci.c)
diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c
index d40c4794a..b4a698199 100644
--- a/src/portable/microchip/pic/dcd_pic.c
+++ b/src/portable/microchip/pic/dcd_pic.c
@@ -1,8 +1,11 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2020 Koji Kitayama
- * Copyright (c) 2022 Reimu NotMoe <[email protected]>
+ * Copyright (c) 2022-2024 SudoMaker, Ltd.
+ * Author: Mike Yang (Reimu NotMoe) <[email protected]>
+ *
+ * Based on usb_device.c - Copyright (c) 2015 Microchip Technology Inc.
+ * Based on dcd_khci.c - Copyright (c) 2020 Koji Kitayama
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -313,12 +316,23 @@ static void prepare_next_setup_packet(uint8_t rhport)
{
const unsigned out_odd = _dcd.endpoint[0][0].odd;
const unsigned in_odd = _dcd.endpoint[0][1].odd;
- TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
+
+ // Abandon any previous control transfers that might have been using EP0.
+ // Ordinarily, nothing actually needs abandoning, since the previous control
+ // transfer would have completed successfully prior to the host sending the
+ // next SETUP packet. However, in a timeout error case, or after an EP0
+ // STALL event, one or more UOWN bits might still be set. If so, we should
+ // clear the UOWN bits, so the EP0 IN/OUT endpoints are in a known inactive
+ // state, ready for re-arming by the `dcd_edpt_xfer' function that will be
+ // called next.
_dcd.bdt[0][0][out_odd].data = 0;
+ _dcd.bdt[0][0][out_odd].own = 0;
_dcd.bdt[0][0][out_odd ^ 1].data = 1;
_dcd.bdt[0][1][in_odd].data = 1;
+ _dcd.bdt[0][1][in_odd].own = 0;
_dcd.bdt[0][1][in_odd ^ 1].data = 0;
+ _dcd.bdt[0][1][in_odd ^ 1].own = 0;
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
_dcd.setup_packet, sizeof(_dcd.setup_packet));
}
@@ -475,13 +489,12 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
intr_disable(rhport);
intr_clear(rhport);
-#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
- TRISBbits.TRISB6 = 1;
-#endif
-
tu_memclr(&_dcd, sizeof(_dcd));
#if TU_PIC_INT_SIZE == 4
+ // The USBBUSY bit is present on PIC32s and we're required to check it
+ // prior to powering on the USB peripheral (see DS61126F page 27)
+ while (U1PWRCbits.USBBUSY);
U1PWRCSET = _U1PWRC_USBPWR_MASK;
#else
U1PWRCbits.USBPWR = 1;
@@ -505,6 +518,19 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
+bool dcd_deinit(uint8_t rhport)
+{
+ U1CON = 0;
+ U1IE = 0;
+ U1OTGIE = 0;
+#if TU_PIC_INT_SIZE == 4
+ U1PWRCCLR = _U1PWRC_USUSPEND_MASK | _U1PWRC_USBPWR_MASK;
+#else
+ U1PWRC &= ~(_U1PWRC_USUSPEND_MASK | _U1PWRC_USBPWR_MASK);
+#endif
+ return true;
+}
+
void dcd_int_enable(uint8_t rhport)
{
intr_enable(rhport);
@@ -529,8 +555,25 @@ void dcd_remote_wakeup(uint8_t rhport)
#else
U1CONbits.RESUME = 1;
#endif
- unsigned cnt = 25000000 / 1000;
+
+ // FIXME: Assert RESUME signal correctly, requires device-specific handling
+ // For now we use a hardcoded cycle-based delay which attempts to delay 10ms
+ // at the most common CPU frequencies. On PIC32s we assume the loop body
+ // takes 3 cycles. On 16-bit PICs we assume the XC16 compiler is in use and
+ // use its `__delay_ms' function.
+
+#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
+ uint32_t cnt = 24000000 / 1000 / 3;
+ while (cnt--) asm volatile("nop");
+#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
+ uint32_t cnt = 40000000 / 1000 / 3;
+ while (cnt--) asm volatile("nop");
+#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
+ uint32_t cnt = 120000000 / 1000 / 3;
while (cnt--) asm volatile("nop");
+#else
+ __delay_ms(10);
+#endif
#if TU_PIC_INT_SIZE == 4
U1CONCLR = _U1CON_RESUME_MASK;
@@ -738,8 +781,11 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
void dcd_int_handler(uint8_t rhport)
{
- uint32_t is = U1IR;
- uint32_t msk = U1IE;
+ uint32_t is, msk;
+
+ // Part 1 - "USB interrupts"
+ is = U1IR;
+ msk = U1IE;
U1IR = is & ~msk;
is &= msk;
@@ -747,7 +793,7 @@ void dcd_int_handler(uint8_t rhport)
if (is & _U1IR_UERRIF_MASK) {
uint32_t es = U1EIR;
U1EIR = es;
- U1IR = is; /* discard any pending events */
+ U1IR = is; /* discard any pending events */
}
if (is & _U1IR_URSTIF_MASK) {
@@ -758,29 +804,66 @@ void dcd_int_handler(uint8_t rhport)
if (is & _U1IR_IDLEIF_MASK) {
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
// detected as Sleep event. Though usbd has debouncing logic so we are good
+
+ /*
+ * NOTE: Do not clear U1OTGIRbits.ACTVIF here!
+ * Reason:
+ * ACTVIF is only generated once an IDLEIF has been generated.
+ * This is a 1:1 ratio interrupt generation.
+ * For every IDLEIF, there will be only one ACTVIF regardless of
+ * the number of subsequent bus transitions.
+ *
+ * If the ACTIF is cleared here, a problem could occur when:
+ * [ IDLE ][bus activity ->
+ * <--- 3 ms -----> ^
+ * ^ ACTVIF=1
+ * IDLEIF=1
+ * # # # # (#=Program polling flags)
+ * ^
+ * This polling loop will see both
+ * IDLEIF=1 and ACTVIF=1.
+ * However, the program services IDLEIF first
+ * because ACTIVIE=0.
+ * If this routine clears the only ACTIVIF,
+ * then it can never get out of the suspend
+ * mode.
+ */
+ U1OTGIESET = _U1OTGIE_ACTVIE_MASK;
U1IR = _U1IR_IDLEIF_MASK;
process_bus_sleep(rhport);
}
- if (is & _U1IR_RESUMEIF_MASK) {
- U1IR = _U1IR_RESUMEIF_MASK;
- process_bus_resume(rhport);
- }
-
if (is & _U1IR_SOFIF_MASK) {
U1IR = _U1IR_SOFIF_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
if (is & _U1IR_STALLIF_MASK) {
- U1IR = _U1IR_STALLIF_MASK;
process_stall(rhport);
+ U1IR = _U1IR_STALLIF_MASK;
}
if (is & _U1IR_TRNIF_MASK) {
process_tokdne(rhport);
}
+ // Part 2 - "USB OTG interrupts"
+ is = U1OTGIR;
+ msk = U1OTGIE;
+
+ U1OTGIR = is & ~msk;
+ is &= msk;
+
+ if (is & _U1OTGIR_ACTVIF_MASK) {
+#if TU_PIC_INT_SIZE == 4
+ U1OTGIECLR = _U1OTGIE_ACTVIE_MASK;
+#else
+ U1OTGIE &= ~_U1OTGIE_ACTVIE_MASK;
+#endif
+ U1OTGIR = _U1OTGIR_ACTVIF_MASK;
+ process_bus_resume(rhport);
+ }
+
intr_clear(rhport);
}
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 5bfedae17..9e5f5117f 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -39,8 +39,8 @@
#endif
#include "nrf.h"
-#include "nrf_clock.h"
-#include "nrfx_usbd_errata.h"
+#include "nrfx_clock.h"
+#include "nrf_erratas.h"
#ifdef __GNUC__
#pragma GCC diagnostic pop
@@ -926,7 +926,7 @@ void tusb_hal_nrf_power_event(uint32_t event) {
#ifdef NRF52_SERIES // NRF53 does not need this errata
// ERRATA 171, 187, 166
- if (nrfx_usbd_errata_187()) {
+ if (nrf52_errata_187()) {
// CRITICAL_REGION_ENTER();
if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
*((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
@@ -938,7 +938,7 @@ void tusb_hal_nrf_power_event(uint32_t event) {
// CRITICAL_REGION_EXIT();
}
- if (nrfx_usbd_errata_171()) {
+ if (nrf52_errata_171()) {
// CRITICAL_REGION_ENTER();
if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
*((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
@@ -974,7 +974,7 @@ void tusb_hal_nrf_power_event(uint32_t event) {
__DSB(); // for sync
#ifdef NRF52_SERIES
- if (nrfx_usbd_errata_171()) {
+ if (nrf52_errata_171()) {
// CRITICAL_REGION_ENTER();
if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
*((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
@@ -987,7 +987,7 @@ void tusb_hal_nrf_power_event(uint32_t event) {
// CRITICAL_REGION_EXIT();
}
- if (nrfx_usbd_errata_187()) {
+ if (nrf52_errata_187()) {
// CRITICAL_REGION_ENTER();
if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
*((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
@@ -999,7 +999,7 @@ void tusb_hal_nrf_power_event(uint32_t event) {
// CRITICAL_REGION_EXIT();
}
- if (nrfx_usbd_errata_166()) {
+ if (nrf52_errata_166()) {
*((volatile uint32_t*) (NRF_USBD_BASE + 0x800)) = 0x7E3;
*((volatile uint32_t*) (NRF_USBD_BASE + 0x804)) = 0x40;
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index 8398b09bf..3d5e195a9 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -565,7 +565,7 @@ void dcd_int_handler(uint8_t rhport)
if (is & USB_ISTAT_SOFTOK_MASK) {
KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ dcd_event_sof(rhport, tu_u16(KHCI->FRMNUMH, KHCI->FRMNUML), true);
}
if (is & USB_ISTAT_STALL_MASK) {
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c
index 056dbf40b..45732a866 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/nxp/khci/hcd_khci.c
@@ -36,6 +36,7 @@
#endif
#include "host/hcd.h"
+#include "host/usbh.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@@ -541,6 +542,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) ep_addr;
+ return false; // TODO not implemented yet
+}
+
/* The address of buffer must be aligned to 4 byte boundary. And it must be at least 4 bytes long.
* DMA writes data in 4 byte unit */
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
index 090d1ba69..fea3e2a66 100644
--- a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
@@ -31,6 +31,7 @@
#include "chip.h"
#include "host/hcd.h"
+#include "host/usbh.h"
void hcd_int_enable(uint8_t rhport)
{
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index 7d5cfb5b0..7c637ce0c 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -297,7 +297,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
dcd_reg->EPLISTSTART = (uint32_t) _dcd.ep;
dcd_reg->DATABUFSTART = tu_align((uint32_t) &_dcd, TU_BIT(22)); // 22-bit alignment
- dcd_reg->INTSTAT |= dcd_reg->INTSTAT; // clear all pending interrupt
+ dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK;
dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_DEVICE_ENABLE_MASK | DEVCMDSTAT_DEVICE_CONNECT_MASK |
DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
@@ -563,7 +563,8 @@ void dcd_int_handler(uint8_t rhport)
uint32_t const cmd_stat = dcd_reg->DEVCMDSTAT;
- uint32_t int_status = dcd_reg->INTSTAT & dcd_reg->INTEN;
+ uint32_t int_status = dcd_reg->INTSTAT;
+ int_status &= dcd_reg->INTEN;
dcd_reg->INTSTAT = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index ce35eab70..81091c9a7 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -38,6 +38,7 @@
#include "osal/osal.h"
#include "host/hcd.h"
+#include "host/usbh.h"
#include "ohci.h"
// TODO remove
@@ -328,13 +329,13 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t
tu_memclr(p_ed, sizeof(ohci_ed_t));
}
- hcd_devtree_info_t devtree_info;
- hcd_devtree_get_info(dev_addr, &devtree_info);
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
p_ed->dev_addr = dev_addr;
p_ed->ep_number = ep_addr & 0x0F;
p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
- p_ed->speed = devtree_info.speed;
+ p_ed->speed = bus_info.speed;
p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
p_ed->max_packet_size = ep_size;
@@ -451,7 +452,6 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
@@ -486,6 +486,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) ep_addr;
+ return false; // TODO not implemented yet
+}
+
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
(void) rhport;
diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
index 6422afff1..d59a2b4ee 100644
--- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
@@ -114,14 +114,19 @@ void hcd_int_disable(uint8_t rhport) {
//--------------------------------------------------------------------+
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *desc_ep) {
- hcd_devtree_info_t dev_tree;
- hcd_devtree_get_info(dev_addr, &dev_tree);
- bool const need_pre = (dev_tree.hub_addr && dev_tree.speed == TUSB_SPEED_LOW);
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
+ bool const need_pre = (bus_info.hub_addr && bus_info.speed == TUSB_SPEED_LOW);
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const *) desc_ep, need_pre);
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ uint8_t const pio_rhport = RHPORT_PIO(rhport);
+ return pio_usb_host_endpoint_close(pio_rhport, daddr, ep_addr);
+}
+
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen);
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index af08b549d..358ce84bc 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -148,6 +148,32 @@ static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_by
hw_endpoint_xfer_start(ep, buffer, total_bytes);
}
+static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
+ // Abort any pending transfer
+ // 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) {}
+ }
+
+ uint32_t buf_ctrl = USB_BUF_CTRL_SEL; // reset to buffer 0
+ if (ep->next_pid) {
+ buf_ctrl |= USB_BUF_CTRL_DATA1_PID;
+ }
+
+ _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
+ hw_endpoint_reset_transfer(ep);
+
+ if (rp2040_chip_version() >= 2) {
+ usb_hw_clear->abort_done = abort_mask;
+ usb_hw_clear->abort = abort_mask;
+ }
+}
+
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\r\n", remaining_buffers);
@@ -178,25 +204,10 @@ TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
// 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);
+ ep->next_pid = 1u;
if (ep->active) {
- // Abort any pending transfer from a prior control transfer per USB specs
- // 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.
- uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS);
- if (rp2040_chip_version() >= 2) {
- usb_hw_set->abort = abort_mask;
- while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
- }
-
- _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
- hw_endpoint_reset_transfer(ep);
-
- if (rp2040_chip_version() >= 2) {
- usb_hw_clear->abort_done = abort_mask;
- usb_hw_clear->abort = abort_mask;
- }
+ hw_endpoint_abort_xfer(ep); // Abort any pending transfer per USB specs
}
- ep->next_pid = 1u;
}
}
@@ -495,12 +506,14 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
// 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;
- const uint8_t ep_addr = ep_desc->bEndpointAddress;
- // Fill in endpoint control register with buffer offset
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
- TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and buffer allocated
- ep->wMaxPacketSize = ep_desc->wMaxPacketSize;
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_desc->bEndpointAddress);
+ 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);
return true;
}
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 2c0a3fd49..cd8c905d5 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -459,28 +459,33 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
}
// Close all opened endpoint belong to this device
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
-{
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
pico_trace("hcd_device_close %d\n", dev_addr);
(void) rhport;
- if (dev_addr == 0) return;
-
- for (size_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++)
- {
- hw_endpoint_t* ep = &ep_pool[i];
+ // 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;
+ hw_endpoint_reset_transfer(&epx);
+ }
- if (ep->dev_addr == dev_addr && ep->configured)
- {
- // in case it is an interrupt endpoint, disable it
- usb_hw_clear->int_ep_ctrl = (1 << (ep->interrupt_num + 1));
- usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = 0;
+ // dev0 only has ep0
+ if (dev_addr != 0) {
+ for (size_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) {
+ hw_endpoint_t *ep = &ep_pool[i];
+ if (ep->dev_addr == dev_addr && ep->configured) {
+ // in case it is an interrupt endpoint, disable it
+ usb_hw_clear->int_ep_ctrl = (1 << (ep->interrupt_num + 1));
+ usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = 0;
- // unconfigure the endpoint
- ep->configured = false;
- *ep->endpoint_control = 0;
- *ep->buffer_control = 0;
- hw_endpoint_reset_transfer(ep);
+ // unconfigure the endpoint
+ ep->configured = false;
+ *ep->endpoint_control = 0;
+ *ep->buffer_control = 0;
+ hw_endpoint_reset_transfer(ep);
+ }
}
}
}
@@ -509,7 +514,6 @@ void hcd_int_disable(uint8_t rhport)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
@@ -530,6 +534,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) 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)
{
(void) rhport;
@@ -557,7 +566,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
}
// If a normal transfer (non-interrupt) then initiate using
- // sie ctrl registers. Otherwise interrupt ep registers should
+ // sie ctrl registers. Otherwise, interrupt ep registers should
// already be configured
if ( ep == &epx )
{
@@ -597,13 +606,12 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
(void) rhport;
// Copy data into setup packet buffer
- for ( uint8_t i = 0; i < 8; i++ )
- {
+ for (uint8_t i = 0; i < 8; i++) {
usbh_dpram->setup_packet[i] = setup_packet[i];
}
// Configure EP0 struct with setup info for the trans complete
- struct hw_endpoint * ep = _hw_endpoint_allocate(0);
+ struct hw_endpoint * ep = _hw_endpoint_allocate( (uint8_t) TUSB_XFER_CONTROL);
TU_ASSERT(ep);
// EPX should be inactive
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 43f48da39..9d0bd762d 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -110,7 +110,7 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi
*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
// Don't need delay in host mode as host is in charge
- if ( !is_host_mode()) {
+ if (!is_host_mode()) {
busy_wait_at_least_cycles(12);
}
}
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index 4c81b05be..6f6d27d0e 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -30,6 +30,7 @@
#if CFG_TUH_ENABLED && defined(TUP_USBIP_RUSB2)
#include "host/hcd.h"
+#include "host/usbh.h"
#include "rusb2_type.h"
#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
@@ -662,13 +663,13 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
if (0 == epn) {
rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
- hcd_devtree_info_t devtree;
- hcd_devtree_get_info(dev_addr, &devtree);
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
uint16_t volatile *devadd = (uint16_t volatile *)(uintptr_t) &rusb->DEVADD[0];
devadd += dev_addr;
while (rusb->DCPCTR_b.PBUSY) {}
rusb->DCPMAXP = (dev_addr << 12) | mps;
- *devadd = (TUSB_SPEED_FULL == devtree.speed) ? RUSB2_DEVADD_USBSPD_FS : RUSB2_DEVADD_USBSPD_LS;
+ *devadd = (TUSB_SPEED_FULL == bus_info.speed) ? RUSB2_DEVADD_USBSPD_FS : RUSB2_DEVADD_USBSPD_LS;
_hcd.ctl_mps[dev_addr] = mps;
return true;
}
@@ -718,6 +719,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) 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 r;
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index bde48ef57..5f86d6b76 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -39,14 +39,9 @@
#define DWC2_DEBUG 2
#include "device/dcd.h"
+#include "device/usbd_pvt.h"
#include "dwc2_common.h"
-#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
- #define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX
-#else
- #define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep)
-#endif
-
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
@@ -58,6 +53,7 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
+// This variable is modified from ISR context, so it must be protected by critical section
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
@@ -79,6 +75,16 @@ CFG_TUD_MEM_SECTION static struct {
TUD_EPBUF_DEF(setup_packet, 8);
} _dcd_usbbuf;
+TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc2) {
+ #if TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ return DWC2_EP_MAX;
+ #else
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ return ghwcfg2.num_dev_ep + 1;
+ #endif
+}
+
+
//--------------------------------------------------------------------
// DMA
//--------------------------------------------------------------------
@@ -102,7 +108,8 @@ bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) {
(void) dwc2;
// Internal DMA only
- return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ return CFG_TUD_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
static void dma_setup_prepare(uint8_t rhport) {
@@ -250,20 +257,15 @@ static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint
xfer->interval = p_endpoint_desc->bInterval;
// Endpoint control
- union {
- uint32_t value;
- dwc2_depctl_t bm;
- } depctl;
- depctl.value = 0;
-
- depctl.bm.mps = xfer->max_size;
- depctl.bm.active = 1;
- depctl.bm.type = p_endpoint_desc->bmAttributes.xfer;
+ dwc2_depctl_t depctl = {.value = 0};
+ depctl.mps = xfer->max_size;
+ depctl.active = 1;
+ depctl.type = p_endpoint_desc->bmAttributes.xfer;
if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
- depctl.bm.set_data0_iso_even = 1;
+ depctl.set_data0_iso_even = 1;
}
if (dir == TUSB_DIR_IN) {
- depctl.bm.tx_fifo_num = epnum;
+ depctl.tx_fifo_num = epnum;
}
dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
@@ -321,6 +323,9 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
+// 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
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
@@ -343,31 +348,22 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
}
// transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC.
- union {
- uint32_t value;
- dwc2_ep_tsize_t bm;
- } deptsiz;
- deptsiz.value = 0;
- deptsiz.bm.xfer_size = total_bytes;
- deptsiz.bm.packet_count = num_packets;
-
+ dwc2_ep_tsize_t deptsiz = {.value = 0};
+ deptsiz.xfer_size = total_bytes;
+ deptsiz.packet_count = num_packets;
dep->tsiz = deptsiz.value;
// control
- union {
- dwc2_depctl_t bm;
- uint32_t value;
- } depctl;
- depctl.value = dep->ctl;
-
- depctl.bm.clear_nak = 1;
- depctl.bm.enable = 1;
- if (depctl.bm.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
- const uint32_t odd_now = (dwc2->dsts_bm.frame_number & 1u);
+ dwc2_depctl_t depctl = {.value = dep->ctl};
+ depctl.clear_nak = 1;
+ depctl.enable = 1;
+ if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
+ const uint32_t odd_now = dsts.frame_number & 1u;
if (odd_now) {
- depctl.bm.set_data0_iso_even = 1;
+ depctl.set_data0_iso_even = 1;
} else {
- depctl.bm.set_data1_iso_odd = 1;
+ depctl.set_data1_iso_odd = 1;
}
}
@@ -410,7 +406,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
// when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
- if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
dcfg |= DCFG_XCVRDLY;
}
} else {
@@ -428,6 +425,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
+#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
+
// Enable required interrupts
dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
@@ -539,6 +541,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+ usbd_spin_lock(false);
+
_dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
@@ -556,8 +560,9 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
-
dfifo_device_init(rhport); // re-init dfifo
+
+ usbd_spin_unlock(false);
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -575,21 +580,31 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpo
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
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);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
+ bool ret;
- // EP0 can only handle one packet
- if (epnum == 0) {
- _dcd_data.ep0_pending[dir] = total_bytes;
+ usbd_spin_lock(false);
+
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
+
+ // EP0 can only handle one packet
+ if (epnum == 0) {
+ _dcd_data.ep0_pending[dir] = total_bytes;
+ }
+
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
}
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_unlock(false);
- return true;
+ return ret;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
@@ -602,17 +617,27 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
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);
- xfer->buffer = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
+ bool ret;
- // Schedule packets to be sent within interrupt
- // TODO xfer fifo may only available for slave mode
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_lock(false);
- return true;
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+
+ // Schedule packets to be sent within interrupt
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
+ }
+
+ usbd_spin_unlock(false);
+
+ return ret;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
@@ -639,9 +664,10 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
//--------------------------------------------------------------------
// 7.4.1 Initialization on USB Reset
+// Must be called from critical section
static void handle_bus_reset(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
- const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+ const uint8_t ep_count = dwc2_ep_count(dwc2);
tu_memclr(xfer_status, sizeof(xfer_status));
@@ -671,7 +697,9 @@ static void handle_bus_reset(uint8_t rhport) {
dfifo_device_init(rhport);
// 5. Reset device address
- dwc2->dcfg_bm.address = 0;
+ dwc2_dcfg_t dcfg = {.value = dwc2->dcfg};
+ dcfg.address = 0;
+ dwc2->dcfg = dcfg.value;
// Fixed both control EP0 size to 64 bytes
dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
@@ -691,8 +719,9 @@ static void handle_bus_reset(uint8_t rhport) {
static void handle_enum_done(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
tusb_speed_t speed;
- switch (dwc2->dsts_bm.enum_speed) {
+ switch (dsts.enum_speed) {
case DCFG_SPEED_HIGH:
speed = TUSB_SPEED_HIGH;
break;
@@ -737,12 +766,12 @@ static void handle_rxflvl_irq(uint8_t rhport) {
const volatile uint32_t* rx_fifo = dwc2->fifo[0];
// Pop control word off FIFO
- const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
- const uint8_t epnum = grxstsp_bm.ep_ch_num;
+ const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp};
+ const uint8_t epnum = grxstsp.ep_ch_num;
dwc2_dep_t* epout = &dwc2->epout[epnum];
- switch (grxstsp_bm.packet_status) {
+ switch (grxstsp.packet_status) {
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
// Global OUT NAK: do nothing
break;
@@ -764,7 +793,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
- const uint16_t byte_count = grxstsp_bm.byte_count;
+ const uint16_t byte_count = grxstsp.byte_count;
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
if (byte_count) {
@@ -778,7 +807,8 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// short packet, minus remaining bytes (xfer_size)
if (byte_count < xfer->max_size) {
- xfer->total_len -= epout->tsiz_bm.xfer_size;
+ const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
+ xfer->total_len -= tsiz.xfer_size;
if (epnum == 0) {
xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
_dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
@@ -840,11 +870,13 @@ 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 && (dwc2->diepempmsk & (1 << epnum))) {
- const uint16_t remain_packets = epin->tsiz_bm.packet_count;
+ dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
+ const uint16_t remain_packets = tsiz.packet_count;
// Process every single packet (only whole packets can be written to fifo)
for (uint16_t i = 0; i < remain_packets; i++) {
- const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
+ 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
@@ -863,7 +895,8 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
}
// Turn off TXFE if all bytes are written.
- if (epin->tsiz_bm.xfer_size == 0) {
+ tsiz.value = epin->tsiz;
+ if (tsiz.xfer_size == 0) {
dwc2->diepempmsk &= ~(1 << epnum);
}
}
@@ -894,7 +927,8 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
// determine actual received bytes
- const uint16_t remain = epout->tsiz_bm.xfer_size;
+ const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
+ const uint16_t remain = tsiz.xfer_size;
xfer->total_len -= remain;
// this is ZLP, so prepare EP0 for next setup
@@ -930,7 +964,7 @@ static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepin
static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const bool is_dma = dma_device_enabled(dwc2);
- const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+ const uint8_t ep_count = dwc2_ep_count(dwc2);
const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos;
dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0];
@@ -983,14 +1017,16 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
*/
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
const uint32_t gintmask = dwc2->gintmsk;
const uint32_t gintsts = dwc2->gintsts & gintmask;
if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
+
+ usbd_spin_lock(true);
handle_bus_reset(rhport);
+ usbd_spin_unlock(true);
}
if (gintsts & GINTSTS_ENUMDNE) {
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index f80ae9acb..e1e7d5c1a 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -36,6 +36,7 @@
#if CFG_TUH_ENABLED
#include "host/hcd.h"
+#include "host/usbh.h"
#endif
#include "dwc2_common.h"
@@ -88,11 +89,13 @@ static void phy_fs_init(dwc2_regs_t* dwc2) {
static void phy_hs_init(dwc2_regs_t* dwc2) {
uint32_t gusbcfg = dwc2->gusbcfg;
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4};
// De-select FS PHY
gusbcfg &= ~GUSBCFG_PHYSEL;
- if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
// Select ULPI PHY (external)
@@ -116,7 +119,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
// Set 16-bit interface if supported
- if (dwc2->ghwcfg4_bm.phy_data_width) {
+ if (ghwcfg4.phy_data_width) {
gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
} else {
gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
@@ -127,7 +130,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
dwc2->gusbcfg = gusbcfg;
// mcu specific phy init
- dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+ dwc2_phy_init(dwc2, ghwcfg2.hs_phy_type);
// Reset core after selecting PHY
reset_core(dwc2);
@@ -136,11 +139,11 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// - 9 if using 8-bit PHY interface
// - 5 if using 16-bit PHY interface
gusbcfg &= ~GUSBCFG_TRDT_Msk;
- gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
+ gusbcfg |= (ghwcfg4.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
dwc2->gusbcfg = gusbcfg;
// MCU specific PHY update post reset
- dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+ dwc2_phy_update(dwc2, ghwcfg2.hs_phy_type);
}
static bool check_dwc2(dwc2_regs_t* dwc2) {
@@ -171,7 +174,6 @@ 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;
@@ -183,7 +185,8 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
}
#endif
- return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ return ghwcfg2.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
}
/* dwc2 has several PHYs option
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index ddbfd0d10..49b8c54cb 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -55,8 +55,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
-{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
-{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
+ { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
};
#endif
@@ -73,7 +73,7 @@ static void dwc2_int_handler_wrap(void* arg) {
dcd_int_handler(rhport);
}
#endif
-#if CFG_TUH_ENABLED
+#if CFG_TUH_ENABLED && !CFG_TUH_MAX3421
if (role == TUSB_ROLE_HOST) {
hcd_int_handler(rhport, true);
}
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index dec021f59..595c89b75 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5 HS | XMC4500 | GD32VF103 |
-|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:-------------|:-------------|:------------|
-| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
-| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
-| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
-| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
-| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
-| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
-| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
-| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
-| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
-| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------|
+| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
+| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
+| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
+| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
+| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
+| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
+| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index 25edcf22d..3b1993cc5 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -21,7 +21,7 @@ dwc2_reg_value = {
'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 U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ '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_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index c11c1eb05..dc4251c29 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -77,6 +77,17 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
+#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
+ #include "stm32n6xx.h"
+ #define EP_MAX_FS 9
+ #define EP_FIFO_SIZE_FS 4096
+
+ #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
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index 812096759..0a8dacf5f 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -184,415 +184,470 @@ enum {
//--------------------------------------------------------------------
// Common Register Bitfield
//--------------------------------------------------------------------
-typedef struct TU_ATTR_PACKED {
- uint32_t ses_req_scs : 1; // 0 Session request success
- uint32_t ses_req : 1; // 1 Session request
- uint32_t vbval_ov_en : 1; // 2 VBUS valid override enable
- uint32_t vbval_ov_val : 1; // 3 VBUS valid override value
- uint32_t aval_ov_en : 1; // 4 A-peripheral session valid override enable
- uint32_t aval_ov_al : 1; // 5 A-peripheral session valid override value
- uint32_t bval_ov_en : 1; // 6 B-peripheral session valid override enable
- uint32_t bval_ov_val : 1; // 7 B-peripheral session valid override value
- uint32_t hng_scs : 1; // 8 Host negotiation success
- uint32_t hnp_rq : 1; // 9 HNP (host negotiation protocol) request
- uint32_t host_set_hnp_en : 1; // 10 Host set HNP enable
- uint32_t dev_hnp_en : 1; // 11 Device HNP enabled
- uint32_t embedded_host_en : 1; // 12 Embedded host enable
- uint32_t rsv13_14 : 2; // 13.14 Reserved
- uint32_t dbnc_filter_bypass : 1; // 15 Debounce filter bypass
- uint32_t cid_status : 1; // 16 Connector ID status
- uint32_t dbnc_done : 1; // 17 Debounce done
- uint32_t ases_valid : 1; // 18 A-session valid
- uint32_t bses_valid : 1; // 19 B-session valid
- uint32_t otg_ver : 1; // 20 OTG version 0: v1.3, 1: v2.0
- uint32_t current_mode : 1; // 21 Current mode of operation. Only from v3.00a
- uint32_t mult_val_id_bc : 5; // 22..26 Multi-valued input pin ID battery charger
- uint32_t chirp_en : 1; // 27 Chirp detection enable
- uint32_t rsv28_30 : 3; // 28.30: Reserved
- uint32_t test_mode_corr_eusb2 : 1; // 31 Test mode control for eUSB2 PHY
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t ses_req_scs : 1; // 0 Session request success
+ uint32_t ses_req : 1; // 1 Session request
+ uint32_t vbval_ov_en : 1; // 2 VBUS valid override enable
+ uint32_t vbval_ov_val : 1; // 3 VBUS valid override value
+ uint32_t aval_ov_en : 1; // 4 A-peripheral session valid override enable
+ uint32_t aval_ov_al : 1; // 5 A-peripheral session valid override value
+ uint32_t bval_ov_en : 1; // 6 B-peripheral session valid override enable
+ uint32_t bval_ov_val : 1; // 7 B-peripheral session valid override value
+ uint32_t hng_scs : 1; // 8 Host negotiation success
+ uint32_t hnp_rq : 1; // 9 HNP (host negotiation protocol) request
+ uint32_t host_set_hnp_en : 1; // 10 Host set HNP enable
+ uint32_t dev_hnp_en : 1; // 11 Device HNP enabled
+ uint32_t embedded_host_en : 1; // 12 Embedded host enable
+ uint32_t rsv13_14 : 2; // 13.14 Reserved
+ uint32_t dbnc_filter_bypass : 1; // 15 Debounce filter bypass
+ uint32_t cid_status : 1; // 16 Connector ID status
+ uint32_t dbnc_done : 1; // 17 Debounce done
+ uint32_t ases_valid : 1; // 18 A-session valid
+ uint32_t bses_valid : 1; // 19 B-session valid
+ uint32_t otg_ver : 1; // 20 OTG version 0: v1.3, 1: v2.0
+ uint32_t current_mode : 1; // 21 Current mode of operation. Only from v3.00a
+ uint32_t mult_val_id_bc : 5; // 22..26 Multi-valued input pin ID battery charger
+ uint32_t chirp_en : 1; // 27 Chirp detection enable
+ uint32_t rsv28_30 : 3; // 28.30: Reserved
+ uint32_t test_mode_corr_eusb2 : 1; // 31 Test mode control for eUSB2 PHY
+ };
} dwc2_gotgctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_gotgctl_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t rsv0_1 : 2; // 0..1 Reserved
- uint32_t ses_end_det : 1; // 2 Session end detected
- uint32_t rsv3_7 : 5; // 3..7 Reserved
- uint32_t srs_status_change : 1; // 8 Session request success status change
- uint32_t hns_status_change : 1; // 9 Host negotiation success status change
- uint32_t rsv10_16 : 7; // 10..16 Reserved
- uint32_t hng_det : 1; // 17 Host negotiation detected
- uint32_t adev_timeout_change : 1; // 18 A-device timeout change
- uint32_t dbnc_done : 1; // 19 Debounce done
- uint32_t mult_val_lp_change : 1; // 20 Multi-valued input pin change
- uint32_t rsv21_31 :11; // 21..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t rsv0_1 : 2; // 0..1 Reserved
+ uint32_t ses_end_det : 1; // 2 Session end detected
+ uint32_t rsv3_7 : 5; // 3..7 Reserved
+ uint32_t srs_status_change : 1; // 8 Session request success status change
+ uint32_t hns_status_change : 1; // 9 Host negotiation success status change
+ uint32_t rsv10_16 : 7; // 10..16 Reserved
+ uint32_t hng_det : 1; // 17 Host negotiation detected
+ uint32_t adev_timeout_change : 1; // 18 A-device timeout change
+ uint32_t dbnc_done : 1; // 19 Debounce done
+ uint32_t mult_val_lp_change : 1; // 20 Multi-valued input pin change
+ uint32_t rsv21_31 :11; // 21..31 Reserved
+ };
} dwc2_gotgint_t;
TU_VERIFY_STATIC(sizeof(dwc2_gotgint_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t gintmask : 1; // 0 Global interrupt mask
- uint32_t hbst_len : 4; // 1..4 Burst length/type
- uint32_t dma_en : 1; // 5 DMA enable
- uint32_t rsv6 : 1; // 6 Reserved
- uint32_t nptxf_empty_lvl : 1; // 7 Non-periodic Tx FIFO empty level
- uint32_t ptxf_empty_lvl : 1; // 8 Periodic Tx FIFO empty level
- uint32_t rsv9_20 : 12; // 9.20: Reserved
- uint32_t remote_mem_support : 1; // 21 Remote memory support
- uint32_t notify_all_dma_write : 1; // 22 Notify all DMA writes
- uint32_t ahb_single : 1; // 23 AHB single
- uint32_t inv_desc_endian : 1; // 24 Inverse descriptor endian
- uint32_t rsv25_31 : 7; // 25..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t gintmask : 1; // 0 Global interrupt mask
+ uint32_t hbst_len : 4; // 1..4 Burst length/type
+ uint32_t dma_en : 1; // 5 DMA enable
+ uint32_t rsv6 : 1; // 6 Reserved
+ uint32_t nptxf_empty_lvl : 1; // 7 Non-periodic Tx FIFO empty level
+ uint32_t ptxf_empty_lvl : 1; // 8 Periodic Tx FIFO empty level
+ uint32_t rsv9_20 : 12; // 9.20: Reserved
+ uint32_t remote_mem_support : 1; // 21 Remote memory support
+ uint32_t notify_all_dma_write : 1; // 22 Notify all DMA writes
+ uint32_t ahb_single : 1; // 23 AHB single
+ uint32_t inv_desc_endian : 1; // 24 Inverse descriptor endian
+ uint32_t rsv25_31 : 7; // 25..31 Reserved
+ };
} dwc2_gahbcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_gahbcfg_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t timeout_cal : 3; /* 0..2 Timeout calibration.
- The USB standard timeout value for high-speed operation is 736 to 816 (inclusive) bit times. The USB standard
- timeout value for full- speed operation is 16 to 18 (inclusive) bit times. The application must program this field
- based on the speed of enumeration. The number of bit times added per PHY clock are as follows:
- - High-speed: PHY clock One 30-MHz = 16 bit times, One 60-MHz = 8 bit times
- - Full-speed: PHY clock One 30-MHz = 0.4 bit times, One 60-MHz = 0.2 bit times, One 48-MHz = 0.25 bit times */
- uint32_t phy_if16 : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
- uint32_t ulpi_utmi_sel : 1; // 4 ULPI/UTMI select. 0: UTMI+, 1: ULPI
- uint32_t fs_intf_sel : 1; // 5 Fullspeed serial interface select. 0: 6-pin, 1: 3-pin
- uint32_t phy_sel : 1; // 6 HS/FS PHY selection. 0: HS UTMI+ or ULPI, 1: FS serial transceiver
- uint32_t ddr_sel : 1; // 7 ULPI DDR select. 0: Single data rate 8-bit, 1: Double data rate 4-bit
- uint32_t srp_capable : 1; // 8 SRP-capable
- uint32_t hnp_capable : 1; // 9 HNP-capable
- uint32_t turnaround_time : 4; // 10..13 Turnaround time. 9: 8-bit UTMI+, 5: 16-bit UTMI+
- uint32_t rsv14 : 1; // 14 Reserved
- uint32_t phy_low_power_clk_sel : 1; /* 15 PHY low-power clock select either 480-MHz or 48-MHz (low-power) PHY mode.
- In FS/LS modes, the PHY can usually operate on a 48-MHz clock to save power. This bit is valid only for UTMI+ PHYs.
- - 0: 480 Mhz internal PLL: the UTMI interface operates at either 60 MHz (8 bit) or 30 MHz (16-bit)
- - 1 48 Mhz external clock: the UTMI interface operates at 48 MHz in FS mode and at either 48 or 6 MHz in LS mode */
- uint32_t otg_i2c_sel : 1; // 16 OTG I2C interface select. 0: UTMI-FS, 1: I2C for OTG signals
- uint32_t ulpi_fsls : 1; /* 17 ULPI FS/LS select. 0: ULPI, 1: ULPI FS/LS.
- valid only when the FS serial transceiver is selected on the ULPI PHY. */
- uint32_t ulpi_auto_resume : 1; // 18 ULPI Auto-resume
- uint32_t ulpi_clk_sus_m : 1; // 19 ULPI Clock SuspendM
- uint32_t ulpi_ext_vbus_drv : 1; // 20 ULPI External VBUS Drive
- uint32_t ulpi_int_vbus_indicator : 1; // 21 ULPI Internal VBUS Indicator
- uint32_t term_sel_dl_pulse : 1; // 22 TermSel DLine pulsing
- uint32_t indicator_complement : 1; // 23 Indicator complement
- uint32_t indicator_pass_through : 1; // 24 Indicator pass through
- uint32_t ulpi_if_protect_disable : 1; // 25 ULPI interface protect disable
- uint32_t ic_usb_capable : 1; // 26 IC_USB Capable
- uint32_t ic_usb_traf_ctl : 1; // 27 IC_USB Traffic Control
- uint32_t tx_end_delay : 1; // 28 TX end delay
- uint32_t force_host_mode : 1; // 29 Force host mode
- uint32_t force_dev_mode : 1; // 30 Force device mode
- uint32_t corrupt_tx_pkt : 1; // 31 Corrupt Tx packet. 0: normal, 1: debug
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t timeout_cal : 3; /* 0..2 Timeout calibration.
+ The USB standard timeout value for high-speed operation is 736 to 816 (inclusive) bit times. The USB standard
+ timeout value for full- speed operation is 16 to 18 (inclusive) bit times. The application must program this field
+ based on the speed of enumeration. The number of bit times added per PHY clock are as follows:
+ - High-speed: PHY clock One 30-MHz = 16 bit times, One 60-MHz = 8 bit times
+ - Full-speed: PHY clock One 30-MHz = 0.4 bit times, One 60-MHz = 0.2 bit times, One 48-MHz = 0.25 bit times */
+ uint32_t phy_if16 : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
+ uint32_t ulpi_utmi_sel : 1; // 4 ULPI/UTMI select. 0: UTMI+, 1: ULPI
+ uint32_t fs_intf_sel : 1; // 5 Fullspeed serial interface select. 0: 6-pin, 1: 3-pin
+ uint32_t phy_sel : 1; // 6 HS/FS PHY selection. 0: HS UTMI+ or ULPI, 1: FS serial transceiver
+ uint32_t ddr_sel : 1; // 7 ULPI DDR select. 0: Single data rate 8-bit, 1: Double data rate 4-bit
+ uint32_t srp_capable : 1; // 8 SRP-capable
+ uint32_t hnp_capable : 1; // 9 HNP-capable
+ uint32_t turnaround_time : 4; // 10..13 Turnaround time. 9: 8-bit UTMI+, 5: 16-bit UTMI+
+ uint32_t rsv14 : 1; // 14 Reserved
+ uint32_t phy_low_power_clk_sel : 1; /* 15 PHY low-power clock select either 480-MHz or 48-MHz (low-power) PHY mode.
+ In FS/LS modes, the PHY can usually operate on a 48-MHz clock to save power. This bit is valid only for UTMI+ PHYs.
+ - 0: 480 Mhz internal PLL: the UTMI interface operates at either 60 MHz (8 bit) or 30 MHz (16-bit)
+ - 1 48 Mhz external clock: the UTMI interface operates at 48 MHz in FS mode and at either 48 or 6 MHz in LS mode */
+ uint32_t otg_i2c_sel : 1; // 16 OTG I2C interface select. 0: UTMI-FS, 1: I2C for OTG signals
+ uint32_t ulpi_fsls : 1; /* 17 ULPI FS/LS select. 0: ULPI, 1: ULPI FS/LS.
+ valid only when the FS serial transceiver is selected on the ULPI PHY. */
+ uint32_t ulpi_auto_resume : 1; // 18 ULPI Auto-resume
+ uint32_t ulpi_clk_sus_m : 1; // 19 ULPI Clock SuspendM
+ uint32_t ulpi_ext_vbus_drv : 1; // 20 ULPI External VBUS Drive
+ uint32_t ulpi_int_vbus_indicator : 1; // 21 ULPI Internal VBUS Indicator
+ uint32_t term_sel_dl_pulse : 1; // 22 TermSel DLine pulsing
+ uint32_t indicator_complement : 1; // 23 Indicator complement
+ uint32_t indicator_pass_through : 1; // 24 Indicator pass through
+ uint32_t ulpi_if_protect_disable : 1; // 25 ULPI interface protect disable
+ uint32_t ic_usb_capable : 1; // 26 IC_USB Capable
+ uint32_t ic_usb_traf_ctl : 1; // 27 IC_USB Traffic Control
+ uint32_t tx_end_delay : 1; // 28 TX end delay
+ uint32_t force_host_mode : 1; // 29 Force host mode
+ uint32_t force_dev_mode : 1; // 30 Force device mode
+ uint32_t corrupt_tx_pkt : 1; // 31 Corrupt Tx packet. 0: normal, 1: debug
+ };
} dwc2_gusbcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_gusbcfg_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t core_soft_rst : 1; // 0 Core Soft Reset
- uint32_t piufs_soft_rst : 1; // 1 PIU FS Dedicated Controller Soft Reset
- uint32_t frame_counter_rst : 1; // 2 Frame Counter Reset (host)
- uint32_t intoken_q_flush : 1; // 3 IN Token Queue Flush
- uint32_t rx_fifo_flush : 1; // 4 RX FIFO Flush
- uint32_t tx_fifo_flush : 1; // 5 TX FIFO Flush
- uint32_t tx_fifo_num : 5; // 6..10 TX FIFO Number
- uint32_t rsv11_28 :18; // 11..28 Reserved
- uint32_t core_soft_rst_done : 1; // 29 Core Soft Reset Done, from v4.20a
- uint32_t dma_req : 1; // 30 DMA Request
- uint32_t ahb_idle : 1; // 31 AHB Idle
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t core_soft_rst : 1; // 0 Core Soft Reset
+ uint32_t piufs_soft_rst : 1; // 1 PIU FS Dedicated Controller Soft Reset
+ uint32_t frame_counter_rst : 1; // 2 Frame Counter Reset (host)
+ uint32_t intoken_q_flush : 1; // 3 IN Token Queue Flush
+ uint32_t rx_fifo_flush : 1; // 4 RX FIFO Flush
+ uint32_t tx_fifo_flush : 1; // 5 TX FIFO Flush
+ uint32_t tx_fifo_num : 5; // 6..10 TX FIFO Number
+ uint32_t rsv11_28 :18; // 11..28 Reserved
+ uint32_t core_soft_rst_done : 1; // 29 Core Soft Reset Done, from v4.20a
+ uint32_t dma_req : 1; // 30 DMA Request
+ uint32_t ahb_idle : 1; // 31 AHB Idle
+ };
} dwc2_grstctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number
- uint32_t byte_count :11; // 4..14 Byte Count
- uint32_t dpid : 2; // 15..16 Data PID
- uint32_t packet_status : 4; // 17..20 Packet Status
- uint32_t frame_number : 4; // 21..24 Frame Number
- uint32_t rsv25_31 : 7; // 25..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number
+ uint32_t byte_count :11; // 4..14 Byte Count
+ uint32_t dpid : 2; // 15..16 Data PID
+ uint32_t packet_status : 4; // 17..20 Packet Status
+ uint32_t frame_number : 4; // 21..24 Frame Number
+ uint32_t rsv25_31 : 7; // 25..31 Reserved
+ };
} dwc2_grxstsp_t;
TU_VERIFY_STATIC(sizeof(dwc2_grxstsp_t) == 4, "incorrect size");
-// Hardware Configuration
-typedef struct TU_ATTR_PACKED {
- uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode
- uint32_t arch : 2; // 3..4 Slave/External/Internal DMA
- uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split
- uint32_t hs_phy_type : 2; // 6..7 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI
- uint32_t fs_phy_type : 2; // 8..9 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI
- uint32_t num_dev_ep : 4; // 10..13 Number of device endpoints (excluding EP0)
- uint32_t num_host_ch : 4; // 14..17 Number of host channel (excluding control)
- uint32_t period_channel_support : 1; // 18 Support Periodic OUT Host Channel
- uint32_t enable_dynamic_fifo : 1; // 19 Dynamic FIFO Sizing Enabled
- uint32_t mul_proc_intrpt : 1; // 20 Multi-Processor Interrupt enabled (OTG_MULTI_PROC_INTRPT)
- uint32_t reserved21 : 1; // 21 reserved
- uint32_t nptx_q_depth : 2; // 22..23 Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
- uint32_t ptx_q_depth : 2; // 24..25 Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
- uint32_t token_q_depth : 5; // 26..30 Device IN token sequence learning queue depth: 0-30
- uint32_t otg_enable_ic_usb : 1; // 31 IC_USB mode specified for mode of operation
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode
+ uint32_t arch : 2; // 3..4 Slave/External/Internal DMA
+ uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split
+ uint32_t hs_phy_type : 2; // 6..7 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI
+ uint32_t fs_phy_type : 2; // 8..9 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI
+ uint32_t num_dev_ep : 4; // 10..13 Number of device endpoints (excluding EP0)
+ uint32_t num_host_ch : 4; // 14..17 Number of host channel (excluding control)
+ uint32_t period_channel_support : 1; // 18 Support Periodic OUT Host Channel
+ uint32_t enable_dynamic_fifo : 1; // 19 Dynamic FIFO Sizing Enabled
+ uint32_t mul_proc_intrpt : 1; // 20 Multi-Processor Interrupt enabled (OTG_MULTI_PROC_INTRPT)
+ uint32_t reserved21 : 1; // 21 reserved
+ uint32_t nptx_q_depth : 2; // 22..23 Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
+ uint32_t ptx_q_depth : 2; // 24..25 Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
+ uint32_t token_q_depth : 5; // 26..30 Device IN token sequence learning queue depth: 0-30
+ uint32_t otg_enable_ic_usb : 1; // 31 IC_USB mode specified for mode of operation
+ };
} dwc2_ghwcfg2_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg2_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t xfer_size_width : 4; // 0..3 Transfer size counter in bits = 11 + n (max 19 bits)
- uint32_t packet_size_width : 3; // 4..6 Packet size counter in bits = 4 + n (max 10 bits)
- uint32_t otg_enable : 1; // 7 OTG capable
- uint32_t i2c_enable : 1; // 8 I2C interface is available
- uint32_t vendor_ctrl_itf : 1; // 9 Vendor control interface is available
- uint32_t optional_feature_removed : 1; // 10 remove User ID, GPIO, SOF toggle & counter to save gate count
- uint32_t synch_reset : 1; // 11 0: async reset | 1: synch reset
- uint32_t otg_adp_support : 1; // 12 ADP logic is present along with HSOTG controller
- uint32_t otg_enable_hsic : 1; // 13 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC
- uint32_t battery_charger_support : 1; // s14 upport battery charger
- uint32_t lpm_mode : 1; // 15 LPM mode
- uint32_t dfifo_depth : 16; // DFIFO depth - EP_LOC_CNT in terms of 32-bit words
-}dwc2_ghwcfg3_t;
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t xfer_size_width : 4; // 0..3 Transfer size counter in bits = 11 + n (max 19 bits)
+ uint32_t packet_size_width : 3; // 4..6 Packet size counter in bits = 4 + n (max 10 bits)
+ uint32_t otg_enable : 1; // 7 OTG capable
+ uint32_t i2c_enable : 1; // 8 I2C interface is available
+ uint32_t vendor_ctrl_itf : 1; // 9 Vendor control interface is available
+ uint32_t optional_feature_removed : 1; // 10 remove User ID, GPIO, SOF toggle & counter to save gate count
+ uint32_t synch_reset : 1; // 11 0: async reset | 1: synch reset
+ uint32_t otg_adp_support : 1; // 12 ADP logic is present along with HSOTG controller
+ uint32_t otg_enable_hsic : 1; // 13 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC
+ uint32_t battery_charger_support : 1; // s14 upport battery charger
+ uint32_t lpm_mode : 1; // 15 LPM mode
+ uint32_t dfifo_depth : 16; // DFIFO depth - EP_LOC_CNT in terms of 32-bit words
+ };
+} dwc2_ghwcfg3_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t num_dev_period_in_ep : 4; // 0..3 Number of Device Periodic IN Endpoints
- uint32_t partial_powerdown : 1; // 4 Partial Power Down Enabled
- uint32_t ahb_freq_min : 1; // 5 1: minimum of AHB frequency is less than 60 MHz
- uint32_t hibernation : 1; // 6 Hibernation feature is enabled
- uint32_t extended_hibernation : 1; // 7 Extended Hibernation feature is enabled
- uint32_t reserved8 : 1; // 8 Reserved
- uint32_t enhanced_lpm_support1 : 1; // 9 Enhanced LPM Support1
- uint32_t service_interval_flow : 1; // 10 Service Interval flow is supported
- uint32_t ipg_isoc_support : 1; // 11 Interpacket GAP ISO OUT worst-case is supported
- uint32_t acg_support : 1; // 12 Active clock gating is supported
- uint32_t enhanced_lpm_support : 1; // 13 Enhanced LPM Support
- uint32_t phy_data_width : 2; // 14..15 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable
- uint32_t ctrl_ep_num : 4; // 16..19 Number of Device control endpoints in addition to EP0
- uint32_t iddg_filter : 1; // 20 IDDG Filter Enabled
- uint32_t vbus_valid_filter : 1; // 21 VBUS Valid Filter Enabled
- uint32_t a_valid_filter : 1; // 22 A Valid Filter Enabled
- uint32_t b_valid_filter : 1; // 23 B Valid Filter Enabled
- uint32_t session_end_filter : 1; // 24 Session End Filter Enabled
- uint32_t dedicated_fifos : 1; // 25 Dedicated tx fifo for device IN Endpoint
- uint32_t num_dev_in_eps : 4; // 26..29 Number of Device IN Endpoints including EP0
- uint32_t dma_desc_enabled : 1; // scatter/gather DMA configuration enabled
- uint32_t dma_desc_dynamic : 1; // Dynamic scatter/gather DMA
-}dwc2_ghwcfg4_t;
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t num_dev_period_in_ep : 4; // 0..3 Number of Device Periodic IN Endpoints
+ uint32_t partial_powerdown : 1; // 4 Partial Power Down Enabled
+ uint32_t ahb_freq_min : 1; // 5 1: minimum of AHB frequency is less than 60 MHz
+ uint32_t hibernation : 1; // 6 Hibernation feature is enabled
+ uint32_t extended_hibernation : 1; // 7 Extended Hibernation feature is enabled
+ uint32_t reserved8 : 1; // 8 Reserved
+ uint32_t enhanced_lpm_support1 : 1; // 9 Enhanced LPM Support1
+ uint32_t service_interval_flow : 1; // 10 Service Interval flow is supported
+ uint32_t ipg_isoc_support : 1; // 11 Interpacket GAP ISO OUT worst-case is supported
+ uint32_t acg_support : 1; // 12 Active clock gating is supported
+ uint32_t enhanced_lpm_support : 1; // 13 Enhanced LPM Support
+ uint32_t phy_data_width : 2; // 14..15 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable
+ uint32_t ctrl_ep_num : 4; // 16..19 Number of Device control endpoints in addition to EP0
+ uint32_t iddg_filter : 1; // 20 IDDG Filter Enabled
+ uint32_t vbus_valid_filter : 1; // 21 VBUS Valid Filter Enabled
+ uint32_t a_valid_filter : 1; // 22 A Valid Filter Enabled
+ uint32_t b_valid_filter : 1; // 23 B Valid Filter Enabled
+ uint32_t session_end_filter : 1; // 24 Session End Filter Enabled
+ uint32_t dedicated_fifos : 1; // 25 Dedicated tx fifo for device IN Endpoint
+ uint32_t num_dev_in_eps : 4; // 26..29 Number of Device IN Endpoints including EP0
+ uint32_t dma_desc_enabled : 1; // scatter/gather DMA configuration enabled
+ uint32_t dma_desc_dynamic : 1; // Dynamic scatter/gather DMA
+ };
+} dwc2_ghwcfg4_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size");
-//--------------------------------------------------------------------
-// Host Register Bitfield
-//--------------------------------------------------------------------
-
-typedef struct TU_ATTR_PACKED {
- uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
- uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU
- // 24..31 is top entry in the request queue that is currently being processed by the MAC
- uint32_t qtop_terminate : 1; // 24 Last entry for selected channel
- uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
- uint32_t qtop_ch_num : 4; // 27..30 Channel number
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU
+ // 24..31 is top entry in the request queue that is currently being processed by the MAC
+ uint32_t qtop_terminate : 1; // 24 Last entry for selected channel
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+ };
} dwc2_hnptxsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
- uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue
- uint32_t qtop_terminate : 1; // 23 Last entry for selected channel
- uint32_t qtop_last_period : 1; // 24 Last entry for selected channel is a periodic entry
- uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
- uint32_t qtop_ch_num : 4; // 27..30 Channel number
- uint32_t qtop_odd_frame : 1; // 31 Send in odd frame
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t fifo_available :16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue
+ uint32_t qtop_terminate : 1; // 23 Last entry for selected channel
+ uint32_t qtop_last_period : 1; // 24 Last entry for selected channel is a periodic entry
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+ uint32_t qtop_odd_frame : 1; // 31 Send in odd frame
+ };
} dwc2_hptxsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_hptxsts_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t conn_status : 1; // 0 Port connect status
- uint32_t conn_detected : 1; // 1 Port connect detected
- uint32_t enable : 1; // 2 Port enable status
- uint32_t enable_change : 1; // 3 Port enable change
- uint32_t over_current_active : 1; // 4 Port Over-current active
- uint32_t over_current_change : 1; // 5 Port Over-current change
- uint32_t resume : 1; // 6 Port resume
- uint32_t suspend : 1; // 7 Port suspend
- uint32_t reset : 1; // 8 Port reset
- uint32_t rsv9 : 1; // 9 Reserved
- uint32_t line_status : 2; // 10..11 Line status
- uint32_t power : 1; // 12 Port power
- uint32_t test_control : 4; // 13..16 Port Test control
- uint32_t speed : 2; // 17..18 Port speed
- uint32_t rsv19_31 :13; // 19..31 Reserved
-}dwc2_hprt_t;
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t conn_status : 1; // 0 Port connect status
+ uint32_t conn_detected : 1; // 1 Port connect detected
+ uint32_t enable : 1; // 2 Port enable status
+ uint32_t enable_change : 1; // 3 Port enable change
+ uint32_t over_current_active : 1; // 4 Port Over-current active
+ uint32_t over_current_change : 1; // 5 Port Over-current change
+ uint32_t resume : 1; // 6 Port resume
+ uint32_t suspend : 1; // 7 Port suspend
+ uint32_t reset : 1; // 8 Port reset
+ uint32_t rsv9 : 1; // 9 Reserved
+ uint32_t line_status : 2; // 10..11 Line status
+ uint32_t power : 1; // 12 Port power
+ uint32_t test_control : 4; // 13..16 Port Test control
+ uint32_t speed : 2; // 17..18 Port speed
+ uint32_t rsv19_31 :13; // 19..31 Reserved
+ };
+} dwc2_hprt_t;
TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t ep_size : 11; // 0..10 Maximum packet size
- uint32_t ep_num : 4; // 11..14 Endpoint number
- uint32_t ep_dir : 1; // 15 Endpoint direction
- uint32_t rsv16 : 1; // 16 Reserved
- uint32_t low_speed_dev : 1; // 17 Low-speed device
- uint32_t ep_type : 2; // 18..19 Endpoint type
- uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count
- uint32_t dev_addr : 7; // 22..28 Device address
- uint32_t odd_frame : 1; // 29 Odd frame
- uint32_t disable : 1; // 30 Channel disable
- uint32_t enable : 1; // 31 Channel enable
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t ep_size : 11; // 0..10 Maximum packet size
+ uint32_t ep_num : 4; // 11..14 Endpoint number
+ uint32_t ep_dir : 1; // 15 Endpoint direction
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t low_speed_dev : 1; // 17 Low-speed device
+ uint32_t ep_type : 2; // 18..19 Endpoint type
+ uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count
+ uint32_t dev_addr : 7; // 22..28 Device address
+ uint32_t odd_frame : 1; // 29 Odd frame
+ uint32_t disable : 1; // 30 Channel disable
+ uint32_t enable : 1; // 31 Channel enable
+ };
} dwc2_channel_char_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t hub_port : 7; // 0..6 Hub port number
- uint32_t hub_addr : 7; // 7..13 Hub address
- uint32_t xact_pos : 2; // 14..15 Transaction position
- uint32_t split_compl : 1; // 16 Split completion
- uint32_t rsv17_30 : 14; // 17..30 Reserved
- uint32_t split_en : 1; // 31 Split enable
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t hub_port : 7; // 0..6 Hub port number
+ uint32_t hub_addr : 7; // 7..13 Hub address
+ uint32_t xact_pos : 2; // 14..15 Transaction position
+ uint32_t split_compl : 1; // 16 Split completion
+ uint32_t rsv17_30 : 14; // 17..30 Reserved
+ uint32_t split_en : 1; // 31 Split enable
+ };
} dwc2_channel_split_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
- uint32_t packet_count : 10; // 19..28 Number of packets
- uint32_t pid : 2; // 29..30 Packet ID
- uint32_t do_ping : 1; // 31 Do PING
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t pid : 2; // 29..30 Packet ID
+ uint32_t do_ping : 1; // 31 Do PING
+ };
} dwc2_channel_tsize_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t num : 16; // 0..15 Frame number
- uint32_t remainning : 16; // 16..31 Frame remaining
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t num : 16; // 0..15 Frame number
+ uint32_t remainning : 16; // 16..31 Frame remaining
+ };
} dwc2_hfnum_t;
TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size");
// Host Channel
typedef struct {
- union {
- volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
- volatile dwc2_channel_char_t hcchar_bm;
- };
- union {
- volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
- volatile dwc2_channel_split_t hcsplt_bm;
- };
- volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
- volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
- union {
- volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
- volatile dwc2_channel_tsize_t hctsiz_bm;
- };
- volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
- uint32_t reserved518; // 518 + 20*ch
- volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
+ volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
+ volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
+ volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
+ volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
+ volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
+ volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
+ uint32_t reserved518; // 518 + 20*ch
+ volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
} dwc2_channel_t;
//--------------------------------------------------------------------
// Device Register Bitfield
//--------------------------------------------------------------------
-typedef struct TU_ATTR_PACKED {
- uint32_t speed : 2; // 0..1 Speed
- uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake
- uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode
- uint32_t address : 7; // 4..10 Device address
- uint32_t period_frame_interval : 2; // 11..12 Periodic frame interval
- uint32_t en_out_nak : 1; // 13 Enable Device OUT NAK
- uint32_t xcvr_delay : 1; // 14 Transceiver delay
- uint32_t erratic_int_mask : 1; // 15 Erratic interrupt mask
- uint32_t rsv16 : 1; // 16 Reserved
- uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support
- uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count
- uint32_t dma_desc : 1; // 23 Enable scatter/gatter DMA descriptor
- uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gatter DMA
- uint32_t resume_valid : 6; // 26..31 Resume valid period
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t speed : 2; // 0..1 Speed
+ uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake
+ uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode
+ uint32_t address : 7; // 4..10 Device address
+ uint32_t period_frame_interval : 2; // 11..12 Periodic frame interval
+ uint32_t en_out_nak : 1; // 13 Enable Device OUT NAK
+ uint32_t xcvr_delay : 1; // 14 Transceiver delay
+ uint32_t erratic_int_mask : 1; // 15 Erratic interrupt mask
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support
+ uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count
+ uint32_t dma_desc : 1; // 23 Enable scatter/gather DMA descriptor
+ uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gather DMA
+ uint32_t resume_valid : 6; // 26..31 Resume valid period
+ };
} dwc2_dcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_dcfg_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal
- uint32_t soft_disconnet : 1; // 1 Soft disconnect
- uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status
- uint32_t gout_nak_status : 1; // 3 Global OUT NAK status
- uint32_t test_control : 3; // 4..6 Test control
- uint32_t set_gnp_in_nak : 1; // 7 Set global non-periodic IN NAK
- uint32_t clear_gnp_in_nak : 1; // 8 Clear global non-periodic IN NAK
- uint32_t set_gout_nak : 1; // 9 Set global OUT NAK
- uint32_t clear_gout_nak : 1; // 10 Clear global OUT NAK
- uint32_t poweron_prog_done : 1; // 11 Power-on programming done
- uint32_t rsv12 : 1; // 12 Reserved
- uint32_t global_multi_count : 2; // 13..14 Global multi-count
- uint32_t ignore_frame_number : 1; // 15 Ignore frame number
- uint32_t nak_on_babble : 1; // 16 NAK on babble
- uint32_t en_cont_on_bna : 1; // 17 Enable continue on BNA
- uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject
- uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint
- uint32_t rsv20_31 :12; // 20..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal
+ uint32_t soft_disconnet : 1; // 1 Soft disconnect
+ uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status
+ uint32_t gout_nak_status : 1; // 3 Global OUT NAK status
+ uint32_t test_control : 3; // 4..6 Test control
+ uint32_t set_gnp_in_nak : 1; // 7 Set global non-periodic IN NAK
+ uint32_t clear_gnp_in_nak : 1; // 8 Clear global non-periodic IN NAK
+ uint32_t set_gout_nak : 1; // 9 Set global OUT NAK
+ uint32_t clear_gout_nak : 1; // 10 Clear global OUT NAK
+ uint32_t poweron_prog_done : 1; // 11 Power-on programming done
+ uint32_t rsv12 : 1; // 12 Reserved
+ uint32_t global_multi_count : 2; // 13..14 Global multi-count
+ uint32_t ignore_frame_number : 1; // 15 Ignore frame number
+ uint32_t nak_on_babble : 1; // 16 NAK on babble
+ uint32_t en_cont_on_bna : 1; // 17 Enable continue on BNA
+ uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject
+ uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint
+ uint32_t rsv20_31 :12; // 20..31 Reserved
+ };
} dwc2_dctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_dctl_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t suspend_status : 1; // 0 Suspend status
- uint32_t enum_speed : 2; // 1..2 Enumerated speed
- uint32_t erratic_err : 1; // 3 Erratic error
- uint32_t rsv4_7 : 4; // 4..7 Reserved
- uint32_t frame_number : 14; // 8..21 Frame/MicroFrame number
- uint32_t line_status : 2; // 22..23 Line status
- uint32_t rsv24_31 : 8; // 24..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t suspend_status : 1; // 0 Suspend status
+ uint32_t enum_speed : 2; // 1..2 Enumerated speed
+ uint32_t erratic_err : 1; // 3 Erratic error
+ uint32_t rsv4_7 : 4; // 4..7 Reserved
+ uint32_t frame_number :14; // 8..21 Frame/MicroFrame number
+ uint32_t line_status : 2; // 22..23 Line status
+ uint32_t rsv24_31 : 8; // 24..31 Reserved
+ };
} dwc2_dsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_dsts_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t xfer_complete : 1; // 0 Transfer complete
- uint32_t disabled : 1; // 1 Endpoint disabled
- uint32_t ahb_err : 1; // 2 AHB error
- uint32_t timeout : 1; // 3 Timeout
- uint32_t in_rx_txfe : 1; // 4 IN token received when TxFIFO is empty
- uint32_t in_rx_ep_mismatch : 1; // 5 IN token received with EP mismatch
- uint32_t in_ep_nak_effective : 1; // 6 IN endpoint NAK effective
- uint32_t txfifo_empty : 1; // 7 TX FIFO empty
- uint32_t txfifo_underrun : 1; // 8 Tx FIFO under run
- uint32_t bna : 1; // 9 Buffer not available
- uint32_t rsv10 : 1; // 10 Reserved
- uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
- uint32_t babble_err : 1; // 12 Babble error
- uint32_t nak : 1; // 13 NAK
- uint32_t nyet : 1; // 14 NYET
- uint32_t rsv14_31 :17; // 15..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t timeout : 1; // 3 Timeout
+ uint32_t in_rx_txfe : 1; // 4 IN token received when TxFIFO is empty
+ uint32_t in_rx_ep_mismatch : 1; // 5 IN token received with EP mismatch
+ uint32_t in_ep_nak_effective : 1; // 6 IN endpoint NAK effective
+ uint32_t txfifo_empty : 1; // 7 TX FIFO empty
+ uint32_t txfifo_underrun : 1; // 8 Tx FIFO under run
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t rsv14_31 :17; // 15..31 Reserved
+ };
} dwc2_diepint_t;
TU_VERIFY_STATIC(sizeof(dwc2_diepint_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bit
- uint32_t next_ep : 4; // 11..14 Next endpoint number
- uint32_t active : 1; // 15 Active
- const uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
- const uint32_t nak_status : 1; // 17 NAK status
- uint32_t type : 2; // 18..19 Endpoint type
- uint32_t rsv20 : 1; // 20 Reserved
- uint32_t stall : 1; // 21 Stall
- uint32_t tx_fifo_num : 4; // 22..25 Tx FIFO number (IN)
- uint32_t clear_nak : 1; // 26 Clear NAK
- uint32_t set_nak : 1; // 27 Set NAK
- uint32_t set_data0_iso_even : 1; // 28 Set DATA0 if bulk/interrupt, even frame for isochronous
- uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous
- uint32_t disable : 1; // 30 Disable
- uint32_t enable : 1; // 31 Enable
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bits
+ uint32_t next_ep : 4; // 11..14 Next endpoint number
+ uint32_t active : 1; // 15 Active
+ uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
+ uint32_t nak_status : 1; // 17 NAK status
+ uint32_t type : 2; // 18..19 Endpoint type
+ uint32_t rsv20 : 1; // 20 Reserved
+ uint32_t stall : 1; // 21 Stall
+ uint32_t tx_fifo_num : 4; // 22..25 Tx FIFO number (IN)
+ uint32_t clear_nak : 1; // 26 Clear NAK
+ uint32_t set_nak : 1; // 27 Set NAK
+ uint32_t set_data0_iso_even : 1; // 28 Set DATA0 if bulk/interrupt, even frame for isochronous
+ uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous
+ uint32_t disable : 1; // 30 Disable
+ uint32_t enable : 1; // 31 Enable
+ };
} dwc2_depctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t xfer_complete : 1; // 0 Transfer complete
- uint32_t disabled : 1; // 1 Endpoint disabled
- uint32_t ahb_err : 1; // 2 AHB error
- uint32_t setup_phase_done : 1; // 3 Setup phase done
- uint32_t out_rx_ep_disabled : 1; // 4 OUT token received when endpoint disabled
- uint32_t status_phase_rx : 1; // 5 Status phase received
- uint32_t setup_b2b : 1; // 6 Setup packet back-to-back
- uint32_t rsv7 : 1; // 7 Reserved
- uint32_t out_packet_err : 1; // 8 OUT packet error
- uint32_t bna : 1; // 9 Buffer not available
- uint32_t rsv10 : 1; // 10 Reserved
- uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
- uint32_t babble_err : 1; // 12 Babble error
- uint32_t nak : 1; // 13 NAK
- uint32_t nyet : 1; // 14 NYET
- uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only)
- uint32_t rsv16_31 :16; // 16..31 Reserved
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t setup_phase_done : 1; // 3 Setup phase done
+ uint32_t out_rx_ep_disabled : 1; // 4 OUT token received when endpoint disabled
+ uint32_t status_phase_rx : 1; // 5 Status phase received
+ uint32_t setup_b2b : 1; // 6 Setup packet back-to-back
+ uint32_t rsv7 : 1; // 7 Reserved
+ uint32_t out_packet_err : 1; // 8 OUT packet error
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only)
+ uint32_t rsv16_31 :16; // 16..31 Reserved
+ };
} dwc2_doepint_t;
TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED {
- uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
- uint32_t packet_count : 10; // 19..28 Number of packets
- uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID
+typedef union {
+ uint32_t value;
+ struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID
+ };
} dwc2_ep_tsize_t;
TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
@@ -601,26 +656,19 @@ typedef struct {
union {
volatile uint32_t diepctl;
volatile uint32_t doepctl;
-
volatile uint32_t ctl;
- volatile dwc2_depctl_t ctl_bm;
};
uint32_t rsv04;
union {
volatile uint32_t intr;
-
volatile uint32_t diepint;
- volatile dwc2_diepint_t diepint_bm;
-
volatile uint32_t doepint;
- volatile dwc2_doepint_t doepint_bm;
};
uint32_t rsv0c;
union {
volatile uint32_t dieptsiz;
volatile uint32_t doeptsiz;
volatile uint32_t tsiz;
- volatile dwc2_ep_tsize_t tsiz_bm;
};
union {
volatile uint32_t diepdma;
@@ -628,7 +676,7 @@ typedef struct {
};
volatile uint32_t dtxfsts;
uint32_t rsv1c;
-}dwc2_dep_t;
+} dwc2_dep_t;
TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size");
@@ -636,157 +684,108 @@ TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size");
// CSR Register Map
//--------------------------------------------------------------------
typedef struct {
- //------------- Core Global -------------//
- union {
- volatile uint32_t gotgctl; // 000 OTG Control and Status
- volatile dwc2_gotgctl_t gotgctl_bm;
- };
- union {
- volatile uint32_t gotgint; // 004 OTG Interrupt
- volatile dwc2_gotgint_t gotgint_bm;
- };
- union {
- volatile uint32_t gahbcfg; // 008 AHB Configuration
- volatile dwc2_gahbcfg_t gahbcfg_bm;
- };
- union {
- volatile uint32_t gusbcfg; // 00c USB Configuration
- volatile dwc2_gusbcfg_t gusbcfg_bm;
- };
- union {
- volatile uint32_t grstctl; // 010 Reset
- volatile dwc2_grstctl_t grstctl_bm;
- };
- volatile uint32_t gintsts; // 014 Interrupt
- volatile uint32_t gintmsk; // 018 Interrupt Mask
- volatile uint32_t grxstsr; // 01c Receive Status Debug Read
- union {
- volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
- volatile dwc2_grxstsp_t grxstsp_bm;
- };
- volatile uint32_t grxfsiz; // 024 Receive FIFO Size
+ //------------- Core Global -------------
+ volatile uint32_t gotgctl; // 000 OTG Control and Status
+ volatile uint32_t gotgint; // 004 OTG Interrupt
+ volatile uint32_t gahbcfg; // 008 AHB Configuration
+ volatile uint32_t gusbcfg; // 00c USB Configuration
+ volatile uint32_t grstctl; // 010 Reset
+ volatile uint32_t gintsts; // 014 Interrupt
+ volatile uint32_t gintmsk; // 018 Interrupt Mask
+ volatile uint32_t grxstsr; // 01c Receive Status Debug Read
+ volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
+ volatile uint32_t grxfsiz; // 024 Receive FIFO Size
union {
volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size
};
union {
volatile uint32_t hnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
- volatile dwc2_hnptxsts_t hnptxsts_bm;
volatile uint32_t gnptxsts;
};
- volatile uint32_t gi2cctl; // 030 I2C Address
- volatile uint32_t gpvndctl; // 034 PHY Vendor Control
+ volatile uint32_t gi2cctl; // 030 I2C Address
+ volatile uint32_t gpvndctl; // 034 PHY Vendor Control
union {
volatile uint32_t ggpio; // 038 General Purpose IO
volatile uint32_t stm32_gccfg; // 038 STM32 General Core Configuration
};
- volatile uint32_t guid; // 03C User (Application programmable) ID
- volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version
- volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep)
- union {
- volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2
- volatile dwc2_ghwcfg2_t ghwcfg2_bm;
- };
- union {
- volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3
- volatile dwc2_ghwcfg3_t ghwcfg3_bm;
- };
+ volatile uint32_t guid; // 03C User (Application programmable) ID
+ volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version
+ volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep)
+ volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2
+ volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3
union {
volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4
volatile dwc2_ghwcfg4_t ghwcfg4_bm;
};
- volatile uint32_t glpmcfg; // 054 Core LPM Configuration
- volatile uint32_t gpwrdn; // 058 Power Down
- volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration
- volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status
- uint32_t reserved64[39]; // 064..0FF
- volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size
- volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size
- uint32_t reserved140[176]; // 140..3FF
+ volatile uint32_t glpmcfg; // 054 Core LPM Configuration
+ volatile uint32_t gpwrdn; // 058 Power Down
+ volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration
+ volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status
+ uint32_t reserved64[39]; // 064..0FF
+ volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size
+ volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size
+ uint32_t reserved140[176]; // 140..3FF
- //------------ Host -------------//
- volatile uint32_t hcfg; // 400 Host Configuration
- volatile uint32_t hfir; // 404 Host Frame Interval
- union {
- volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
- volatile dwc2_hfnum_t hfnum_bm;
- };
- uint32_t reserved40c; // 40C
- union {
- volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
- volatile dwc2_hptxsts_t hptxsts_bm;
- };
- volatile uint32_t haint; // 414 Host All Channels Interrupt
- volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
- volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
- uint32_t reserved420[8]; // 420..43F
- union {
- volatile uint32_t hprt; // 440 Host Port Control and Status
- volatile dwc2_hprt_t hprt_bm;
- };
- uint32_t reserved444[47]; // 444..4FF
+ //------------ Host -------------
+ volatile uint32_t hcfg; // 400 Host Configuration
+ volatile uint32_t hfir; // 404 Host Frame Interval
+ volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
+ uint32_t reserved40c; // 40C
+ volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
+ volatile uint32_t haint; // 414 Host All Channels Interrupt
+ volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
+ volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
+ uint32_t reserved420[8]; // 420..43F
+ volatile uint32_t hprt; // 440 Host Port Control and Status
+ uint32_t reserved444[47]; // 444..4FF
- //------------- Host Channel -------------//
- dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15
- uint32_t reserved700[64]; // 700..7FF
+ //------------- Host Channel --------
+ dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15
+ uint32_t reserved700[64]; // 700..7FF
- //------------- Device -----------//
- union {
- volatile uint32_t dcfg; // 800 Device Configuration
- volatile dwc2_dcfg_t dcfg_bm;
- };
- union {
- volatile uint32_t dctl; // 804 Device Control
- volatile dwc2_dctl_t dctl_bm;
- };
- union {
- volatile uint32_t dsts; // 808 Device Status (RO)
- volatile dwc2_dsts_t dsts_bm;
- };
- uint32_t reserved80c; // 80C
- union {
- volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
- volatile dwc2_diepint_t diepmsk_bm;
- };
- union {
- volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
- volatile dwc2_doepint_t doepmsk_bm;
- };
- volatile uint32_t daint; // 818 Device All Endpoints Interrupt
- volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
- volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
- volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2
- volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time
- volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time
- volatile uint32_t dthrctl; // 830 Device threshold Control
- volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask
+ //------------- Device -----------
+ volatile uint32_t dcfg; // 800 Device Configuration
+ volatile uint32_t dctl; // 804 Device Control
+ volatile uint32_t dsts; // 808 Device Status (RO)
+ uint32_t reserved80c; // 80C
+ volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
+ volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
+ volatile uint32_t daint; // 818 Device All Endpoints Interrupt
+ volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
+ volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
+ volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2
+ volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time
+ volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time
+ volatile uint32_t dthrctl; // 830 Device threshold Control
+ volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask
- // Device Each Endpoint (IN/OUT) Interrupt/Mask for generating dedicated EP interrupt line
- // require OTG_MULTI_PROC_INTRPT=1
- volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt
- volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt mask
- volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask
- volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask
- uint32_t reserved8c0[16]; // 8C0..8FF
+ // Device Each Endpoint (IN/OUT) Interrupt/Mask for generating dedicated EP interrupt line require
+ // OTG_MULTI_PROC_INTRPT=1
+ volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt
+ volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt mask
+ volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask
+ volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask
+ uint32_t reserved8c0[16]; // 8C0..8FF
- //------------- Device Endpoint -------------//
- union {
- dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
- struct {
- dwc2_dep_t epin[16]; // 900..AFF IN Endpoints
- dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints
- };
+ //------------- Device Endpoint -----
+ union {
+ dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
+ struct {
+ dwc2_dep_t epin[16]; // 900..AFF IN Endpoints
+ dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints
};
- uint32_t reservedd00[64]; // D00..DFF
+ };
+ uint32_t reservedd00[64]; // D00..DFF
- //------------- Power Clock -------------//
- volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control
- volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1
- uint32_t reservede08[126]; // E08..FFF
+ //------------- Power Clock ---------
+ volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control
+ volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1
+ uint32_t reservede08[126]; // E08..FFF
- //------------- FIFOs -------------//
- // Word-accessed only using first pointer since it auto shift
- volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO
+ //------------- FIFOs -------------
+ // Word-accessed only using first pointer since it auto shift
+ volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO
} dwc2_regs_t;
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, hcfg ) == 0x0400, "incorrect size");
@@ -2089,9 +2088,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCTSIZ_DOPING_Pos (31U)
#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000
#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING
-#define HCTSIZ_PID_Pos (29U)
-#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000
-#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID
+#define HCTSIZ_PID_Pos (29U)
+#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000
+#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID
/******************** Bit definition for DIEPDMA register ********************/
#define DIEPDMA_DMAADDR_Pos (0U)
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index ebbb6200a..257fa2833 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -26,7 +26,7 @@
#include "tusb_option.h"
-#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2)
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2) && !CFG_TUH_MAX3421
#if !(CFG_TUH_DWC2_SLAVE_ENABLE || CFG_TUH_DWC2_DMA_ENABLE)
#error DWC2 require either CFG_TUH_DWC2_SLAVE_ENABLE or CFG_TUH_DWC2_DMA_ENABLE to be enabled
@@ -36,6 +36,7 @@
#define DWC2_DEBUG 2
#include "host/hcd.h"
+#include "host/usbh.h"
#include "dwc2_common.h"
// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
@@ -44,8 +45,6 @@
#endif
#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count
-#define DWC2_CHANNEL_COUNT(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX)
-
TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
enum {
@@ -77,9 +76,9 @@ typedef struct {
struct TU_ATTR_PACKED {
uint32_t uframe_interval : 18; // micro-frame interval
- uint32_t speed : 2;
- uint32_t next_pid : 2;
- uint32_t do_ping : 1;
+ uint32_t speed : 2;
+ uint32_t next_pid : 2; // PID for next transfer
+ uint32_t next_do_ping : 1; // Do PING for next transfer if possible (highspeed OUT)
// uint32_t : 9;
};
@@ -97,7 +96,6 @@ typedef struct {
uint8_t err_count : 3;
uint8_t period_split_nyet_count : 3;
uint8_t halted_nyet : 1;
- uint8_t halted_sof_schedule : 1;
};
uint8_t result;
@@ -116,9 +114,15 @@ hcd_data_t _hcd_data;
//--------------------------------------------------------------------
//
//--------------------------------------------------------------------
+TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_channel_count(const dwc2_regs_t* dwc2) {
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ return tu_min8(ghwcfg2.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX);
+}
+
TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) {
tusb_speed_t speed;
- switch(dwc2->hprt_bm.speed) {
+ const dwc2_hprt_t hprt = {.value = dwc2->hprt};
+ switch(hprt.speed) {
case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break;
case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break;
case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break;
@@ -133,7 +137,8 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc
TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) {
(void) dwc2;
// Internal DMA only
- return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
+ return CFG_TUH_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
#if CFG_TUH_MEM_DCACHE_ENABLE
@@ -155,7 +160,7 @@ bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
// Allocate a channel for new transfer
TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
- const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ const uint8_t max_channel = dwc2_channel_count(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
if (!xfer->allocated) {
@@ -168,15 +173,18 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
}
// Check if is periodic (interrupt/isochronous)
-TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) {
- return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
+TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(uint32_t hcchar) {
+ const dwc2_channel_char_t hcchar_bm = {.value = hcchar};
+ return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
if (is_period) {
- return dwc2->hptxsts_bm.req_queue_available;
+ const dwc2_hptxsts_t hptxsts = {.value = dwc2->hptxsts};
+ return hptxsts.req_queue_available;
} else {
- return dwc2->hnptxsts_bm.req_queue_available;
+ const dwc2_hnptxsts_t hnptxsts = {.value = dwc2->hnptxsts};
+ return hnptxsts.req_queue_available;
}
}
@@ -188,7 +196,7 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint
TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
// disable also require request queue
- TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
channel->hcintmsk |= HCINT_HALTED;
channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
return true;
@@ -196,18 +204,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2
// attempt to send IN token to receive data
TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
- TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
channel->hcchar |= HCCHAR_CHENA;
return true;
}
// Find currently enabled channel. Note: EP0 is bidirectional
TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
- const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ const uint8_t max_channel = dwc2_channel_count(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
if (_hcd_data.xfer[ch_id].allocated) {
- const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm;
- if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) {
+ const dwc2_channel_char_t hcchar = {.value = dwc2->channel[ch_id].hcchar};
+ if (hcchar.dev_addr == dev_addr && hcchar.ep_num == ep_num && (ep_num == 0 || hcchar.ep_dir == ep_dir)) {
return ch_id;
}
}
@@ -304,12 +312,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t pa
static void dfifo_host_init(uint8_t rhport) {
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
// Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel
const bool is_dma = dma_host_enabled(dwc2);
uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4;
if (is_dma) {
- dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch;
+ dfifo_top -= ghwcfg2.num_host_ch;
}
// fixed allocation for now, improve later:
@@ -319,7 +328,7 @@ static void dfifo_host_init(uint8_t rhport) {
uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
uint16_t nptxfsiz = 2 * nptx_largest;
- uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch;
+ uint16_t rxfsiz = 2 * (ptx_largest + 2) + ghwcfg2.num_host_ch;
TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
@@ -372,6 +381,10 @@ 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
@@ -381,7 +394,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
dwc2->hprt = HPRT_POWER; // turn on VBUS
// Enable required interrupts
- dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT;
+ dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | 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;
@@ -461,8 +474,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const tusb_speed_t rh_speed = hprt_speed_get(dwc2);
- hcd_devtree_info_t devtree_info;
- hcd_devtree_get_info(dev_addr, &devtree_info);
+ tuh_bus_info_t bus_info;
+ tuh_bus_info_get(dev_addr, &bus_info);
// find a free endpoint
const uint8_t ep_id = edpt_alloc();
@@ -473,7 +486,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep);
hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress);
hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
- hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
+ hcchar_bm->low_speed_dev = (bus_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_*
hcchar_bm->err_multi_count = 0;
hcchar_bm->dev_addr = dev_addr;
@@ -482,21 +495,21 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
hcchar_bm->enable = 1;
dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm;
- hcsplt_bm->hub_port = devtree_info.hub_port;
- hcsplt_bm->hub_addr = devtree_info.hub_addr;
+ hcsplt_bm->hub_port = bus_info.hub_port;
+ hcsplt_bm->hub_addr = bus_info.hub_addr;
hcsplt_bm->xact_pos = 0;
hcsplt_bm->split_compl = 0;
- hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
+ hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && bus_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
- edpt->speed = devtree_info.speed;
+ edpt->speed = bus_info.speed;
edpt->next_pid = HCTSIZ_PID_DATA0;
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
- if (devtree_info.speed == TUSB_SPEED_FULL) {
+ if (bus_info.speed == TUSB_SPEED_FULL) {
edpt->uframe_interval <<= 3;
}
} else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
- if (devtree_info.speed == TUSB_SPEED_HIGH) {
+ if (bus_info.speed == TUSB_SPEED_HIGH) {
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
} else {
edpt->uframe_interval = desc_ep->bInterval << 3;
@@ -506,13 +519,19 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
return true;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) ep_addr;
+ return false; // TODO not implemented yet
+}
+
// clean up channel after part of transfer is done but the whole urb is not complete
static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
- dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ const dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
- edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+ const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
+ edpt->next_pid = hctsiz.pid; // save PID
/* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB)
* For IN: we can use hctsiz.xfersize as remaining bytes.
@@ -520,9 +539,10 @@ static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
* number of packets that have been transferred via the USB. This is always an integral number of packets if the
* transfer was halted before its normal completion.
*/
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
- const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size);
- const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
+ const uint16_t remain_packets = hctsiz.packet_count;
+ const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
+ const uint16_t total_packets = cal_packet_count(edpt->buflen, hcchar.ep_size);
+ const uint16_t actual_bytes = (total_packets - remain_packets) * hcchar.ep_size;
xfer->fifo_bytes = 0;
xfer->xferred_bytes += actual_bytes;
@@ -535,7 +555,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
dwc2_channel_t* channel = &dwc2->channel[ch_id];
- bool const is_period = edpt_is_periodic(hcchar_bm->ep_type);
+ bool const is_period = channel_is_periodic(edpt->hcchar);
// clear previous state
xfer->fifo_bytes = 0;
@@ -548,13 +568,16 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
// hctsiz: zero length packet still count as 1
const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size);
- uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen;
- if (edpt->do_ping && edpt->speed == TUSB_SPEED_HIGH &&
+ dwc2_channel_tsize_t hctsiz = {.value = 0};
+ hctsiz.pid = edpt->next_pid; // next PID is set in transfer complete interrupt
+ hctsiz.packet_count = packet_count;
+ hctsiz.xfer_size = edpt->buflen;
+ if (edpt->next_do_ping && edpt->speed == TUSB_SPEED_HIGH &&
edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
- hctsiz |= HCTSIZ_DOPING;
+ hctsiz.do_ping = 1;
}
- channel->hctsiz = hctsiz;
- edpt->do_ping = 0;
+ channel->hctsiz = hctsiz.value;
+ edpt->next_do_ping = 0;
// pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet
if (hcchar_bm->ep_num == 0) {
@@ -585,7 +608,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
} else {
hcintmsk |= HCINT_NYET;
- if (edpt->hcsplt_bm.split_en) {
+ if (edpt->hcsplt_bm.split_en || hctsiz.do_ping) {
hcintmsk |= HCINT_ACK;
}
}
@@ -694,18 +717,23 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
//--------------------------------------------------------------------
// HCD Event Handler
//--------------------------------------------------------------------
+
+// retry an IN transfer, channel must be halted
static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
- dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ dwc2_channel_char_t hcchar = {.value = channel->hcchar};
- if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
+ if (channel_is_periodic(hcchar.value)){
+ const dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
// retry immediately for periodic split NYET if we haven't reach max retry
- if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
+ if (hcsplt.split_en && hcsplt.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
xfer->period_split_nyet_count++;
xfer->halted_nyet = 0;
if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) {
- channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ hcchar.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ channel->hcchar = hcchar.value;
channel_send_in_token(dwc2, channel);
return;
} else {
@@ -714,18 +742,24 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
}
}
- // for periodic, de-allocate channel, enable SOF set frame counter for later transfer
- edpt->next_pid = channel->hctsiz_bm.pid; // save PID
- edpt->uframe_countdown = edpt->uframe_interval;
- dwc2->gintmsk |= GINTSTS_SOF;
-
- if (hcint & HCINT_HALTED) {
+ const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
+ if (edpt->uframe_interval == ucount) {
+ // retry on next frame if bInterval is 1
+ hcchar.odd_frame = 1 - (dwc2->hfnum & 1);
+ channel->hcchar = hcchar.value;
+ channel_send_in_token(dwc2, channel);
+ } else {
+ // otherwise, de-allocate channel, enable SOF set frame counter for later transfer
+ const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
+ edpt->next_pid = hctsiz.pid; // save PID
+ edpt->uframe_countdown = edpt->uframe_interval - ucount;
+ // enable SOF interrupt if not already enabled
+ if (!(dwc2->gintmsk & GINTMSK_SOFM)) {
+ dwc2->gintsts = GINTSTS_SOF;
+ dwc2->gintmsk |= GINTMSK_SOFM;
+ }
// already halted, de-allocate channel (called from DMA isr)
channel_dealloc(dwc2, ch_id);
- } else {
- // disable channel first if not halted (called slave isr)
- xfer->halted_sof_schedule = 1;
- channel_disable(dwc2, channel);
}
} else {
// for control/bulk: retry immediately
@@ -756,13 +790,13 @@ static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// Pop control word off FIFO
- const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
- const uint8_t ch_id = grxstsp_bm.ep_ch_num;
+ const dwc2_grxstsp_t grxstsp = {.value= dwc2->grxstsp};
+ const uint8_t ch_id = grxstsp.ep_ch_num;
- switch (grxstsp_bm.packet_status) {
+ switch (grxstsp.packet_status) {
case GRXSTS_PKTSTS_RX_DATA: {
// In packet received, pop this entry --> ACK interrupt
- const uint16_t byte_count = grxstsp_bm.byte_count;
+ const uint16_t byte_count = grxstsp.byte_count;
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
@@ -796,25 +830,26 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// return true if there is still pending data and need more ISR
static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
// Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough)
- volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts);
+ const dwc2_hptxsts_t txsts = {.value = (is_periodic ? dwc2->hptxsts : dwc2->hnptxsts)};
- const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ const uint8_t max_channel = dwc2_channel_count(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
// skip writing to FIFO if channel is expecting halted.
- if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) {
+ if (!(channel->hcintmsk & HCINT_HALTED) && (hcchar.ep_dir == TUSB_DIR_OUT)) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
-
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
+ const uint16_t remain_packets = hctsiz.packet_count;
for (uint16_t i = 0; i < remain_packets; i++) {
const uint16_t remain_bytes = edpt->buflen - xfer->fifo_bytes;
- const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size);
+ const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar.ep_size);
// skip if there is not enough space in FIFO and RequestQueue.
// Packet's last word written to FIFO will trigger a request queue
- if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) {
+ if ((xact_bytes > (txsts.fifo_available << 2)) || (txsts.req_queue_available == 0)) {
return true;
}
@@ -831,23 +866,26 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
+ const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
bool is_done = false;
- // if (channel->hcsplt_bm.split_en) {
+ // if (hcsplt.split_en) {
// if (edpt->hcchar_bm.ep_num == 1) {
- // TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, channel->hcchar_bm.ep_num, hcint);
+ // TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, hcsplt.ep_num, hcint);
// print_hcint(hcint);
// }
if (hcint & HCINT_XFER_COMPLETE) {
if (edpt->hcchar_bm.ep_num != 0) {
- edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled)
+ edpt->next_pid = hctsiz.pid; // save pid (already toggled)
}
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
- if (channel->hcsplt_bm.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
+ const uint16_t remain_packets = hctsiz.packet_count;
+ if (hcsplt.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
// Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
- channel->hcsplt_bm.split_compl = 0;
+ hcsplt.split_compl = 0;
+ channel->hcsplt = hcsplt.value;
} else {
xfer->result = XFER_RESULT_SUCCESS;
}
@@ -866,43 +904,44 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
// restart complete split
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
xfer->halted_nyet = 1;
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NAK) {
- // NAK received, re-enable channel if request queue is available
- if (channel->hcsplt_bm.split_en) {
- channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ // NAK received, disable channel to flush all posted request and try again
+ if (hcsplt.split_en) {
+ hcsplt.split_compl = 0; // restart with start-split
+ channel->hcsplt = hcsplt.value;
}
channel_disable(dwc2, channel);
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
- if (channel->hcsplt_bm.split_en) {
- if (!channel->hcsplt_bm.split_compl) {
+ if (hcsplt.split_en) {
+ if (!hcsplt.split_compl) {
// start split is ACK --> do complete split
channel->hcintmsk |= HCINT_NYET;
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
channel_send_in_token(dwc2, channel);
} else {
// do nothing for complete split with DATA, this will trigger XferComplete and handled there
}
} else {
// ACK with data
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t remain_packets = hctsiz.packet_count;
if (remain_packets) {
// still more packet to receive, also reset to start split
- channel->hcsplt_bm.split_compl = 0;
+ hcsplt.split_compl = 0;
+ channel->hcsplt = hcsplt.value;
channel_send_in_token(dwc2, channel);
}
}
} else if (hcint & HCINT_HALTED) {
channel->hcintmsk &= ~HCINT_HALTED;
- if (xfer->halted_sof_schedule) {
- // de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt
- channel_dealloc(dwc2, ch_id);
- } else if (xfer->result != XFER_RESULT_INVALID) {
+ if (xfer->result != XFER_RESULT_INVALID) {
is_done = true;
} else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
xfer->result = XFER_RESULT_FAILED;
@@ -922,23 +961,29 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
bool is_done = false;
if (hcint & HCINT_XFER_COMPLETE) {
is_done = true;
xfer->result = XFER_RESULT_SUCCESS;
channel->hcintmsk &= ~HCINT_ACK;
+ if (hcint & HCINT_NYET) {
+ // complete transfer with NYET, do ping next time
+ edpt->next_do_ping = 1;
+ }
} else if (hcint & HCINT_STALL) {
xfer->result = XFER_RESULT_STALLED;
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
xfer->err_count = 0;
- if (channel->hcsplt_bm.split_en) {
+ if (hcsplt.split_en) {
// retry complete split
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
channel->hcchar |= HCCHAR_CHENA;
} else {
- edpt->do_ping = 1;
+ edpt->next_do_ping = 1;
channel_xfer_out_wrapup(dwc2, ch_id);
channel_disable(dwc2, channel);
}
@@ -951,7 +996,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
channel->hcintmsk |= HCINT_ACK;
} else {
// NAK disable channel to flush all posted request and try again
- edpt->do_ping = 1;
+ edpt->next_do_ping = 1;
xfer->err_count = 0;
}
} else if (hcint & HCINT_HALTED) {
@@ -968,9 +1013,17 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
- if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
- // start split is ACK --> do complete split
- channel->hcsplt_bm.split_compl = 1;
+ if (hcsplt.split_en) {
+ if (!hcsplt.split_compl) {
+ // ACK for start split --> do complete split
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else {
+ // ACK interrupt is only enabled for Split and PING
+ // ACK for PING, which mean device is ready to receive data
+ channel->hctsiz &= ~HCTSIZ_DOPING; // HC already cleared PING bit, but we clear anyway
channel->hcchar |= HCCHAR_CHENA;
}
}
@@ -989,6 +1042,9 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_char_t hcchar = {.value = channel->hcchar};
+ dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
+ const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
bool is_done = false;
@@ -996,8 +1052,8 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
if (hcint & HCINT_HALTED) {
if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
- const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size;
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t remain_bytes = (uint16_t) hctsiz.xfer_size;
+ const uint16_t remain_packets = hctsiz.packet_count;
const uint16_t actual_len = edpt->buflen - remain_bytes;
xfer->xferred_bytes += actual_len;
@@ -1007,13 +1063,14 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
xfer->result = XFER_RESULT_STALLED;
} else if (hcint & HCINT_BABBLE_ERR) {
xfer->result = XFER_RESULT_FAILED;
- } else if (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) {
+ } else if (hcsplt.split_en && remain_packets && actual_len == hcchar.ep_size) {
// Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
is_done = false;
edpt->buffer += actual_len;
edpt->buflen -= actual_len;
- channel->hcsplt_bm.split_compl = 0;
+ hcsplt.split_compl = 0;
+ channel->hcsplt = hcsplt.value;
channel_xfer_in_retry(dwc2, ch_id, hcint);
} else {
xfer->result = XFER_RESULT_SUCCESS;
@@ -1028,33 +1085,38 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
xfer->result = XFER_RESULT_FAILED;
} else {
channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
- channel->hcsplt_bm.split_compl = 0;
+ hcsplt.split_compl = 0;
+ channel->hcsplt = hcsplt.value;
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_NYET) {
// Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL
// OUT that started with a PING. The nyet takes precedence.
- if (channel->hcsplt_bm.split_en) {
+ if (hcsplt.split_en) {
// split not yet mean hub has no data, retry complete split
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
- if (channel->hcsplt_bm.split_en) {
+ if (hcsplt.split_en) {
// start split is ACK --> do complete split
// TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe)
- channel->hcsplt_bm.split_compl = 1;
- if (edpt_is_periodic(channel->hcchar_bm.ep_type)) {
- channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
+ if (channel_is_periodic(channel->hcchar)) {
+ hcchar.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ channel->hcchar = hcchar.value;
}
channel_send_in_token(dwc2, channel);
}
} else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
xfer->err_count = 0;
channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
- channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ hcsplt.split_compl = 0; // restart with start-split
+ channel->hcsplt = hcsplt.value;
channel_xfer_in_retry(dwc2, ch_id, hcint);
} else if (hcint & HCINT_FARME_OVERRUN) {
// retry start-split in next binterval
@@ -1069,6 +1131,8 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
+ dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
bool is_done = false;
@@ -1104,16 +1168,18 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
}
}
} else if (hcint & HCINT_NYET) {
- if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
+ if (hcsplt.split_en && hcsplt.split_compl) {
// split not yet mean hub has no data, retry complete split
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
channel->hcchar |= HCCHAR_CHENA;
}
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
- if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ if (hcsplt.split_en && !hcsplt.split_compl) {
// start split is ACK --> do complete split
- channel->hcsplt_bm.split_compl = 1;
+ hcsplt.split_compl = 1;
+ channel->hcsplt = hcsplt.value;
channel->hcchar |= HCCHAR_CHENA;
}
}
@@ -1129,14 +1195,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
static void handle_channel_irq(uint8_t rhport, bool in_isr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const bool is_dma = dma_host_enabled(dwc2);
- const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ const uint8_t max_channel = dwc2_channel_count(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
if (tu_bit_test(dwc2->haint, ch_id)) {
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
- dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
+ dwc2_channel_char_t hcchar = {.value = channel->hcchar};
const uint32_t hcint = channel->hcint;
channel->hcint = hcint; // clear interrupt
@@ -1144,7 +1210,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
bool is_done = false;
if (is_dma) {
#if CFG_TUH_DWC2_DMA_ENABLE
- if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ if (hcchar.ep_dir == TUSB_DIR_OUT) {
is_done = handle_channel_out_dma(dwc2, ch_id, hcint);
} else {
is_done = handle_channel_in_dma(dwc2, ch_id, hcint);
@@ -1156,7 +1222,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
#endif
} else {
#if CFG_TUH_DWC2_SLAVE_ENABLE
- if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ if (hcchar.ep_dir == TUSB_DIR_OUT) {
is_done = handle_channel_out_slave(dwc2, ch_id, hcint);
} else {
is_done = handle_channel_in_slave(dwc2, ch_id, hcint);
@@ -1165,8 +1231,8 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
}
if (is_done) {
- const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir);
- hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr);
+ const uint8_t ep_addr = tu_edpt_addr(hcchar.ep_num, hcchar.ep_dir);
+ hcd_event_xfer_complete(hcchar.dev_addr, ep_addr, xfer->xferred_bytes, (xfer_result_t)xfer->result, in_isr);
channel_dealloc(dwc2, ch_id);
}
}
@@ -1177,6 +1243,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
(void) in_isr;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2->gintsts = GINTSTS_SOF; // Clear the SOF interrupt flag
bool more_isr = false;
@@ -1185,7 +1252,7 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
- if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
+ if (edpt->hcchar_bm.enable && channel_is_periodic(edpt->hcchar) && edpt->uframe_countdown > 0) {
edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
if (edpt->uframe_countdown == 0) {
if (!edpt_xfer_kickoff(dwc2, ep_id)) {
@@ -1204,10 +1271,10 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
- const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
+ const dwc2_gusbcfg_t gusbcfg = {.value = dwc2->gusbcfg};
uint32_t phy_clock;
- if (gusbcfg_bm.phy_sel) {
+ if (gusbcfg.phy_sel) {
phy_clock = 48; // dedicated FS is 48Mhz
if (speed == TUSB_SPEED_LOW) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
@@ -1215,11 +1282,11 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
}
} else {
- if (gusbcfg_bm.ulpi_utmi_sel) {
+ if (gusbcfg.ulpi_utmi_sel) {
phy_clock = 60; // ULPI 8-bit is 60Mhz
} else {
// UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
- phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60;
+ phy_clock = gusbcfg.phy_if16 ? 30 : 60;
// Enable UTMI+ low power mode 48Mhz external clock if not highspeed
if (speed == TUSB_SPEED_HIGH) {
@@ -1236,9 +1303,9 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk;
if (speed == TUSB_SPEED_HIGH) {
- hfir |= 125*phy_clock;
+ hfir |= 125*phy_clock - 1; // The "- 1" is the correct value. The Synopsys databook was corrected in 3.30a
} else {
- hfir |= 1000*phy_clock;
+ hfir |= 1000*phy_clock - 1;
}
dwc2->hfir = hfir;
@@ -1251,21 +1318,19 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
*/
static void handle_hprt_irq(uint8_t rhport, bool in_isr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
- const dwc2_hprt_t hprt_bm = dwc2->hprt_bm;
+ const dwc2_hprt_t hprt_bm = {.value = dwc2->hprt};
+ uint32_t hprt = hprt_bm.value & ~HPRT_W1_MASK;
- if (dwc2->hprt & HPRT_CONN_DETECT) {
+ if (hprt_bm.conn_detected) {
// Port Connect Detect
hprt |= HPRT_CONN_DETECT;
if (hprt_bm.conn_status) {
hcd_event_device_attach(rhport, in_isr);
- } else {
- hcd_event_device_remove(rhport, in_isr);
}
}
- if (dwc2->hprt & HPRT_ENABLE_CHANGE) {
+ if (hprt_bm.enable_change) {
// Port enable change
hprt |= HPRT_ENABLE_CHANGE;
@@ -1324,6 +1389,15 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
handle_channel_irq(rhport, in_isr);
}
+ if (gintsts & GINTSTS_DISCINT) {
+ // Device disconnected
+ dwc2->gintsts = GINTSTS_DISCINT;
+
+ if (!(dwc2->hprt & HPRT_CONN_STATUS)) {
+ hcd_event_device_remove(rhport, in_isr);
+ }
+ }
+
#if CFG_TUH_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling
if (gintsts & GINTSTS_RXFLVL) {
diff --git a/src/portable/template/hcd_template.c b/src/portable/template/hcd_template.c
index d8bca196f..d2f304407 100644
--- a/src/portable/template/hcd_template.c
+++ b/src/portable/template/hcd_template.c
@@ -29,6 +29,7 @@
#if CFG_TUH_ENABLED && CFG_TUSB_MCU == OPT_MCU_NONE
#include "host/hcd.h"
+#include "host/usbh.h"
//--------------------------------------------------------------------+
// Controller API
@@ -36,24 +37,19 @@
// 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;
-
+ (void) rhport; (void) cfg_id; (void) cfg_param;
return false;
}
// Initialize controller to host mode
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rhport;
- (void) rh_init;
+ (void) rhport; (void) rh_init;
return false;
}
// Interrupt Handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
- (void) rhport;
- (void) in_isr;
+ (void) rhport; (void) in_isr;
}
// Enable USB interrupt
@@ -69,7 +65,6 @@ void hcd_int_disable(uint8_t rhport) {
// Get frame number (1ms)
uint32_t hcd_frame_number(uint8_t rhport) {
(void) rhport;
-
return 0;
}
@@ -80,7 +75,6 @@ uint32_t hcd_frame_number(uint8_t rhport) {
// Get the current connect status of roothub port
bool hcd_port_connect_status(uint8_t rhport) {
(void) rhport;
-
return false;
}
@@ -98,14 +92,12 @@ void hcd_port_reset_end(uint8_t rhport) {
// Get port link speed
tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
(void) rhport;
-
return TUSB_SPEED_FULL;
}
// HCD closes all opened endpoints belong to this device
void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
- (void) rhport;
- (void) dev_addr;
+ (void) rhport; (void) dev_addr;
}
//--------------------------------------------------------------------+
@@ -114,49 +106,42 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
// Open an endpoint
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) {
- (void) rhport;
- (void) dev_addr;
- (void) ep_desc;
+ (void) rhport; (void) dev_addr; (void) ep_desc;
+
+ // NOTE: ep_desc is allocated on the stack when called from usbh_edpt_control_open()
+ // You need to copy the data into a local variable who maintains the state of the endpoint and transfer.
+ // Check _hcd_data in hcd_dwc2.c for example.
return false;
}
+bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport; (void) daddr; (void) ep_addr;
+ return false; // TODO not implemented yet
+}
+
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
- (void) rhport;
- (void) dev_addr;
- (void) ep_addr;
- (void) buffer;
- (void) buflen;
-
+ (void) rhport; (void) dev_addr; (void) ep_addr; (void) buffer; (void) buflen;
return false;
}
// Abort a queued transfer. Note: it can only abort transfer that has not been started
// Return true if a queued transfer is aborted, false if there is no transfer to abort
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
- (void) rhport;
- (void) dev_addr;
- (void) ep_addr;
-
+ (void) rhport; (void) dev_addr; (void) ep_addr;
return false;
}
// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
- (void) rhport;
- (void) dev_addr;
- (void) setup_packet;
-
+ (void) rhport; (void) dev_addr; (void) setup_packet;
return false;
}
// 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;
-
+ (void) rhport; (void) dev_addr; (void) ep_addr;
return false;
}
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
index 4e8e25a00..c248ba14e 100644
--- a/src/portable/wch/dcd_ch32_usbfs.c
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -192,7 +192,8 @@ void dcd_int_handler(uint8_t rhport) {
data.xfer[0][TUSB_DIR_OUT].max_size = 64;
data.xfer[0][TUSB_DIR_IN].max_size = 64;
- dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+ //dcd_event_bus_reset(rhport, (USBOTG_FS->BASE_CTRL & USBFS_CTRL_LOW_SPEED) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL, true);
+ dcd_event_bus_reset(rhport, (USBOTG_FS->UDEV_CTRL & USBFS_UDEV_CTRL_LOW_SPEED) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL, true);
USBOTG_FS->DEV_ADDR = 0x00;
EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;