summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-11-28 16:11:30 +0700
committerhathach <[email protected]>2024-11-28 16:11:30 +0700
commit79373afaafffe64dd2bb33d725b88137c3d4ee6f (patch)
tree272da237079fd812ec088f16fa0cabb3494891c7 /src/portable
parent047ba0a62db8a078aa2f7d68d00cbf41406c0fb2 (diff)
parent5bb90efd5ff59d3b1d67f3c9269ce1aa57a925a6 (diff)
Merge branch 'master' into fork/HiFiPhile/lwip_fix
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c4
-rw-r--r--src/portable/bridgetek/ft9xx/dcd_ft9xx.c5
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c5
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c38
-rw-r--r--src/portable/chipidea/ci_hs/hcd_ci_hs.c7
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c10
-rw-r--r--src/portable/espressif/esp32sx/dcd_esp32sx.c5
-rw-r--r--src/portable/mentor/musb/dcd_musb.c4
-rw-r--r--src/portable/mentor/musb/hcd_musb.c6
-rw-r--r--src/portable/microchip/pic/dcd_pic.c5
-rw-r--r--src/portable/microchip/pic32mz/dcd_pic32mz.c5
-rw-r--r--src/portable/microchip/samd/dcd_samd.c8
-rw-r--r--src/portable/microchip/samg/dcd_samg.c47
-rw-r--r--src/portable/microchip/samx7x/dcd_samx7x.c5
-rw-r--r--src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c5
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c9
-rw-r--r--src/portable/nuvoton/nuc120/dcd_nuc120.c6
-rw-r--r--src/portable/nuvoton/nuc121/dcd_nuc121.c6
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c6
-rw-r--r--src/portable/nxp/khci/dcd_khci.c6
-rw-r--r--src/portable/nxp/khci/hcd_khci.c9
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.c6
-rw-r--r--src/portable/nxp/lpc17_40/hcd_lpc17_40.c1
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c6
-rw-r--r--src/portable/ohci/ohci.c4
-rw-r--r--src/portable/raspberrypi/pio_usb/dcd_pio_usb.c7
-rw-r--r--src/portable/raspberrypi/pio_usb/hcd_pio_usb.c3
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c4
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c4
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c12
-rw-r--r--src/portable/renesas/rusb2/hcd_rusb2.c4
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.c1
-rw-r--r--src/portable/sony/cxd56/dcd_cxd56.c6
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c6
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h16
-rw-r--r--src/portable/sunxi/dcd_sunxi_musb.c7
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c1259
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c311
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h101
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h84
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-x[-rw-r--r--]src/portable/synopsys/dwc2/dwc2_info.py164
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h24
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h813
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c1360
-rw-r--r--src/portable/template/dcd_template.c49
-rw-r--r--src/portable/template/hcd_template.c4
-rw-r--r--src/portable/ti/msp430x5xx/dcd_msp430x5xx.c13
-rw-r--r--src/portable/valentyusb/eptri/dcd_eptri.c6
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c5
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c5
51 files changed, 3321 insertions, 1281 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 215feb481..c5e924266 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -494,8 +494,8 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
}
// Initialize controller to host mode
-bool hcd_init(uint8_t rhport) {
- (void) rhport;
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
tuh_max3421_int_api(rhport, false);
diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
index f02415904..34a8be3b6 100644
--- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
+++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
@@ -517,8 +517,8 @@ static uint16_t _ft9xx_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t len
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
TU_LOG2("FT9xx initialisation\r\n");
_dcd_ft9xx_attach();
@@ -526,6 +526,7 @@ void dcd_init(uint8_t rhport)
interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft9xx_usbd_ISR);
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
index 02f813ab5..11ddb683f 100644
--- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -267,9 +267,9 @@ static void process_bus_resume(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// save crystal-less setting (if available)
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
@@ -296,6 +296,7 @@ void dcd_init(uint8_t rhport)
dcd_connect(rhport);
// NVIC_ClearPendingIRQ(CIFS_IRQN);
+ return true;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index 93e1d78dd..d9f7ca8ea 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -34,43 +34,29 @@
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
#include "ci_hs_imxrt.h"
- void dcd_dcache_clean(void const* addr, uint32_t data_size) {
- imxrt_dcache_clean(addr, data_size);
+ bool dcd_dcache_clean(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean(addr, data_size);
}
- void dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
- imxrt_dcache_invalidate(addr, data_size);
+ bool dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_invalidate(addr, data_size);
}
- void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
- 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);
}
-#else
-
-#if TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
+#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
#include "ci_hs_lpc18_43.h"
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
// MCX N9 only port 1 use this controller
#include "ci_hs_mcx.h"
+
#else
#error "Unsupported MCUs"
#endif
- TU_ATTR_WEAK void dcd_dcache_clean(void const* addr, uint32_t data_size) {
- (void) addr; (void) data_size;
- }
-
- TU_ATTR_WEAK void dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
- (void) addr; (void) data_size;
- }
-
- TU_ATTR_WEAK void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
- (void) addr; (void) data_size;
- }
-#endif
-
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -234,13 +220,13 @@ static void bus_reset(uint8_t rhport)
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
- TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX, );
+ TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX);
// Reset controller
dcd_reg->USBCMD |= USBCMD_RESET;
@@ -268,6 +254,8 @@ void dcd_init(uint8_t rhport)
usbcmd |= USBCMD_RUN_STOP; // run
dcd_reg->USBCMD = usbcmd;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
index 462cbd301..14f8acb45 100644
--- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
@@ -70,7 +70,8 @@ bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
// Controller API
//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport) {
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
ci_hs_regs_t *hcd_reg = CI_HS_REG(rhport);
// Reset controller
@@ -82,7 +83,9 @@ bool hcd_init(uint8_t rhport) {
// LPC18XX/43XX need to set VBUS Power Select to HIGH
// RHPORT1 is fullspeed only (need external PHY for Highspeed)
hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
- if ( rhport == 1 ) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ if (rhport == 1) {
+ hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ }
#else
hcd_reg->USBMODE = USBMODE_CM_HOST;
#endif
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index d1e85c2df..56ecb7575 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -804,15 +804,16 @@ static void handle_ep0_nak(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
_dcd.init_called = true;
- if (_dcd.vbus_present)
- {
+ if (_dcd.vbus_present) {
dcd_connect(rhport);
}
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -895,6 +896,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
+void tusb_vbus_changed(bool present);
void tusb_vbus_changed(bool present)
{
if (present && !_dcd.vbus_present)
diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c
index 61911ab4c..1b6aae026 100644
--- a/src/portable/espressif/esp32sx/dcd_esp32sx.c
+++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c
@@ -172,8 +172,8 @@ static void enum_done_processing(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
@@ -211,6 +211,7 @@ void dcd_init(uint8_t rhport)
USB_DISCONNINTMSK_M; // host most only
dcd_connect(rhport);
+ return true;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c
index a40b3dc07..4fce08dd9 100644
--- a/src/portable/mentor/musb/dcd_musb.c
+++ b/src/portable/mentor/musb/dcd_musb.c
@@ -582,7 +582,8 @@ void print_musb_info(musb_regs_t* musb_regs) {
}
#endif
-void dcd_init(uint8_t rhport) {
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
musb_regs_t* musb_regs = MUSB_REGS(rhport);
#if CFG_TUSB_DEBUG >= MUSB_DEBUG
@@ -593,6 +594,7 @@ void dcd_init(uint8_t rhport) {
musb_dcd_int_clear(rhport);
musb_dcd_phy_init(rhport);
dcd_connect(rhport);
+ return true;
}
void dcd_int_enable(uint8_t rhport) {
diff --git a/src/portable/mentor/musb/hcd_musb.c b/src/portable/mentor/musb/hcd_musb.c
index 8fc225676..1c0740193 100644
--- a/src/portable/mentor/musb/hcd_musb.c
+++ b/src/portable/mentor/musb/hcd_musb.c
@@ -556,9 +556,9 @@ static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
* Host API
*------------------------------------------------------------------*/
-bool hcd_init(uint8_t rhport)
-{
- (void)rhport;
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
NVIC_ClearPendingIRQ(USB0_IRQn);
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c
index ccc27c3c9..d40c4794a 100644
--- a/src/portable/microchip/pic/dcd_pic.c
+++ b/src/portable/microchip/pic/dcd_pic.c
@@ -470,8 +470,8 @@ static void process_bus_resume(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
intr_disable(rhport);
intr_clear(rhport);
@@ -502,6 +502,7 @@ void dcd_init(uint8_t rhport)
U1IE = _U1IE_URSTIE_MASK;
dcd_connect(rhport);
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c
index 9ad755670..8709baf67 100644
--- a/src/portable/microchip/pic32mz/dcd_pic32mz.c
+++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c
@@ -120,8 +120,8 @@ static ep0_stage_t ep0_get_stage(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
// Disable endpoint interrupts for now
USB_REGS->INTRRXEbits.w = 0;
USB_REGS->INTRTXEbits.w = 0;
@@ -129,6 +129,7 @@ void dcd_init(uint8_t rhport)
USB_REGS->INTRUSBEbits.w = 7;
dcd_connect(rhport);
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 005b63faf..357aa1549 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -78,9 +78,9 @@ static void bus_reset(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
@@ -102,6 +102,8 @@ void dcd_init (uint8_t rhport)
USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST;
+
+ return true;
}
#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
@@ -333,7 +335,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
// Interrupt Handler
//--------------------------------------------------------------------+
-void maybe_transfer_complete(void) {
+static void maybe_transfer_complete(void) {
uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index a15431937..a5c768839 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -52,37 +52,31 @@ typedef struct
// Endpoint 0-5, each can only be either OUT or In
xfer_desc_t _dcd_xfer[EP_COUNT];
-void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) {
xfer->epsize = epsize;
}
-void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) {
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->actual_len = 0;
}
-void xfer_end(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_end(xfer_desc_t* xfer) {
xfer->buffer = NULL;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = 0;
xfer->actual_len = 0;
}
-uint16_t xfer_packet_len(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t xfer_packet_len(xfer_desc_t* xfer) {
// also cover zero-length packet
return tu_min16(xfer->total_len - xfer->actual_len, xfer->epsize);
}
-void xfer_packet_done(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_packet_done(xfer_desc_t* xfer) {
uint16_t const xact_len = xfer_packet_len(xfer);
-
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
@@ -90,19 +84,15 @@ void xfer_packet_done(xfer_desc_t* xfer)
//------------- Transaction helpers -------------//
// Write data to EP FIFO, return number of written bytes
-static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
UDP->UDP_FDR[epnum] = (uint32_t) buffer[i];
}
}
// Read data from EP FIFO
-static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
buffer[i] = (uint8_t) UDP->UDP_FDR[epnum];
}
}
@@ -112,24 +102,24 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
#define CSR_NO_EFFECT_1_ALL (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT | UDP_CSR_RXSETUP | UDP_CSR_TXCOMP)
// Per Specs: CSR need synchronization each write
-static inline void csr_write(uint8_t epnum, uint32_t value)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_write(uint8_t epnum, uint32_t value) {
uint32_t const csr = value;
UDP->UDP_CSR[epnum] = csr;
volatile uint32_t nop_count;
- for (nop_count = 0; nop_count < 20; nop_count ++) __NOP();
+ for (nop_count = 0; nop_count < 20; nop_count ++) {
+ __NOP();
+ }
}
// Per Specs: CSR need synchronization each write
-static inline void csr_set(uint8_t epnum, uint32_t mask)
+TU_ATTR_ALWAYS_INLINE static inline void csr_set(uint8_t epnum, uint32_t mask)
{
csr_write(epnum, UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL | mask);
}
// Per Specs: CSR need synchronization each write
-static inline void csr_clear(uint8_t epnum, uint32_t mask)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_clear(uint8_t epnum, uint32_t mask) {
csr_write(epnum, (UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL) & ~mask);
}
@@ -155,10 +145,13 @@ static void bus_reset(void)
}
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
+
tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c
index 9586df84d..8aec1568d 100644
--- a/src/portable/microchip/samx7x/dcd_samx7x.c
+++ b/src/portable/microchip/samx7x/dcd_samx7x.c
@@ -104,9 +104,10 @@ TU_ATTR_ALWAYS_INLINE static inline void CleanInValidateCache(uint32_t *addr, in
//------------------------------------------------------------------
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
index d5c0daaeb..1ce3da27e 100644
--- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -245,9 +245,9 @@ static void process_bus_active(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
tu_memclr(&_dcd, sizeof(_dcd));
USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
@@ -256,6 +256,7 @@ void dcd_init(uint8_t rhport)
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB_FS_IRQn);
+ return true;
}
#define USB_DEVICE_INTERRUPT_PRIORITY (3U)
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index a63e442f3..5bfedae17 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -230,9 +230,11 @@ static void xact_in_dma(uint8_t epnum) {
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-void dcd_init(uint8_t rhport) {
- TU_LOG2("dcd init\r\n");
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
+ TU_LOG2("dcd init\r\n");
+ return true;
}
void dcd_int_enable(uint8_t rhport) {
@@ -528,7 +530,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
/*------------------------------------------------------------------*/
/* Interrupt Handler
*------------------------------------------------------------------*/
-void bus_reset(void) {
+static void bus_reset(void) {
// 6.35.6 USB controller automatically disabled all endpoints (except control)
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
@@ -899,6 +901,7 @@ static void hfclk_disable(void) {
// Therefore this function must be called to handle USB power event by
// - nrfx_power_usbevt_init() : if Softdevice is not used or enabled
// - SoftDevice SOC event : if SD is used and enabled
+void tusb_hal_nrf_power_event(uint32_t event);
void tusb_hal_nrf_power_event(uint32_t event) {
// Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h
enum {
diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c
index fb12122e0..b0b6fe857 100644
--- a/src/portable/nuvoton/nuc120/dcd_nuc120.c
+++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -201,9 +201,9 @@ static const uint32_t enabled_irqs = USBD_INTSTS_FLDET_STS_Msk | USBD_INTSTS_BUS
NUC100/NUC120 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
USBD->ATTR = 0x7D0;
@@ -215,6 +215,8 @@ void dcd_init(uint8_t rhport)
USBD->INTSTS = enabled_irqs;
USBD->INTEN = enabled_irqs;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c
index 873b1b7ef..f4af97ca7 100644
--- a/src/portable/nuvoton/nuc121/dcd_nuc121.c
+++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -209,9 +209,9 @@ enum {
NUC121/NUC125/NUC126 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
#ifdef SUPPORT_LPM
USBD->ATTR = 0x7D0 | USBD_LPMACK;
@@ -227,6 +227,8 @@ void dcd_init(uint8_t rhport)
USBD->INTSTS = ENABLED_IRQS;
USBD->INTEN = ENABLED_IRQS;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index 3a92c9794..1c98a0a49 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -279,9 +279,9 @@ static const uint32_t enabled_irqs = USBD_GINTEN_USBIEN_Msk | \
NUC505 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
/* configure interrupts in their initial state; BUSINTEN and CEPINTEN will be subsequently and dynamically re-written as needed */
USBD->GINTEN = enabled_irqs;
@@ -291,6 +291,8 @@ void dcd_init(uint8_t rhport)
bus_reset();
usb_attach();
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index ef61146aa..8398b09bf 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -265,9 +265,9 @@ static void process_bus_resume(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// save crystal-less setting (if available)
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
@@ -294,6 +294,8 @@ void dcd_init(uint8_t rhport)
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB0_IRQn);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c
index 57684b259..056dbf40b 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/nxp/khci/hcd_khci.c
@@ -140,7 +140,7 @@ typedef struct
CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd;
//CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024];
-int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
+static int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
{
/* Find the target pipe */
int num;
@@ -368,10 +368,9 @@ static void process_bus_reset(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Host API
*------------------------------------------------------------------*/
-bool hcd_init(uint8_t rhport)
-{
- (void)rhport;
-
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
index 75b29faf3..855c59cd1 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
@@ -167,9 +167,9 @@ static void bus_reset(void)
tu_memclr(&_dcd, sizeof(dcd_data_t));
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
//------------- user manual 11.13 usb device controller initialization -------------//
// step 6 : set up control endpoint
@@ -186,6 +186,8 @@ void dcd_init(uint8_t rhport)
// Clear pending IRQ
NVIC_ClearPendingIRQ(USB_IRQn);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
index 372dcf51f..090d1ba69 100644
--- a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
@@ -30,6 +30,7 @@
(CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#include "chip.h"
+#include "host/hcd.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 cc18cf59b..7d5cfb5b0 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -289,8 +289,8 @@ static void edpt_reset_all(uint8_t rhport)
}
prepare_setup_packet(rhport);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
edpt_reset_all(rhport);
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@@ -303,6 +303,8 @@ void dcd_init(uint8_t rhport)
DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
NVIC_ClearPendingIRQ(_dcd_controller[rhport].irqnum);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index c59d4755e..ce35eab70 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -178,9 +178,9 @@ TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address)
}
// Initialization according to 5.1.1.4
-bool hcd_init(uint8_t rhport)
-{
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
//------------- Data Structure init -------------//
tu_memclr(&ohci_data, sizeof(ohci_data_t));
diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
index e6daf6827..60afbd435 100644
--- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
@@ -50,12 +50,13 @@ static usb_descriptor_buffers_t desc;
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
-
+ (void) rh_init;
static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
usb_device = pio_usb_device_init(&config, &desc);
+
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
index f4de3c51d..6422afff1 100644
--- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
@@ -55,8 +55,9 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
return true;
}
-bool hcd_init(uint8_t rhport) {
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// To run USB SOF interrupt in core1, call this init in core1
pio_usb_host_init(&pio_host_cfg);
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index bc0deee32..2e177d5cb 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -369,7 +369,8 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
#endif
-void dcd_init(uint8_t rhport) {
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
assert(rhport == 0);
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
@@ -405,6 +406,7 @@ void dcd_init(uint8_t rhport) {
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
dcd_connect(rhport);
+ return true;
}
bool dcd_deinit(uint8_t rhport) {
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 222dbbbf0..2c0a3fd49 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -375,9 +375,9 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport)
-{
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
pico_trace("hcd_init %d\n", rhport);
assert(rhport == 0);
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index 7c6044ca0..ecd28973c 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -657,14 +657,14 @@ static void enable_interrupt(uint32_t pswi)
}
#endif
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
rusb2_reg_t* rusb = RUSB2_REG(rhport);
rusb2_module_start(rhport, true);
-// We disable SOF for now until needed later on.
-// Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval)
-_dcd.sof_enabled = false;
+ // We disable SOF for now until needed later on.
+ // Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval)
+ _dcd.sof_enabled = false;
#ifdef RUSB2_SUPPORT_HIGHSPEED
if ( rusb2_is_highspeed_rhport(rhport) ) {
@@ -719,6 +719,8 @@ _dcd.sof_enabled = false;
if (rusb->INTSTS0_b.VBSTS) {
dcd_connect(rhport);
}
+
+ return true;
}
void dcd_int_enable(uint8_t rhport) {
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index f140da690..4c81b05be 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -466,8 +466,8 @@ static void enable_interrupt(uint32_t pswi)
}
#endif
-bool hcd_init(uint8_t rhport)
-{
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
rusb2_reg_t* rusb = RUSB2_REG(rhport);
rusb2_module_start(rhport, true);
diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c
index 850060777..72e65736b 100644
--- a/src/portable/renesas/rusb2/rusb2_common.c
+++ b/src/portable/renesas/rusb2/rusb2_common.c
@@ -45,6 +45,7 @@ rusb2_controller_t rusb2_controller[] = {
};
// Application API for setting IRQ number. May throw warnings for missing prototypes.
+void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum);
void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) {
rusb2_controller[rhport].irqnum = irqnum;
}
diff --git a/src/portable/sony/cxd56/dcd_cxd56.c b/src/portable/sony/cxd56/dcd_cxd56.c
index 41814370e..b16509c6f 100644
--- a/src/portable/sony/cxd56/dcd_cxd56.c
+++ b/src/portable/sony/cxd56/dcd_cxd56.c
@@ -201,9 +201,9 @@ static void _dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbd
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
usbdcd_driver.usbdevclass_driver.speed = USB_SPEED_HIGH;
usbdcd_driver.usbdevclass_driver.ops = &g_driverops;
@@ -211,6 +211,8 @@ void dcd_init(uint8_t rhport)
usbdcd_driver.setup_queue = osal_queue_create(&_setup_queue_def);
usbdev_register(&usbdcd_driver.usbdevclass_driver);
+
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 6eea1ab32..ef58957bb 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -40,6 +40,7 @@
* F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?)
* F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up
* F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up
+ * C0 2048 byte buffer; 32-bit bus; host mode
* G0 2048 byte buffer; 32-bit bus; host mode
* G4 1024 byte buffer
* H5 2048 byte buffer; 32-bit bus; host mode
@@ -184,7 +185,8 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-void dcd_init(uint8_t rhport) {
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
// Follow the RM mentions to use a special ordering of PDWN and FRES
for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
@@ -223,6 +225,8 @@ void dcd_init(uint8_t rhport) {
// Enable pull-up if supported
dcd_connect(rhport);
+
+ return true;
}
void dcd_sof_enable(uint8_t rhport, bool en) {
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index 03ea4c67e..ccf31e035 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -104,6 +104,20 @@
#define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
#define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #include "stm32c0xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+ #define USB_EP_CTR_RX USB_CHEP_VTRX
+ #define USB_EP_CTR_TX USB_CHEP_VTTX
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
#define FSDEV_PMA_SIZE (2048u)
@@ -260,6 +274,8 @@ static const IRQn_Type fsdev_irq[] = {
#else
USB_UCPD1_2_IRQn,
#endif
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ USB_DRD_FS_IRQn,
#elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
USB_HP_IRQn,
USB_LP_IRQn,
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index 6f36ad441..21f13b279 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -867,8 +867,9 @@ static void usb_isr_handler(void) {
dcd_int_handler(0);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+
dcd_disconnect(rhport);
USBC_HardwareReset();
USBC_PhyConfig();
@@ -895,6 +896,8 @@ void dcd_init(uint8_t rhport)
f1c100s_intc_set_isr(F1C100S_IRQ_USBOTG, usb_isr_handler);
dcd_connect(rhport);
+
+ return true;
}
// Connect by enabling internal pull-up resistor on D+/D-
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 4fa869241..63d2e5bb1 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -31,56 +31,25 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2)
+#if !(CFG_TUD_DWC2_SLAVE_ENABLE || CFG_TUD_DWC2_DMA_ENABLE)
+#error DWC2 require either CFG_TUD_DWC2_SLAVE_ENABLE or CFG_TUD_DWC2_DMA_ENABLE to be enabled
+#endif
+
// Debug level for DWC2
#define DWC2_DEBUG 2
#include "device/dcd.h"
-#include "dwc2_type.h"
-
-// Following symbols must be defined by port header
-// - _dwc2_controller[]: array of controllers
-// - DWC2_EP_MAX: largest EP counts of all controllers
-// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
-// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
-// - dwc2_remote_wakeup_delay
+#include "dwc2_common.h"
-#if defined(TUP_USBIP_DWC2_STM32)
- #include "dwc2_stm32.h"
-#elif defined(TUP_USBIP_DWC2_ESP32)
- #include "dwc2_esp32.h"
-#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
- #include "dwc2_gd32.h"
-#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
- #include "dwc2_bcm.h"
-#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
- #include "dwc2_efm32.h"
-#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
- #include "dwc2_xmc.h"
+#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX
#else
- #error "Unsupported MCUs"
+ #define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep)
#endif
-enum {
- DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller)
-};
-
-// DWC2 registers
-//#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base)
-
-TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
- if (rhport >= DWC2_CONTROLLER_COUNT) {
- // user mis-configured, ignore and use first controller
- rhport = 0;
- }
- return (dwc2_regs_t*) _dwc2_controller[rhport].reg_base;
-}
-
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
-
-static CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
-
typedef struct {
uint8_t* buffer;
tu_fifo_t* ff;
@@ -92,34 +61,48 @@ typedef struct {
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
-// EP0 transfers are limited to 1 packet - larger sizes has to be split
-static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
-static uint16_t _dfifo_top; // top free location in FIFO RAM
+typedef struct {
+ // EP0 transfers are limited to 1 packet - larger sizes has to be split
+ uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
+ uint16_t dfifo_top; // top free location in DFIFO in words
-// Number of IN endpoints active
-static uint8_t _allocated_ep_in_count;
+ // Number of IN endpoints active
+ uint8_t allocated_epin_count;
-// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
-static bool _sof_en;
+ // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
+ bool sof_en;
+} dcd_data_t;
+
+static dcd_data_t _dcd_data;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(setup_packet, 8);
+} _dcd_usbbuf;
//--------------------------------------------------------------------
// DMA
//--------------------------------------------------------------------
+#if CFG_TUD_MEM_DCACHE_ENABLE
+bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
+}
-TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(const dwc2_regs_t* dwc2) {
- #if !CFG_TUD_DWC2_DMA
- (void) dwc2;
- return false;
- #else
- // Internal DMA only
- return (dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA);
- #endif
+bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) {
- // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
- const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
- return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count;
+bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
+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;
}
static void dma_setup_prepare(uint8_t rhport) {
@@ -133,7 +116,7 @@ static void dma_setup_prepare(uint8_t rhport) {
// Receive only 1 packet
dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
- dwc2->epout[0].doepdma = (uintptr_t)_setup_packet;
+ dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet;
dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
}
@@ -141,18 +124,8 @@ static void dma_setup_prepare(uint8_t rhport) {
// Data FIFO
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
- // flush TX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos);
- while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
-}
-TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
- // flush RX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_RXFFLSH;
- while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
-}
-/* USB Data FIFO Layout
+/* Device Data FIFO scheme
The FIFO is split up into
- EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called
@@ -167,11 +140,9 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
possible since the free space is located between the RX and TX FIFOs.
---------------- ep_fifo_size
- | EPInfo |
- | for DMA |
+ | DxEPIDMAn |
|-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
- | IN FIFO 0 |
- | control |
+ | IN FIFO 0 | control EP
|-------------|
| IN FIFO 1 |
|-------------|
@@ -190,190 +161,146 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
- All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula):
- 13 for setup packets + control words (up to 3 setup packets).
- 1 for global NAK (not required/used here).
- - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1"
+ - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
- 2 for each used OUT endpoint
Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
*/
-TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count;
}
static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
- uint8_t const ep_count = dwc2_controller->ep_count;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_count = dwc2_controller->ep_count;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
TU_ASSERT(epnum < ep_count);
uint16_t fifo_size = tu_div_ceil(packet_size, 4);
if (dir == TUSB_DIR_OUT) {
// Calculate required size of RX FIFO
- uint16_t const new_sz = calc_grxfsiz(4 * fifo_size, ep_count);
+ const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count);
// If size_rx needs to be extended check if there is enough free space
if (dwc2->grxfsiz < new_sz) {
- TU_ASSERT(new_sz <= _dfifo_top);
+ TU_ASSERT(new_sz <= _dcd_data.dfifo_top);
dwc2->grxfsiz = new_sz; // Enlarge RX FIFO
}
} else {
// Check IN endpoints concurrently active limit
if(_dwc2_controller->ep_in_count) {
- TU_ASSERT(_allocated_ep_in_count < _dwc2_controller->ep_in_count);
- _allocated_ep_in_count++;
+ TU_ASSERT(_dcd_data.allocated_epin_count < _dwc2_controller->ep_in_count);
+ _dcd_data.allocated_epin_count++;
}
// If The TXFELVL is configured as half empty, the fifo must be twice the max_size.
- if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
+ if ((dwc2->gahbcfg & GAHBCFG_TX_FIFO_EPMTY_LVL) == 0) {
fifo_size *= 2;
}
// Check if free space is available
- TU_ASSERT(_dfifo_top >= fifo_size + dwc2->grxfsiz);
- _dfifo_top -= fifo_size;
- TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, _dfifo_top);
+ TU_ASSERT(_dcd_data.dfifo_top >= fifo_size + dwc2->grxfsiz);
+ _dcd_data.dfifo_top -= fifo_size;
+ // TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, dfifo_top);
// Both TXFD and TXSA are in unit of 32-bit words.
if (epnum == 0) {
- dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dfifo_top;
+ dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top;
} else {
// DIEPTXF starts at FIFO #1.
- dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dfifo_top;
+ dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top;
}
}
return true;
}
-static void dfifo_init(uint8_t rhport) {
+static void dfifo_device_init(uint8_t rhport) {
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count);
+ dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count);
- if(dma_enabled(dwc2)) {
- // DMA use last DFIFO to store metadata
- _dfifo_top = dma_cal_epfifo_base(rhport);
- }else {
- _dfifo_top = dwc2_controller->ep_fifo_size / 4;
+ // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
+ const bool is_dma = dma_device_enabled(dwc2);
+ _dcd_data.dfifo_top = dwc2_controller->ep_fifo_size/4;
+ if (is_dma) {
+ _dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count;
}
+ dwc2->gdfifocfg = (_dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top;
// Allocate FIFO for EP0 IN
dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE);
}
-// Read a single data packet from receive FIFO
-static void dfifo_read_packet(uint8_t rhport, uint8_t* dst, uint16_t len) {
- (void) rhport;
-
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile const uint32_t* rx_fifo = dwc2->fifo[0];
-
- // Reading full available 32 bit words from fifo
- uint16_t full_words = len >> 2;
- while (full_words--) {
- tu_unaligned_write32(dst, *rx_fifo);
- dst += 4;
- }
-
- // Read the remaining 1-3 bytes from fifo
- uint8_t const bytes_rem = len & 0x03;
- if (bytes_rem != 0) {
- uint32_t const tmp = *rx_fifo;
- dst[0] = tu_u32_byte0(tmp);
- if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp);
- if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp);
- }
-}
-
-// Write a single data packet to EPIN FIFO
-static void dfifo_write_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) {
- (void) rhport;
-
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
-
- // Pushing full available 32 bit words to fifo
- uint16_t full_words = len >> 2;
- while (full_words--) {
- *tx_fifo = tu_unaligned_read32(src);
- src += 4;
- }
-
- // Write the remaining 1-3 bytes into fifo
- uint8_t const bytes_rem = len & 0x03;
- if (bytes_rem) {
- uint32_t tmp_word = src[0];
- if (bytes_rem > 1) tmp_word |= (src[1] << 8);
- if (bytes_rem > 2) tmp_word |= (src[2] << 16);
-
- *tx_fifo = tmp_word;
- }
-}
//--------------------------------------------------------------------
// Endpoint
//--------------------------------------------------------------------
-
-static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
+static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint_desc) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ const uint8_t dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
xfer->interval = p_endpoint_desc->bInterval;
- // USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints.
- uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) |
- (p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
- (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
- (xfer->max_size << DOEPCTL_MPSIZ_Pos);
+ // Endpoint control
+ union {
+ uint32_t value;
+ dwc2_depctl_t bm;
+ } depctl;
+ depctl.value = 0;
- if (dir == TUSB_DIR_OUT) {
- dwc2->epout[epnum].doepctl = dxepctl;
- dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
- } else {
- dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
- dwc2->daintmsk |= TU_BIT(DAINTMSK_IEPM_Pos + epnum);
+ depctl.bm.mps = xfer->max_size;
+ depctl.bm.active = 1;
+ depctl.bm.type = p_endpoint_desc->bmAttributes.xfer;
+ if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+ depctl.bm.set_data0_iso_even = 1;
+ }
+ if (dir == TUSB_DIR_IN) {
+ depctl.bm.tx_fifo_num = epnum;
}
+
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
+ dep->ctl = depctl.value;
+ dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir));
}
static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
(void) rhport;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
if (dir == TUSB_DIR_IN) {
- dwc2_epin_t* epin = dwc2->epin;
-
// Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) {
- epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
+ if ((epnum == 0) || !(dep->diepctl & DIEPCTL_EPENA)) {
+ dep->diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
} else {
// Stop transmitting packets and NAK IN xfers.
- epin[epnum].diepctl |= DIEPCTL_SNAK;
- while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {}
+ dep->diepctl |= DIEPCTL_SNAK;
+ while ((dep->diepint & DIEPINT_INEPNE) == 0) {}
// Disable the endpoint.
- epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
- while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {}
+ dep->diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
+ while ((dep->diepint & DIEPINT_EPDISD_Msk) == 0) {}
- epin[epnum].diepint = DIEPINT_EPDISD;
+ dep->diepint = DIEPINT_EPDISD;
}
// Flush the FIFO, and wait until we have confirmed it cleared.
dfifo_flush_tx(dwc2, epnum);
} else {
- dwc2_epout_t* epout = dwc2->epout;
-
// Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) {
- epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
+ if ((epnum == 0) || !(dep->doepctl & DOEPCTL_EPENA)) {
+ dep->doepctl |= stall ? DOEPCTL_STALL : 0;
} else {
// Asserting GONAK is required to STALL an OUT endpoint.
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
@@ -382,11 +309,11 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
dwc2->dctl |= DCTL_SGONAK;
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
- // Ditto here- disable the endpoint.
- epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
- while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {}
+ // Ditto here disable the endpoint.
+ dep->doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
+ while ((dep->doepint & DOEPINT_EPDISD_Msk) == 0) {}
- epout[epnum].doepint = DOEPINT_EPDISD;
+ dep->doepint = DOEPINT_EPDISD;
// Allow other OUT endpoints to keep receiving.
dwc2->dctl |= DCTL_CGONAK;
@@ -394,343 +321,124 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
-// Start of Bus Reset
-static void bus_reset(uint8_t rhport) {
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
-
- tu_memclr(xfer_status, sizeof(xfer_status));
-
- _sof_en = false;
-
- _allocated_ep_in_count = 1;
-
- // clear device address
- dwc2->dcfg &= ~DCFG_DAD_Msk;
-
- // 1. NAK for all OUT endpoints
- for (uint8_t n = 0; n < ep_count; n++) {
- dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
- }
-
- // 2. Disable all IN endpoints
- for (uint8_t n = 0; n < ep_count; n++) {
- if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
- dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
- }
- }
-
- dfifo_flush_tx(dwc2, 0x10); // all tx fifo
- dfifo_flush_rx(dwc2);
-
- // 3. Set up interrupt mask
- dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
- dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
- dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
-
- dfifo_init(rhport);
-
- // Fixed control EP0 size to 64 bytes
- dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
- dwc2->epout[0].doepctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
-
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN].max_size = 64;
-
- if(dma_enabled(dwc2)) {
- dma_setup_prepare(rhport);
- } else {
- dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
- }
-
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
-}
-
-static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets,
- uint16_t total_bytes) {
- (void) rhport;
-
+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);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
- // EP0 is limited to one packet each xfer
- // We use multiple transaction of xfer->max_size length to get a whole transfer done
+ uint16_t num_packets;
+ uint16_t total_bytes;
+
+ // EP0 is limited to one packet per xfer
if (epnum == 0) {
- total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
- ep0_pending[dir] -= total_bytes;
+ total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size);
+ _dcd_data.ep0_pending[dir] -= total_bytes;
+ num_packets = 1;
+ } else {
+ total_bytes = xfer->total_len;
+ num_packets = tu_div_ceil(total_bytes, xfer->max_size);
+ if (num_packets == 0) {
+ num_packets = 1; // zero length packet still count as 1
+ }
}
- // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here.
- if (dir == TUSB_DIR_IN) {
- dwc2_epin_t* epin = dwc2->epin;
+ // 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;
- // A full IN transfer (multiple packets, possibly) triggers XFRC.
- epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
+ dep->tsiz = deptsiz.value;
- if(dma_enabled(dwc2)) {
- epin[epnum].diepdma = (uintptr_t)xfer->buffer;
+ // control
+ union {
+ dwc2_depctl_t bm;
+ uint32_t value;
+ } depctl;
+ depctl.value = dep->ctl;
- // For ISO endpoint set correct odd/even bit for next frame.
- if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
- }
-
- epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
+ 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);
+ if (odd_now) {
+ depctl.bm.set_data0_iso_even = 1;
} else {
-
- epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
-
- // For ISO endpoint set correct odd/even bit for next frame.
- if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
- }
- // Enable fifo empty interrupt only if there are something to put in the fifo.
- if (total_bytes != 0) {
- dwc2->diepempmsk |= (1 << epnum);
- }
+ depctl.bm.set_data1_iso_odd = 1;
}
- } else {
- dwc2_epout_t* epout = dwc2->epout;
-
- // A full OUT transfer (multiple packets, possibly) triggers XFRC.
- epout[epnum].doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ);
- epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk);
+ }
- if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 &&
- XFER_CTL_BASE(epnum, dir)->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk);
+ const bool is_dma = dma_device_enabled(dwc2);
+ if(is_dma) {
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dcd_dcache_clean(xfer->buffer, total_bytes);
}
+ dep->diepdma = (uintptr_t) xfer->buffer;
+ dep->diepctl = depctl.value; // enable endpoint
+ } else {
+ dep->diepctl = depctl.value; // enable endpoint
- if(dma_enabled(dwc2)) {
- epout[epnum].doepdma = (uintptr_t)xfer->buffer;
+ // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dwc2->diepempmsk |= (1 << epnum);
}
-
- epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
}
}
-/*------------------------------------------------------------------*/
-/* Controller API
- *------------------------------------------------------------------*/
-
-static void reset_core(dwc2_regs_t* dwc2) {
- // reset core
- dwc2->grstctl |= GRSTCTL_CSRST;
-
- // wait for reset bit is cleared
- // TODO version 4.20a should wait for RESET DONE mask
- while (dwc2->grstctl & GRSTCTL_CSRST) {}
-
- // wait for AHB master IDLE
- while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {}
-
- // wait for device mode ?
-}
-
-static bool phy_hs_supported(dwc2_regs_t* dwc2) {
- (void) dwc2;
-
-#if !TUD_OPT_HIGH_SPEED
- return false;
-#else
- return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
-#endif
-}
-
-static void phy_fs_init(dwc2_regs_t* dwc2) {
- TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n");
-
- // Select FS PHY
- dwc2->gusbcfg |= GUSBCFG_PHYSEL;
-
- // MCU specific PHY init before reset
- dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
-
- // Reset core after selecting PHY
- reset_core(dwc2);
-
- // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
- // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
- // these bits can be programmed to a larger value. Default is 5
- dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos);
-
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
-
- // set max speed
- dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos);
-}
-
-static void phy_hs_init(dwc2_regs_t* dwc2) {
- uint32_t gusbcfg = dwc2->gusbcfg;
-
- // De-select FS PHY
- gusbcfg &= ~GUSBCFG_PHYSEL;
-
- if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
- TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n");
-
- // Select ULPI
- gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
-
- // ULPI 8-bit interface, single data rate
- gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
+//--------------------------------------------------------------------
+// Controller API
+//--------------------------------------------------------------------
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- // default internal VBUS Indicator and Drive
- gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
+ tu_memclr(&_dcd_data, sizeof(_dcd_data));
- // Disable FS/LS ULPI
- gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
- } else {
- TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n");
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
+ const bool is_dma = dma_device_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
- // Select UTMI+ with 8-bit interface
- gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
+ //------------- 7.1 Device Initialization -------------//
+ // Set device max speed
+ uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
+ if (is_highspeed) {
+ dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
- // Set 16-bit interface if supported
- if (dwc2->ghwcfg4_bm.phy_data_width) {
- gusbcfg |= GUSBCFG_PHYIF16;
+ // 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) {
+ dcfg |= DCFG_XCVRDLY;
}
+ } else {
+ dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos;
}
- // Apply config
- dwc2->gusbcfg = gusbcfg;
-
- // mcu specific phy init
- dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
-
- // Reset core after selecting PHY
- reset_core(dwc2);
-
- // Set turn-around, must after core reset otherwise it will be clear
- // - 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;
- dwc2->gusbcfg = gusbcfg;
-
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
-
- // Set max speed
- uint32_t dcfg = dwc2->dcfg;
- dcfg &= ~DCFG_DSPD_Msk;
- dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
-
- // 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) {
- dcfg |= DCFG_XCVRDLY;
- }
-
+ dcfg |= DCFG_NZLSOHSK; // send STALL back and discard if host send non-zlp during control status
dwc2->dcfg = dcfg;
-}
-
-static bool check_dwc2(dwc2_regs_t* dwc2) {
-#if CFG_TUSB_DEBUG >= DWC2_DEBUG
- // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
- // Run 'dwc2_info.py render-md' and check dwc2_info.md for bit-field value and comparison with other ports
- volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
- TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
- for (size_t i = 0; i < 5; i++) {
- TU_LOG1("0x%08" PRIX32 ", ", p[i]);
- }
- TU_LOG1("0x%08" PRIX32 "\r\n", p[5]);
-#endif
-
- // For some reason: GD32VF103 snpsid and all hwcfg register are always zero (skip it)
- (void) dwc2;
-#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
- uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
- TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
-#endif
-
- return true;
-}
-void dcd_init(uint8_t rhport) {
- // Programming model begins in the last section of the chapter on the USB
- // peripheral in each Reference Manual.
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
- // Check Synopsys ID register, failed if controller clock/power is not enabled
- TU_ASSERT(check_dwc2(dwc2), );
dcd_disconnect(rhport);
- if (phy_hs_supported(dwc2)) {
- phy_hs_init(dwc2); // Highspeed
- } else {
- phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present
- }
-
- // Restart PHY clock
- dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE);
-
- /* Set HS/FS Timeout Calibration to 7 (max available value).
- * The number of PHY clocks that the application programs in
- * this field is added to the high/full speed interpacket timeout
- * duration in the core to account for any additional delays
- * introduced by the PHY. This can be required, because the delay
- * introduced by the PHY in generating the linestate condition
- * can vary from one PHY to another.
- */
- dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
-
// Force device mode
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
- // If USB host misbehaves during status portion of control xfer
- // (non zero-length packet), send STALL back and discard.
- dwc2->dcfg |= DCFG_NZLSOHSK;
-
- dfifo_flush_tx(dwc2, 0x10); // all tx fifo
- dfifo_flush_rx(dwc2);
-
- // Clear all interrupts
- uint32_t int_mask = dwc2->gintsts;
- dwc2->gintsts |= int_mask;
- int_mask = dwc2->gotgint;
- dwc2->gotgint |= int_mask;
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
- // Required as part of core initialization.
- dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
-
- // Configure TX FIFO empty level for interrupt. Default is complete empty
- dwc2->gahbcfg |= GAHBCFG_TXFELVL;
-
- if (dma_enabled(dwc2)) {
- const uint16_t epinfo_base = dma_cal_epfifo_base(rhport);
- dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base;
-
- // DMA seems to be only settable after a core reset
- dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
- }else {
- dwc2->gintmsk |= GINTMSK_RXFLVLM;
- }
-
- // Enable global interrupt
- dwc2->gahbcfg |= GAHBCFG_GINT;
-
- // make sure we are in device mode
-// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), );
-
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg);
+ // TX FIFO empty level for interrupt is complete empty
+ uint32_t gahbcfg = dwc2->gahbcfg;
+ gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
dcd_connect(rhport);
+ return true;
}
void dcd_int_enable(uint8_t rhport) {
@@ -806,7 +514,7 @@ void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- _sof_en = en;
+ _dcd_data.sof_en = en;
if (en) {
dwc2->gintsts = GINTSTS_SOF;
@@ -831,29 +539,25 @@ 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;
- _allocated_ep_in_count = 1;
+ _dcd_data.allocated_epin_count = 1;
// Disable non-control interrupt
dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos);
for (uint8_t n = 1; n < ep_count; n++) {
- // disable OUT endpoint
- if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) {
- dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS;
- }
- xfer_status[n][TUSB_DIR_OUT].max_size = 0;
-
- // disable IN endpoint
- if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
- dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+ for (uint8_t d = 0; d < 2; d++) {
+ dwc2_dep_t* dep = &dwc2->ep[d][n];
+ if (dep->ctl & EPCTL_EPENA) {
+ dep->ctl |= EPCTL_SNAK | EPCTL_EPDIS;
+ }
+ xfer_status[n][1-d].max_size = 0;
}
- xfer_status[n][TUSB_DIR_IN].max_size = 0;
}
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
- dfifo_init(rhport); // re-init dfifo
+ dfifo_device_init(rhport); // re-init dfifo
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -879,21 +583,12 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// EP0 can only handle one packet
if (epnum == 0) {
- ep0_pending[dir] = total_bytes;
-
- // Schedule the first transaction for EP0 transfer
- edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]);
- } else {
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if ((short_packet_size > 0) || (total_bytes == 0)) num_packets++;
-
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+ _dcd_data.ep0_pending[dir] = total_bytes;
}
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
+
return true;
}
@@ -913,332 +608,403 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
xfer->ff = ff;
xfer->total_len = total_bytes;
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if (short_packet_size > 0 || (total_bytes == 0)) num_packets++;
-
// Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- edpt_disable(rhport, ep_addr, false);
-}
-
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
edpt_disable(rhport, ep_addr, true);
- if((tu_edpt_number(ep_addr) == 0) && dma_enabled(DWC2_REG(rhport))) {
+ if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) {
dma_setup_prepare(rhport);
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport;
-
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
// Clear stall and reset data toggle
- if (dir == TUSB_DIR_IN) {
- dwc2->epin[epnum].diepctl &= ~DIEPCTL_STALL;
- dwc2->epin[epnum].diepctl |= DIEPCTL_SD0PID_SEVNFRM;
- } else {
- dwc2->epout[epnum].doepctl &= ~DOEPCTL_STALL;
- dwc2->epout[epnum].doepctl |= DOEPCTL_SD0PID_SEVNFRM;
- }
+ dep->ctl &= ~EPCTL_STALL;;
+ dep->ctl |= EPCTL_SD0PID_SEVNFRM;
}
//--------------------------------------------------------------------
// Interrupt Handler
//--------------------------------------------------------------------
+// 7.4.1 Initialization on USB Reset
+static void handle_bus_reset(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+
+ tu_memclr(xfer_status, sizeof(xfer_status));
+
+ _dcd_data.sof_en = false;
+ _dcd_data.allocated_epin_count = 1;
+
+ // 1. NAK for all OUT endpoints
+ for (uint8_t n = 0; n < ep_count; n++) {
+ dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
+ }
+
+ // Disable all IN endpoints
+ for (uint8_t n = 0; n < ep_count; n++) {
+ if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
+ dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+ }
+ }
+
+ // 2. Set up interrupt mask for EP0
+ dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
+ dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
+ dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
+
+ // 4. Set up DFIFO
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+ dfifo_device_init(rhport);
+
+ // 5. Reset device address
+ dwc2->dcfg_bm.address = 0;
+
+ // Fixed both control EP0 size to 64 bytes
+ dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
+ dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
+
+ xfer_status[0][TUSB_DIR_OUT].max_size = 64;
+ xfer_status[0][TUSB_DIR_IN].max_size = 64;
+
+ if(dma_device_enabled(dwc2)) {
+ dma_setup_prepare(rhport);
+ } else {
+ dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
+ }
+
+ dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
+}
+
+static void handle_enum_done(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ tusb_speed_t speed;
+ switch (dwc2->dsts_bm.enum_speed) {
+ case DCFG_SPEED_HIGH:
+ speed = TUSB_SPEED_HIGH;
+ break;
+
+ case DCFG_SPEED_LOW:
+ speed = TUSB_SPEED_LOW;
+ break;
+
+ case DCFG_SPEED_FULL_30_60MHZ:
+ case DCFG_SPEED_FULL_48MHZ:
+ default:
+ speed = TUSB_SPEED_FULL;
+ break;
+ }
+
+ // TODO must update GUSBCFG_TRDT according to link speed
+ dcd_event_bus_reset(rhport, speed, true);
+}
+
+#if 0
+TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) {
+ const char* str[] = {
+ "XFRC", "DIS", "AHBERR", "SETUP_DONE",
+ "ORXED", "STATUS_RX", "SETUP_B2B", "RSV7",
+ "OPERR", "BNA", "RSV10", "ISODROP",
+ "BBLERR", "NAK", "NYET", "SETUP_RX"
+ };
+
+ for(uint32_t i=0; i<TU_ARRAY_SIZE(str); i++) {
+ if (doepint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
+ }
+ TU_LOG1("\r\n");
+}
+#endif
+
+#if CFG_TUD_DWC2_SLAVE_ENABLE
+// Process shared receive FIFO, this interrupt is only used in Slave mode
static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile uint32_t const* rx_fifo = dwc2->fifo[0];
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
// Pop control word off FIFO
- uint32_t const ctl_word = dwc2->grxstsp;
- uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos;
- uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos;
- uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos;
-
- dwc2_epout_t* epout = &dwc2->epout[epnum];
+ const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
+ const uint8_t epnum = grxstsp_bm.ep_ch_num;
-//#if CFG_TUSB_DEBUG >= DWC2_DEBUG
-// const char * pktsts_str[] =
-// {
-// "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)",
-// "Setup Complete (ISR)", "ASSERT", "Setup Data Received"
-// };
-// TU_LOG_LOCATION();
-// TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]);
-// TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04X\r\n", (unsigned long) dwc2->daint, (unsigned int) epout->doepint);
-//#endif
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
- switch (pktsts) {
- // Global OUT NAK: do nothing
- case GRXSTS_PKTSTS_GLOBALOUTNAK:
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
+ // Global OUT NAK: do nothing
break;
- case GRXSTS_PKTSTS_SETUPRX:
+ case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
-
- // We can receive up to three setup packets in succession, but
- // only the last one is valid.
- _setup_packet[0] = (*rx_fifo);
- _setup_packet[1] = (*rx_fifo);
+ uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet;
+ // We can receive up to three setup packets in succession, but only the last one is valid.
+ setup[0] = (*rx_fifo);
+ setup[1] = (*rx_fifo);
break;
+ }
- case GRXSTS_PKTSTS_SETUPDONE:
- // Setup packet done (Interrupt)
+ case GRXSTS_PKTSTS_SETUP_DONE:
+ // Setup packet done:
+ // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
break;
- case GRXSTS_PKTSTS_OUTRX: {
+ case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
+ const uint16_t byte_count = grxstsp_bm.byte_count;
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // Read packet off RxFIFO
- if (xfer->ff) {
- // Ring buffer
- tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt);
- } else {
- // Linear buffer
- dfifo_read_packet(rhport, xfer->buffer, bcnt);
-
- // Increment pointer to xfer data
- xfer->buffer += bcnt;
- }
+ if (byte_count) {
+ // Read packet off RxFIFO
+ if (xfer->ff) {
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
+ } else {
+ dfifo_read_packet(dwc2, xfer->buffer, byte_count);
+ xfer->buffer += byte_count;
+ }
- // Truncate transfer length in case of short packet
- if (bcnt < xfer->max_size) {
- xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos;
- if (epnum == 0) {
- xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
- ep0_pending[TUSB_DIR_OUT] = 0;
+ // short packet, minus remaining bytes (xfer_size)
+ if (byte_count < xfer->max_size) {
+ xfer->total_len -= epout->tsiz_bm.xfer_size;
+ if (epnum == 0) {
+ xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
+ _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
+ }
}
}
- }
break;
+ }
- // Out packet done (Interrupt)
- case GRXSTS_PKTSTS_OUTDONE:
- // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a
- // May (or not) be 3.10a specific feature/bug or depending on MCU configuration
- // XFRC complete is additionally generated when
- // - setup packet is received
- // - complete the data stage of control write is complete
- // It will be handled in handle_epout_irq()
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // Out packet done
+ // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
+ // the specified OUT endpoint which will be handled by handle_epout_irq()
break;
- default: // Invalid
- TU_BREAKPOINT();
- break;
+ default: break;
}
}
-static void handle_epout_irq(uint8_t rhport) {
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
-
- // DAINT for a given EP clears when DOEPINTx is cleared.
- // OEPINT will be cleared when DAINT's out bits are cleared.
- for (uint8_t n = 0; n < ep_count; n++) {
- if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n)) {
- dwc2_epout_t* epout = &dwc2->epout[n];
+static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
+ if (doepint_bm.setup_phase_done) {
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- uint32_t const doepint = epout->doepint;
+ // Normal OUT transfer complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they
+ // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- TU_ASSERT((epout->doepint & DOEPINT_AHBERR) == 0, );
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet, Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+}
- // OUT XFER complete
- if (epout->doepint & DOEPINT_XFRC) {
- epout->doepint = DOEPINT_XFRC;
+static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2_dep_t* epin = &dwc2->epin[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
- if(dma_enabled(dwc2)) {
- if (doepint & DOEPINT_STUP) {
- // STPKTRX is only available for version from 3_00a
- if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) {
- epout->doepint = DOEPINT_STPKTRX;
- }
- } else if (doepint & DOEPINT_OTEPSPR) {
- epout->doepint = DOEPINT_OTEPSPR;
- } else {
- if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) {
- epout->doepint = DOEPINT_STPKTRX;
- } else {
- // EP0 can only handle one packet
- if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
- // Schedule another packet to be received.
- edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- } else {
- // Fix packet length
- uint16_t remain = (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos;
- xfer->total_len -= remain;
- // this is ZLP, so prepare EP0 for next setup
- if(n == 0 && xfer->total_len == 0) {
- dma_setup_prepare(rhport);
- }
+ // TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than
+ // - 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;
- dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- }
- }
- } else {
- if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid == DWC2_CORE_REV_3_10a)) {
- epout->doepint = DOEPINT_STPKTRX;
- } else {
- if ((doepint & DOEPINT_OTEPSPR) && (dwc2->gsnpsid == DWC2_CORE_REV_3_10a)) {
- epout->doepint = DOEPINT_OTEPSPR;
- }
+ // 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;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
- // EP0 can only handle one packet
- if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
- // Schedule another packet to be received.
- edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- } else {
- dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- }
- }
+ // Check if dtxfsts has enough space available
+ if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
}
- // SETUP packet Setup Phase done.
- if (doepint & DOEPINT_STUP) {
- epout->doepint = DOEPINT_STUP;
- if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) {
- epout->doepint = DOEPINT_STPKTRX;
- }
- if(dma_enabled(dwc2) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) {
- dma_setup_prepare(rhport);
- }
-
- dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
+ // Push packet to Tx-FIFO
+ if (xfer->ff) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
+ } else {
+ dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
+ xfer->buffer += xact_bytes;
}
}
+
+ // Turn off TXFE if all bytes are written.
+ if (epin->tsiz_bm.xfer_size == 0) {
+ dwc2->diepempmsk &= ~(1 << epnum);
+ }
}
}
+#endif
-static void handle_epin_irq(uint8_t rhport) {
+#if CFG_TUD_DWC2_DMA_ENABLE
+static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- dwc2_epin_t* epin = dwc2->epin;
- // DAINT for a given EP clears when DIEPINTx is cleared.
- // IEPINT will be cleared when DAINT's out bits are cleared.
- for (uint8_t n = 0; n < ep_count; n++) {
- if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) {
- // IN XFER complete (entire xfer).
- xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
+ if (doepint_bm.setup_phase_done) {
+ dma_setup_prepare(rhport);
+ dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- if (epin[n].diepint & DIEPINT_XFRC) {
- epin[n].diepint = DIEPINT_XFRC;
+ // OUT XFER complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Normal OUT transfer complete
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // EP0 can only handle one packet
- if ((n == 0) && ep0_pending[TUSB_DIR_IN]) {
- // Schedule another packet to be transmitted.
- edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
- } else {
- if((n == 0) && dma_enabled(dwc2)) {
- dma_setup_prepare(rhport);
- }
- dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ // determine actual received bytes
+ const uint16_t remain = epout->tsiz_bm.xfer_size;
+ xfer->total_len -= remain;
+
+ // this is ZLP, so prepare EP0 for next setup
+ // TODO use status phase rx
+ if(epnum == 0 && xfer->total_len == 0) {
+ dma_setup_prepare(rhport);
}
- }
- // XFER FIFO empty
- if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) {
- // diepint's TXFE bit is read-only, software cannot clear it.
- // It will only be cleared by hardware when written bytes is more than
- // - 64 bytes or
- // - Half of TX FIFO size (configured by DIEPTXF)
+ dcd_dcache_invalidate(xfer->buffer, xfer->total_len);
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+}
- uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos;
+static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- // Process every single packet (only whole packets can be written to fifo)
- for (uint16_t i = 0; i < remaining_packets; i++) {
- uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos;
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ if(epnum == 0) {
+ dma_setup_prepare(rhport);
+ }
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+#endif
- // Packet can not be larger than ep max size
- uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
+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 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];
- // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
- // EP has to be checked if the buffer can take another WHOLE packet
- if (packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) break;
+ // DAINT for a given EP clears when DEPINTx is cleared.
+ // EPINT will be cleared when DAINT bits are cleared.
+ for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
+ if (dwc2->daint & TU_BIT(daint_offset + epnum)) {
+ dwc2_dep_t* epout = &ep_base[epnum];
+ union {
+ uint32_t value;
+ dwc2_diepint_t diepint_bm;
+ dwc2_doepint_t doepint_bm;
+ } intr;
+ intr.value = epout->intr;
- // Push packet to Tx-FIFO
- if (xfer->ff) {
- volatile uint32_t* tx_fifo = dwc2->fifo[n];
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size);
- } else {
- dfifo_write_packet(rhport, n, xfer->buffer, packet_size);
+ epout->intr = intr.value; // Clear interrupt
- // Increment pointer to xfer data
- xfer->buffer += packet_size;
- }
+ if (is_dma) {
+ #if CFG_TUD_DWC2_DMA_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_dma(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_dma(rhport, epnum, intr.doepint_bm);
}
-
- // Turn off TXFE if all bytes are written.
- if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) {
- dwc2->diepempmsk &= ~(1 << n);
+ #endif
+ } else {
+ #if CFG_TUD_DWC2_SLAVE_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_slave(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_slave(rhport, epnum, intr.doepint_bm);
}
+ #endif
}
}
}
}
+/* Interrupt Hierarchy
+ DIEPINT DIEPINT
+ \ /
+ \ /
+ DAINT
+ / \
+ / \
+ GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL
+
+ Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk
+ are combined to generate dedicated interrupt line for each endpoint.
+ */
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint32_t const int_mask = dwc2->gintmsk;
- uint32_t const int_status = dwc2->gintsts & int_mask;
+ const uint32_t gintmask = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmask;
- if (int_status & GINTSTS_USBRST) {
+ if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
- bus_reset(rhport);
+ handle_bus_reset(rhport);
}
- if (int_status & GINTSTS_ENUMDNE) {
+ if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
dwc2->gintsts = GINTSTS_ENUMDNE;
-
- tusb_speed_t speed;
- switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) {
- case DSTS_ENUMSPD_HS:
- speed = TUSB_SPEED_HIGH;
- break;
-
- case DSTS_ENUMSPD_LS:
- speed = TUSB_SPEED_LOW;
- break;
-
- case DSTS_ENUMSPD_FS_HSPHY:
- case DSTS_ENUMSPD_FS:
- default:
- speed = TUSB_SPEED_FULL;
- break;
- }
-
- // TODO must update GUSBCFG_TRDT according to link speed
-
- dcd_event_bus_reset(rhport, speed, true);
+ handle_enum_done(rhport);
}
- if (int_status & GINTSTS_USBSUSP) {
+ if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
- if (int_status & GINTSTS_WKUINT) {
+ if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1246,9 +1012,9 @@ void dcd_int_handler(uint8_t rhport) {
// TODO check GINTSTS_DISCINT for disconnect detection
// if(int_status & GINTSTS_DISCINT)
- if (int_status & GINTSTS_OTGINT) {
+ if (gintsts & GINTSTS_OTGINT) {
// OTG INT bit is read-only
- uint32_t const otg_int = dwc2->gotgint;
+ const uint32_t otg_int = dwc2->gotgint;
if (otg_int & GOTGINT_SEDET) {
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
@@ -1257,50 +1023,43 @@ void dcd_int_handler(uint8_t rhport) {
dwc2->gotgint = otg_int;
}
- if(int_status & GINTSTS_SOF) {
+ if(gintsts & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
- if (!_sof_en) {
+ if (!_dcd_data.sof_en) {
dwc2->gintmsk &= ~GINTMSK_SOFM;
}
dcd_event_sof(rhport, frame, true);
}
+#if CFG_TUD_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling.
- if (int_status & GINTSTS_RXFLVL) {
+ if (gintsts & GINTSTS_RXFLVL) {
// RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
- // Mask out RXFLVL while reading data from FIFO
- dwc2->gintmsk &= ~GINTMSK_RXFLVLM;
-
- // Loop until all available packets were handled
do {
- handle_rxflvl_irq(rhport);
+ handle_rxflvl_irq(rhport); // read all packets
} while(dwc2->gintsts & GINTSTS_RXFLVL);
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
+#endif
// OUT endpoint interrupt handling.
- if (int_status & GINTSTS_OEPINT) {
+ if (gintsts & GINTSTS_OEPINT) {
// OEPINT is read-only, clear using DOEPINTn
- handle_epout_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_OUT);
}
// IN endpoint interrupt handling.
- if (int_status & GINTSTS_IEPINT) {
+ if (gintsts & GINTSTS_IEPINT) {
// IEPINT bit read-only, clear using DIEPINTn
- handle_epin_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_IN);
}
-
- // // Check for Incomplete isochronous IN transfer
- // if(int_status & GINTSTS_IISOIXFR) {
- // printf(" IISOIXFR!\r\n");
- //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n");
- // }
}
#if CFG_TUD_TEST_MODE
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
new file mode 100644
index 000000000..f80ae9acb
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -0,0 +1,311 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 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.
+ */
+
+#include "tusb_option.h"
+
+#define DWC2_COMMON_DEBUG 2
+
+#if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+
+#if CFG_TUD_ENABLED
+#include "device/dcd.h"
+#endif
+
+#if CFG_TUH_ENABLED
+#include "host/hcd.h"
+#endif
+
+#include "dwc2_common.h"
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+static void reset_core(dwc2_regs_t* dwc2) {
+ // reset core
+ dwc2->grstctl |= GRSTCTL_CSRST;
+
+ if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ // prior v42.0 CSRST is self-clearing
+ while (dwc2->grstctl & GRSTCTL_CSRST) {}
+ } else {
+ // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking.
+ // CSRST must also be explicitly cleared
+ while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {}
+ dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE;
+ }
+
+ while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
+}
+
+static void phy_fs_init(dwc2_regs_t* dwc2) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n");
+
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // Select FS PHY
+ gusbcfg |= GUSBCFG_PHYSEL;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY init before reset
+ dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
+ // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
+ // these bits can be programmed to a larger value. Default is 5
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= 5u << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+}
+
+static void phy_hs_init(dwc2_regs_t* dwc2) {
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // De-select FS PHY
+ gusbcfg &= ~GUSBCFG_PHYSEL;
+
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
+
+ // Select ULPI PHY (external)
+ gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+
+ // ULPI is always 8-bit interface
+ gusbcfg &= ~GUSBCFG_PHYIF16;
+
+ // ULPI select single data rate
+ gusbcfg &= ~GUSBCFG_DDRSEL;
+
+ // default internal VBUS Indicator and Drive
+ gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
+
+ // Disable FS/LS ULPI
+ gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
+ } else {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed UTMI+ PHY init\r\n");
+
+ // Select UTMI+ PHY (internal)
+ gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
+
+ // Set 16-bit interface if supported
+ if (dwc2->ghwcfg4_bm.phy_data_width) {
+ gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ } else {
+ gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
+ }
+ }
+
+ // Apply config
+ dwc2->gusbcfg = gusbcfg;
+
+ // mcu specific phy init
+ dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // Set turn-around, must after core reset otherwise it will be clear
+ // - 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;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+}
+
+static bool check_dwc2(dwc2_regs_t* dwc2) {
+#if CFG_TUSB_DEBUG >= DWC2_COMMON_DEBUG
+ // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
+ // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports
+ volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
+ TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
+ for (size_t i = 0; i < 5; i++) {
+ TU_LOG1("0x%08" PRIX32 ", ", p[i]);
+ }
+ TU_LOG1("0x%08" PRIX32 "\r\n", p[5]);
+#endif
+
+ // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it)
+ (void)dwc2;
+#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ enum { GSNPSID_ID_MASK = TU_GENMASK(31, 16) };
+ const uint32_t gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
+ TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
+ (void)dwc2;
+
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+#if CFG_TUH_ENABLED
+ if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+
+ return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
+}
+
+/* dwc2 has several PHYs option
+ * - UTMI+ is internal highspeed PHY, clock can be 30 Mhz (8-bit) or 60 Mhz (16-bit)
+ * - ULPI is external highspeed PHY, clock is 60Mhz with only 8-bit interface
+ * - Dedicated FS PHY is internal with clock 48Mhz.
+ *
+ * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
+ *
+*/
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Check Synopsys ID register, failed if controller clock/power is not enabled
+ TU_ASSERT(check_dwc2(dwc2));
+
+ // disable global interrupt
+ dwc2->gahbcfg &= ~GAHBCFG_GINT;
+
+ if (is_highspeed) {
+ phy_hs_init(dwc2);
+ } else {
+ phy_fs_init(dwc2);
+ }
+
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another. */
+ dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
+
+ // Enable PHY clock TODO stop/gate clock when suspended mode
+ dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE);
+
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+
+ // Clear pending and disable all interrupts
+ dwc2->gintsts = 0xFFFFFFFFU;
+ dwc2->gotgint = 0xFFFFFFFFU;
+ dwc2->gintmsk = 0;
+
+ TU_LOG(DWC2_COMMON_DEBUG, "DMA = %u\r\n", is_dma);
+
+ if (is_dma) {
+ // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly
+ dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
+ } else {
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ return true;
+}
+
+// void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr) {
+// (void) in_isr;
+// dwc2_regs_t * const dwc2 = DWC2_REG(rhport);
+// const uint32_t int_mask = dwc2->gintmsk;
+// const uint32_t int_status = dwc2->gintsts & int_mask;
+//
+// // Device disconnect
+// if (int_status & GINTSTS_DISCINT) {
+// dwc2->gintsts = GINTSTS_DISCINT;
+// }
+//
+// }
+
+//--------------------------------------------------------------------
+// DFIFO
+//--------------------------------------------------------------------
+// Read a single data packet from receive DFIFO
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) {
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+
+ // Reading full available 32 bit words from fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ tu_unaligned_write32(dst, *rx_fifo);
+ dst += 4;
+ }
+
+ // Read the remaining 1-3 bytes from fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem != 0) {
+ const uint32_t tmp = *rx_fifo;
+ dst[0] = tu_u32_byte0(tmp);
+ if (bytes_rem > 1) {
+ dst[1] = tu_u32_byte1(tmp);
+ }
+ if (bytes_rem > 2) {
+ dst[2] = tu_u32_byte2(tmp);
+ }
+ }
+}
+
+// Write a single data packet to DFIFO
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
+
+ // Pushing full available 32 bit words to fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ *tx_fifo = tu_unaligned_read32(src);
+ src += 4;
+ }
+
+ // Write the remaining 1-3 bytes into fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
+ uint32_t tmp_word = src[0];
+ if (bytes_rem > 1) {
+ tmp_word |= (src[1] << 8);
+ }
+ if (bytes_rem > 2) {
+ tmp_word |= (src[2] << 16);
+ }
+
+ *tx_fifo = tmp_word;
+ }
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
new file mode 100644
index 000000000..18b93894f
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -0,0 +1,101 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024 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_DWC2_COMMON_H
+#define TUSB_DWC2_COMMON_H
+
+#include "common/tusb_common.h"
+#include "dwc2_type.h"
+
+// Following symbols must be defined by port header
+// - _dwc2_controller[]: array of controllers
+// - DWC2_EP_MAX: largest EP counts of all controllers
+// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
+// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
+// - dwc2_remote_wakeup_delay
+
+#if defined(TUP_USBIP_DWC2_STM32)
+ #include "dwc2_stm32.h"
+#elif defined(TUP_USBIP_DWC2_ESP32)
+ #include "dwc2_esp32.h"
+#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #include "dwc2_gd32.h"
+#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
+ #include "dwc2_bcm.h"
+#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
+ #include "dwc2_efm32.h"
+#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
+ #include "dwc2_xmc.h"
+#else
+ #error "Unsupported MCUs"
+#endif
+
+enum {
+ DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller)
+};
+
+enum {
+ OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG
+};
+
+//--------------------------------------------------------------------+
+// Core/Controller
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
+ if (rhport >= DWC2_CONTROLLER_COUNT) {
+ // user mis-configured, ignore and use first controller
+ rhport = 0;
+ }
+ return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base;
+}
+
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role);
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma);
+void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
+
+//--------------------------------------------------------------------+
+// DFIFO
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) {
+ // flush TX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos);
+ while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
+ // flush RX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_RXFFLSH;
+ while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+}
+
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len);
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, uint16_t len);
+
+//--------------------------------------------------------------------+
+// DMA
+//--------------------------------------------------------------------+
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 4cdbcdb7a..ddbfd0d10 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -25,13 +25,14 @@
*/
-#ifndef _DWC2_ESP32_H_
-#define _DWC2_ESP32_H_
+#ifndef TUSB_DWC2_ESP32_H_
+#define TUSB_DWC2_ESP32_H_
#ifdef __cplusplus
extern "C" {
#endif
+#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_intr_alloc.h"
@@ -59,21 +60,37 @@ static const dwc2_controller_t _dwc2_controller[] = {
};
#endif
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
static intr_handle_t usb_ih[TU_ARRAY_SIZE(_dwc2_controller)];
-static void dcd_int_handler_wrap(void* arg) {
- const uint8_t rhport = (uint8_t)(uintptr_t) arg;
- dcd_int_handler(rhport);
+static void dwc2_int_handler_wrap(void* arg) {
+ const uint8_t rhport = tu_u16_low((uint16_t)(uintptr_t)arg);
+ const tusb_role_t role = (tusb_role_t) tu_u16_high((uint16_t)(uintptr_t)arg);
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE) {
+ dcd_int_handler(rhport);
+ }
+#endif
+#if CFG_TUH_ENABLED
+ if (role == TUSB_ROLE_HOST) {
+ hcd_int_handler(rhport, true);
+ }
+#endif
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) {
- esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
- dcd_int_handler_wrap, (void*)(uintptr_t) rhport, &usb_ih[rhport]);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ if (enabled) {
+ esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
+ dwc2_int_handler_wrap, (void*)(uintptr_t)tu_u16(role, rhport), &usb_ih[rhport]);
+ } else {
+ esp_intr_free(usb_ih[rhport]);
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
- esp_intr_free(usb_ih[rhport]);
-}
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
vTaskDelay(pdMS_TO_TICKS(1));
@@ -83,16 +100,57 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
- // nothing to do
+ // maybe usb_utmi_hal_init()
+
}
// MCU specific PHY update, it is called AFTER init() and core reset
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
- // nothing to do
+ // maybe usb_utmi_hal_disable()
+}
+
+//--------------------------------------------------------------------+
+// Data Cache
+//--------------------------------------------------------------------+
+#if CFG_TUD_DWC2_DMA_ENABLE || CFG_TUH_DWC2_DMA_ENABLE
+#if defined(SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE) && SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
+#include "esp_cache.h"
+
+#if CFG_TUD_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE || \
+ CFG_TUH_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE
+#error "CFG_TUD/TUH_MEM_DCACHE_LINE_SIZE must match CONFIG_CACHE_L1_CACHE_LINE_SIZE"
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) {
+ size = (size & ~(CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) + CONFIG_CACHE_L1_CACHE_LINE_SIZE;
+ }
+ return size;
}
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+#endif
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index 462b5856a..dec021f59 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG FS | ESP32-S2/S3 | ESP32-P4 | STM32F 407/411/429 FS | STM32F 407/429 HS | STM32F 412/767 FS | STM32F723 FS | STM32F723 HS | STM32H743 HS | STM32L476 FS | STM32U5A5 HS | GD32VF103 FS | XMC4500 |
-|:---------------------------|:----------------|:-------------|:--------------|:-----------|:------------------------|:--------------------|:--------------------|:---------------|:---------------|:---------------|:---------------|:---------------|:---------------|:-----------|
-| guid | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00001000 | 0x00AEC000 |
-| gsnpsid | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x00000000 | 0x4F54292A |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.30a | 3.10a | 4.11a | 0.00W | 2.92a |
-| ghwcfg1 | 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 | 0x229ED520 | 0x228FE052 | 0x00000000 | 0x228F5930 |
-| - op_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 |
-| - arch | 2 | 2 | 2 | 2 | 0 | 2 | 0 | 0 | 2 | 2 | 0 | 2 | 0 | 2 |
-| - point2point | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
-| - hs_phy_type | 1 | 0 | 0 | 3 | 0 | 2 | 0 | 0 | 3 | 2 | 0 | 1 | 0 | 0 |
-| - fs_phy_type | 1 | 1 | 1 | 3 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 5 | 8 | 0 | 6 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 11 | 15 | 0 | 13 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - mul_cpu_int | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 2 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 |
-| - ptx_q_depth | 2 | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 8 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| ghwcfg3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x00000000 | 0x027A01E5 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 5 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | 6 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 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 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 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 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 952 | 512 | 952 | 0 | 634 |
-| ghwcfg4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0x00000000 | 0xDBF08030 |
-| - num_dev_period_in_ep | 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 | 0 | 1 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - hibernation | 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 |
-| - reserved8 | 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 | 1 | 0 | 0 |
-| - service_interval_flow | 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 | 1 | 0 | 0 |
-| - acg_support | 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 | 1 | 0 | 0 |
-| - phy_data_width | 0 | 2 | 2 | 2 | 2 | 0 | 2 | 2 | 0 | 0 | 2 | 0 | 0 | 2 |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 5 | 8 | 0 | 6 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
+| | 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 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index e382ba011..25edcf22d 100644..100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -1,36 +1,40 @@
-import click
+#!/usr/bin/env python3
+
import ctypes
+import argparse
+import click
import pandas as pd
# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
# Note: FS is FullSpeed, HS is HighSpeed
-dwc2_reg_list = ['guid', 'gsnpsid', 'ghwcfg1', 'ghwcfg2', 'ghwcfg3', 'ghwcfg4']
+dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4']
dwc2_reg_value = {
'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020],
- 'EFM32GG FS': [0, 0x4F54330A, 0, 0x228F5910, 0x1F204E8, 0x1BF08030],
- 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0xC804B5, 0xD3F0A030],
+ 'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030],
+ 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030],
'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
- 'STM32F 407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030],
- 'STM32F 407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x3F403E8, 0x17F00030],
- 'STM32F 412/767 FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x200D1E8, 0x17F08030],
- 'STM32F723 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x200D1E8, 0x17F08030],
- 'STM32F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x3EED2E8, 0x23F00030],
- 'STM32H743 HS': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x3B8D2E8, 0xE3F00030], # both HS cores
- 'STM32L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x200D1E8, 0x17F08030],
- 'STM32U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
- 'GD32VF103 FS': [0x1000, 0, 0, 0, 0, 0],
- 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x27A01E5, 0xDBF08030]
+ 'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
+ 'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030],
+ 'ST F412/76x FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F723/L4P5 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x03EED2E8, 0x23F00030],
+ 'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
+ 'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
+ 'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
+ 'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
}
# Combine dwc2_info with dwc2_reg_list
# dwc2_info = {
# 'BCM2711 (Pi4)': {
-# 'guid': 0x2708A000,
-# 'gsnpsid': 0x4F54280A,
-# 'ghwcfg1': 0,
-# 'ghwcfg2': 0x228DDD50,
-# 'ghwcfg3': 0xFF000E8,
-# 'ghwcfg4': 0x1FF00020
+# 'GUID': 0x2708A000,
+# 'GSNPSID': 0x4F54280A,
+# 'GHWCFG1': 0,
+# 'GHWCFG2': 0x228DDD50,
+# 'GHWCFG3': 0xFF000E8,
+# 'GHWCFG4': 0x1FF00020
# },
dwc2_info = {key: {field: value for field, value in zip(dwc2_reg_list, values)} for key, values in dwc2_reg_value.items()}
@@ -39,14 +43,14 @@ class GHWCFG2(ctypes.LittleEndianStructure):
_fields_ = [
("op_mode", ctypes.c_uint32, 3),
("arch", ctypes.c_uint32, 2),
- ("point2point", ctypes.c_uint32, 1),
+ ("single_point", ctypes.c_uint32, 1),
("hs_phy_type", ctypes.c_uint32, 2),
("fs_phy_type", ctypes.c_uint32, 2),
("num_dev_ep", ctypes.c_uint32, 4),
("num_host_ch", ctypes.c_uint32, 4),
("period_channel_support", ctypes.c_uint32, 1),
("enable_dynamic_fifo", ctypes.c_uint32, 1),
- ("mul_cpu_int", ctypes.c_uint32, 1),
+ ("mul_proc_intrpt", ctypes.c_uint32, 1),
("reserved21", ctypes.c_uint32, 1),
("nptx_q_depth", ctypes.c_uint32, 2),
("ptx_q_depth", ctypes.c_uint32, 2),
@@ -98,63 +102,99 @@ class GHWCFG4(ctypes.LittleEndianStructure):
("dma_desc_dynamic", ctypes.c_uint32, 1)
]
+# mapping for specific fields in GHWCFG2
+GHWCFG2_field = {
+ 'op_mode': {
+ 0: "HNP SRP",
+ 1: "SRP",
+ 2: "noHNP noSRP",
+ 3: "SRP Device",
+ 4: "noOTG Device",
+ 5: "SRP Host",
+ 6: "noOTG Host"
+ },
+ 'arch': {
+ 0: "Slave only",
+ 1: "DMA external",
+ 2: "DMA internal"
+ },
+ 'single_point': {
+ 0: "hub",
+ 1: "n/a"
+ },
+ 'hs_phy_type': {
+ 0: "n/a",
+ 1: "UTMI+",
+ 2: "ULPI",
+ 3: "UTMI+/ULPI"
+ },
+ 'fs_phy_type': {
+ 0: "n/a",
+ 1: "Dedicated",
+ 2: "Shared UTMI+",
+ 3: "Shared ULPI"
+ },
+ 'nptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ },
+ 'ptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ 3: "16"
+ },
+}
-def cli():
- pass
-
-
[email protected]('mcus', nargs=-1)
[email protected]('-a', '--all', is_flag=True, help='Print all bit-field values')
-def info(mcus, all):
- """Print DWC2 register values for given MCU(s)"""
- if len(mcus) == 0:
- mcus = dwc2_info
+# mapping for specific fields in GHWCFG4
+GHWCFG4_field = {
+ 'phy_data_width': {
+ 0: "8 bit",
+ 1: "16 bit",
+ 2: "8/16 bit",
+ 3: "Reserved"
+ },
+ }
- for mcu in mcus:
- for entry in dwc2_info:
- if mcu.lower() in entry.lower():
- print(f"## {entry}")
- for r_name, r_value in dwc2_info[entry].items():
- print(f"{r_name} = 0x{r_value:08X}")
- # Print bit-field values
- if all and r_name.upper() in globals():
- class_name = globals()[r_name.upper()]
- ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little'))
- for field_name, field_type, _ in class_name._fields_:
- print(f" {field_name} = {getattr(ghwcfg, field_name)}")
+def main():
+ """Render dwc2_info to Markdown table"""
+ parser = argparse.ArgumentParser()
+ args = parser.parse_args()
-def render_md():
- """Render dwc2_info to Markdown table"""
# Create an empty list to hold the dictionaries
- dwc2_info_list = []
+ md_table = []
# Iterate over the dwc2_info dictionary and extract fields
for device, reg_values in dwc2_info.items():
- entry_dict = {"Device": device}
+ md_item = {"Device": device}
for r_name, r_value in reg_values.items():
- entry_dict[r_name] = f"0x{r_value:08X}"
+ md_item[r_name] = f"0x{r_value:08X}"
- if r_name == 'gsnpsid':
+ if r_name == 'GSNPSID':
# Get dwc2 specs version
major = ((r_value >> 8) >> 4) & 0x0F
minor = (r_value >> 4) & 0xFF
patch = chr((r_value & 0x0F) + ord('a') - 0xA)
- entry_dict[f' - specs version'] = f"{major:X}.{minor:02X}{patch}"
- elif r_name.upper() in globals():
+ md_item[f' - specs version'] = f"{major:X}.{minor:02X}{patch}"
+ elif r_name in globals():
# Get bit-field values which exist as ctypes structures
- class_name = globals()[r_name.upper()]
- ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little'))
- for field_name, field_type, _ in class_name._fields_:
- entry_dict[f' - {field_name}'] = getattr(ghwcfg, field_name)
+ class_hdl = globals()[r_name]
+ ghwcfg = class_hdl.from_buffer_copy(r_value.to_bytes(4, byteorder='little'))
+ for field_name, field_type, _ in class_hdl._fields_:
+ field_value = getattr(ghwcfg, field_name)
+ if class_hdl == GHWCFG2 and field_name in GHWCFG2_field:
+ field_value = GHWCFG2_field[field_name].get(field_value, f"Unknown ({field_value})")
+ if class_hdl == GHWCFG4 and field_name in GHWCFG4_field:
+ field_value = GHWCFG4_field[field_name].get(field_value, f"Unknown ({field_value})")
+
+ md_item[f' - {field_name}'] = field_value
- dwc2_info_list.append(entry_dict)
+ md_table.append(md_item)
# Create a Pandas DataFrame from the list of dictionaries
- df = pd.DataFrame(dwc2_info_list).set_index('Device')
+ df = pd.DataFrame(md_table).set_index('Device')
# Transpose the DataFrame to switch rows and columns
df = df.T
@@ -167,4 +207,4 @@ def render_md():
if __name__ == '__main__':
- cli()
+ main()
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index 395b6d870..c11c1eb05 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -69,6 +69,14 @@ extern "C" {
#define OTG_FS_IRQn OTG_HS_IRQn
#endif
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ #include "stm32h7rsxx.h"
+ #define EP_MAX_FS 6
+ #define EP_FIFO_SIZE_FS 1280
+
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
@@ -124,13 +132,19 @@ static const dwc2_controller_t _dwc2_controller[] = {
// SystemCoreClock is already included by family header
// extern uint32_t SystemCoreClock;
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) {
- NVIC_EnableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ (void) role;
+ const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum;
+ if (enabled) {
+ NVIC_EnableIRQ(irqn);
+ } else {
+ NVIC_DisableIRQ(irqn);
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
- NVIC_DisableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum);
-}
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
+
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// try to delay for 1 ms
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index ac88dd24d..812096759 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -31,8 +31,8 @@
* opensource.org/licenses/BSD-3-Clause
*/
-#ifndef _TUSB_DWC2_TYPES_H_
-#define _TUSB_DWC2_TYPES_H_
+#ifndef TUSB_DWC2_TYPES_H_
+#define TUSB_DWC2_TYPES_H_
#include "stdint.h"
@@ -87,6 +87,11 @@ typedef struct
#endif
enum {
+ GOTGCTL_OTG_VERSION_1_3 = 0,
+ GOTGCTL_OTG_VERSION_2_0 = 1,
+};
+
+enum {
GHWCFG2_OPMODE_HNP_SRP = 0,
GHWCFG2_OPMODE_SRP = 1,
GHWCFG2_OPMODE_NON_HNP_NON_SRP = 2,
@@ -122,8 +127,62 @@ enum {
GHWCFFG4_PHY_DATA_WIDTH_8_16 = 2, // software selectable
};
+enum {
+ HPRT_SPEED_HIGH = 0,
+ HPRT_SPEED_FULL = 1,
+ HPRT_SPEED_LOW = 2
+};
+
+enum {
+ GINTSTS_CMODE_DEVICE = 0,
+ GINTSTS_CMODE_HOST = 1,
+};
+
+enum {
+ HCTSIZ_PID_DATA0 = 0, // 00b
+ HCTSIZ_PID_DATA2 = 1, // 01b
+ HCTSIZ_PID_DATA1 = 2, // 10b
+ HCTSIZ_PID_SETUP = 3, // 11b
+};
+enum {
+ HCTSIZ_PID_MDATA = 3,
+};
+
+enum {
+ GRXSTS_PKTSTS_GLOBAL_OUT_NAK = 1,
+ GRXSTS_PKTSTS_RX_DATA = 2,
+ GRXSTS_PKTSTS_RX_COMPLETE = 3,
+ GRXSTS_PKTSTS_SETUP_DONE = 4,
+ GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5,
+ GRXSTS_PKTSTS_SETUP_RX = 6,
+ GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7
+};
+
+// Same as TUSB_XFER_*
+enum {
+ HCCHAR_EPTYPE_CONTROL = 0,
+ HCCHAR_EPTYPE_ISOCHRONOUS = 1,
+ HCCHAR_EPTYPE_BULK = 2,
+ HCCHAR_EPTYPE_INTERRUPT = 3
+};
+
+enum {
+ DCFG_SPEED_HIGH = 0, // Highspeed with 30/60 Mhz
+ DCFG_SPEED_FULL_30_60MHZ = 1, // Fullspeed with UTMI+/ULPI 30/60 Mhz
+ DCFG_SPEED_LOW = 2, // Lowspeed with FS PHY at 6 Mhz
+ DCFG_SPEED_FULL_48MHZ = 3, // Fullspeed with dedicated FS PHY at 48 Mhz
+};
+
+// Same as TUSB_XFER_*
+enum {
+ DEPCTL_EPTYPE_CONTROL = 0,
+ DEPCTL_EPTYPE_ISOCHRONOUS = 1,
+ DEPCTL_EPTYPE_BULK = 2,
+ DEPCTL_EPTYPE_INTERRUPT = 3
+};
+
//--------------------------------------------------------------------
-// Register bitfield definitions
+// Common Register Bitfield
//--------------------------------------------------------------------
typedef struct TU_ATTR_PACKED {
uint32_t ses_req_scs : 1; // 0 Session request success
@@ -146,7 +205,7 @@ typedef struct TU_ATTR_PACKED {
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 0: device, 1: host
+ 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
@@ -192,7 +251,7 @@ typedef struct TU_ATTR_PACKED {
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_if : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
+ 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
@@ -241,16 +300,27 @@ typedef struct TU_ATTR_PACKED {
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
+} 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 point2point : 1; // 5 0: support hub and split | 1: no hub, no split
+ 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_cpu_int : 1; // 20 Multi-Processor Interrupt 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
@@ -301,61 +371,309 @@ typedef struct TU_ATTR_PACKED {
}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
+} 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
+} 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;
+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
+} 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
+} 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
+} 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
+} dwc2_hfnum_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size");
+
// Host Channel
typedef struct {
- 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
+ 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
} dwc2_channel_t;
-// Endpoint IN
-typedef struct {
- volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control
- uint32_t reserved04; // 904
- volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt
- uint32_t reserved0c; // 90C
- volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size
- volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address
- volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status
- uint32_t reserved1c; // 91C
-} dwc2_epin_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
+} 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
+} 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
+} 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
+} 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
+} dwc2_depctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size");
-// Endpoint OUT
+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
+} 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
+} dwc2_ep_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
+
+// Device IN/OUT Endpoint
typedef struct {
- volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control
- uint32_t reserved04; // B04
- volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt
- uint32_t reserved0c; // B0C
- volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size
- volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address
- uint32_t reserved18[2]; // B18..B1C
-} dwc2_epout_t;
+ 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;
+ volatile uint32_t doepdma;
+ };
+ volatile uint32_t dtxfsts;
+ uint32_t rsv1c;
+}dwc2_dep_t;
+
+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
union {
volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size
};
- volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
+ 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
union {
@@ -389,14 +707,23 @@ typedef struct {
//------------ 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 Channel -------------//
@@ -404,12 +731,27 @@ typedef struct {
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
@@ -418,20 +760,28 @@ typedef struct {
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 msk
+ 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 -------------//
- dwc2_epin_t epin[16]; // 900..AFF IN Endpoints
- dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints
- uint32_t reservedd00[64]; // D00..DFF
+ 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
//------------- Power Clock -------------//
- volatile uint32_t pcgctl; // E00 Power and Clock Gating Control
- volatile uint32_t pcgctl1; // E04
+ 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 -------------//
@@ -444,7 +794,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size");
-TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size");
+TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgcctl) == 0x0E00, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
//--------------------------------------------------------------------+
@@ -508,14 +858,16 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version
/******************** Bit definition for HCFG register ********************/
-#define HCFG_FSLSPCS_Pos (0U)
-#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003
-#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select
-#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001
-#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002
-#define HCFG_FSLSS_Pos (2U)
-#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004
-#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support
+#define HCFG_FSLS_PHYCLK_SEL_Pos (0U)
+#define HCFG_FSLS_PHYCLK_SEL_Msk (0x3UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000003
+#define HCFG_FSLS_PHYCLK_SEL HCFG_FSLS_PHYCLK_SEL_Msk // FS/LS PHY clock select
+#define HCFG_FSLS_PHYCLK_SEL_30_60MHZ (0x0UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000000
+#define HCFG_FSLS_PHYCLK_SEL_48MHZ (0x1UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000001
+#define HCFG_FSLS_PHYCLK_SEL_6MHZ (0x2UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000002
+
+#define HCFG_FSLS_ONLY_Pos (2U)
+#define HCFG_FSLS_ONLY_Msk (0x1UL << HCFG_FSLS_ONLY_Pos) // 0x00000004
+#define HCFG_FSLS_ONLY HCFG_FSLS_ONLY_Msk // FS- and LS-only support
/******************** Bit definition for PCGCR register ********************/
#define PCGCR_STPPCLK_Pos (0U)
@@ -630,6 +982,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HFIR_FRIVL_Pos (0U)
#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF
#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval
+#define HFIR_RELOAD_CTRL_Pos (16U) // available since v2.92a
+#define HFIR_RELOAD_CTRL_Msk (0x1UL << HFIR_RELOAD_CTRL_Pos)
+#define HFIR_RELOAD_CTRL HFIR_RELOAD_CTRL_Msk
/******************** Bit definition for HFNUM register ********************/
#define HFNUM_FRNUM_Pos (0U)
@@ -674,19 +1029,17 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GAHBCFG_DMAEN_Pos (5U)
#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020
#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable
-#define GAHBCFG_TXFELVL_Pos (7U)
-#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080
-#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level
-#define GAHBCFG_PTXFELVL_Pos (8U)
-#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100
-#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level
-
-#define GSNPSID_ID_MASK TU_GENMASK(31, 16)
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Pos (7U)
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_TX_FIFO_EPMTY_LVL_Pos) // 0x00000080
+#define GAHBCFG_TX_FIFO_EPMTY_LVL GAHBCFG_TX_FIFO_EPMTY_LVL_Msk // TxFIFO empty level
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos (8U)
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos) // 0x00000100
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk // Periodic TxFIFO empty level
/******************** Bit definition for GUSBCFG register ********************/
#define GUSBCFG_TOCAL_Pos (0U)
#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007
-#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration
+#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // HS/FS timeout calibration
#define GUSBCFG_PHYIF16_Pos (3U)
#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008
#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf)
@@ -770,8 +1123,8 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100
#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200
#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400
-#define GRSTCTL_CSFTRST_DONE_Pos (29)
-#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a
+#define GRSTCTL_CSRST_DONE_Pos (29)
+#define GRSTCTL_CSRST_DONE (1u << GRSTCTL_CSRST_DONE_Pos) // Reset Done, only available from v4.20a
#define GRSTCTL_DMAREQ_Pos (30U)
#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000
#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal
@@ -892,9 +1245,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTSTS_RXFLVL_Pos (4U)
#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010
#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty
-#define GINTSTS_NPTXFE_Pos (5U)
-#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020
-#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty
+#define GINTSTS_NPTX_FIFO_EMPTY_Pos (5U)
+#define GINTSTS_NPTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_NPTX_FIFO_EMPTY_Pos) // 0x00000020
+#define GINTSTS_NPTX_FIFO_EMPTY GINTSTS_NPTX_FIFO_EMPTY_Msk // Nonperiodic TxFIFO empty
#define GINTSTS_GINAKEFF_Pos (6U)
#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040
#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective
@@ -943,15 +1296,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTSTS_HCINT_Pos (25U)
#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000
#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt
-#define GINTSTS_PTXFE_Pos (26U)
-#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000
-#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty
+#define GINTSTS_PTX_FIFO_EMPTY_Pos (26U)
+#define GINTSTS_PTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_PTX_FIFO_EMPTY_Pos) // 0x04000000
+#define GINTSTS_PTX_FIFO_EMPTY GINTSTS_PTX_FIFO_EMPTY_Msk // Periodic TxFIFO empty
#define GINTSTS_LPMINT_Pos (27U)
#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000
#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt
-#define GINTSTS_CIDSCHG_Pos (28U)
-#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000
-#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change
+#define GINTSTS_CONIDSTSCHNG_Pos (28U)
+#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000
+#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change
#define GINTSTS_DISCINT_Pos (29U)
#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000
#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt
@@ -1035,9 +1388,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTMSK_LPMINTM_Pos (27U)
#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000
#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask
-#define GINTMSK_CIDSCHGM_Pos (28U)
-#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000
-#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask
+#define GINTMSK_CONIDSTSCHNGM_Pos (28U)
+#define GINTMSK_CONIDSTSCHNGM_Msk (0x1UL << GINTMSK_CONIDSTSCHNGM_Pos) // 0x10000000
+#define GINTMSK_CONIDSTSCHNGM GINTMSK_CONIDSTSCHNGM_Msk // Connector ID status change mask
#define GINTMSK_DISCINT_Pos (29U)
#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000
#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask
@@ -1075,17 +1428,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000
#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits
-#define GRXSTS_PKTSTS_GLOBALOUTNAK 1
-#define GRXSTS_PKTSTS_OUTRX 2
-#define GRXSTS_PKTSTS_HCHIN 2
-#define GRXSTS_PKTSTS_OUTDONE 3
-#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3
-#define GRXSTS_PKTSTS_SETUPDONE 4
-#define GRXSTS_PKTSTS_DATATOGGLEERR 5
-#define GRXSTS_PKTSTS_SETUPRX 6
-#define GRXSTS_PKTSTS_HCHHALTED 7
-
-
/******************** Bit definition for DAINTMSK register ********************/
#define DAINTMSK_IEPM_Pos (0U)
#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF
@@ -1094,6 +1436,8 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000
#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits
+#define DAINT_SHIFT(_dir) ((_dir == TUSB_DIR_IN) ? 0 : 16)
+
#if 0
/******************** Bit definition for OTG register ********************/
#define CHNUM_Pos (0U)
@@ -1412,56 +1756,53 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask
/******************** Bit definition for HPRT register ********************/
-#define HPRT_PCSTS_Pos (0U)
-#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001
-#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status
-#define HPRT_PCDET_Pos (1U)
-#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002
-#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected
-#define HPRT_PENA_Pos (2U)
-#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004
-#define HPRT_PENA HPRT_PENA_Msk // Port enable
-#define HPRT_PENCHNG_Pos (3U)
-#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008
-#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change
-#define HPRT_POCA_Pos (4U)
-#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010
-#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active
-#define HPRT_POCCHNG_Pos (5U)
-#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020
-#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change
-#define HPRT_PRES_Pos (6U)
-#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040
-#define HPRT_PRES HPRT_PRES_Msk // Port resume
-#define HPRT_PSUSP_Pos (7U)
-#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080
-#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend
-#define HPRT_PRST_Pos (8U)
-#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100
-#define HPRT_PRST HPRT_PRST_Msk // Port reset
-
-#define HPRT_PLSTS_Pos (10U)
-#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00
-#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status
-#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400
-#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800
-#define HPRT_PPWR_Pos (12U)
-#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000
-#define HPRT_PPWR HPRT_PPWR_Msk // Port power
-
-#define HPRT_PTCTL_Pos (13U)
-#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000
-#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control
-#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000
-#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000
-#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000
-#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000
-
-#define HPRT_PSPD_Pos (17U)
-#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000
-#define HPRT_PSPD HPRT_PSPD_Msk // Port speed
-#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000
-#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000
+#define HPRT_CONN_STATUS_Pos (0U)
+#define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001
+#define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status
+#define HPRT_CONN_DETECT_Pos (1U)
+#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002
+#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected
+#define HPRT_ENABLE_Pos (2U)
+#define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004
+#define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable
+#define HPRT_ENABLE_CHANGE_Pos (3U)
+#define HPRT_ENABLE_CHANGE_Msk (0x1UL << HPRT_ENABLE_CHANGE_Pos) // 0x00000008
+#define HPRT_ENABLE_CHANGE HPRT_ENABLE_CHANGE_Msk // Port enable/disable change
+#define HPRT_OVER_CURRENT_ACTIVE_Pos (4U)
+#define HPRT_OVER_CURRENT_ACTIVE_Msk (0x1UL << HPRT_OVER_CURRENT_ACTIVE_Pos) // 0x00000010
+#define HPRT_OVER_CURRENT_ACTIVE HPRT_OVER_CURRENT_ACTIVE_Msk // Port overcurrent active
+#define HPRT_OVER_CURRENT_CHANGE_Pos (5U)
+#define HPRT_OVER_CURRENT_CHANGE_Msk (0x1UL << HPRT_OVER_CURRENT_CHANGE_Pos) // 0x00000020
+#define HPRT_OVER_CURRENT_CHANGE HPRT_OVER_CURRENT_CHANGE_Msk // Port overcurrent change
+#define HPRT_RESUME_Pos (6U)
+#define HPRT_RESUME_Msk (0x1UL << HPRT_RESUME_Pos) // 0x00000040
+#define HPRT_RESUME HPRT_RESUME_Msk // Port resume
+#define HPRT_SUSPEND_Pos (7U)
+#define HPRT_SUSPEND_Msk (0x1UL << HPRT_SUSPEND_Pos) // 0x00000080
+#define HPRT_SUSPEND HPRT_SUSPEND_Msk // Port suspend
+#define HPRT_RESET_Pos (8U)
+#define HPRT_RESET_Msk (0x1UL << HPRT_RESET_Pos) // 0x00000100
+#define HPRT_RESET HPRT_RESET_Msk // Port reset
+#define HPRT_LINE_STATUS_Pos (10U)
+#define HPRT_LINE_STATUS_Msk (0x3UL << HPRT_LINE_STATUS_Pos) // 0x00000C00
+#define HPRT_LINE_STATUS HPRT_LINE_STATUS_Msk // Port line status
+#define HPRT_LINE_STATUS_0 (0x1UL << HPRT_LINE_STATUS_Pos) // 0x00000400
+#define HPRT_LINE_STATUS_1 (0x2UL << HPRT_LINE_STATUS_Pos) // 0x00000800
+#define HPRT_POWER_Pos (12U)
+#define HPRT_POWER_Msk (0x1UL << HPRT_POWER_Pos) // 0x00001000
+#define HPRT_POWER HPRT_POWER_Msk // Port power
+#define HPRT_TEST_CONTROL_Pos (13U)
+#define HPRT_TEST_CONTROL_Msk (0xFUL << HPRT_TEST_CONTROL_Pos) // 0x0001E000
+#define HPRT_TEST_CONTROL HPRT_TEST_CONTROL_Msk // Port test control
+#define HPRT_TEST_CONTROL_0 (0x1UL << HPRT_TEST_CONTROL_Pos) // 0x00002000
+#define HPRT_TEST_CONTROL_1 (0x2UL << HPRT_TEST_CONTROL_Pos) // 0x00004000
+#define HPRT_TEST_CONTROL_2 (0x4UL << HPRT_TEST_CONTROL_Pos) // 0x00008000
+#define HPRT_TEST_CONTROL_3 (0x8UL << HPRT_TEST_CONTROL_Pos) // 0x00010000
+#define HPRT_SPEED_Pos (17U)
+#define HPRT_SPEED_Msk (0x3UL << HPRT_SPEED_Pos) // 0x00060000
+#define HPRT_SPEED HPRT_SPEED_Msk // Port speed
+#define HPRT_SPEED_0 (0x1UL << HPRT_SPEED_Pos) // 0x00020000
+#define HPRT_SPEED_1 (0x2UL << HPRT_SPEED_Pos) // 0x00040000
/******************** Bit definition for DOEPEACHMSK1 register ********************/
#define DOEPEACHMSK1_XFRCM_Pos (0U)
@@ -1643,15 +1984,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable
/******************** Bit definition for HCINT register ********************/
-#define HCINT_XFRC_Pos (0U)
-#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001
-#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed
-#define HCINT_CHH_Pos (1U)
-#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002
-#define HCINT_CHH HCINT_CHH_Msk // Channel halted
-#define HCINT_AHBERR_Pos (2U)
-#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004
-#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error
+#define HCINT_XFER_COMPLETE_Pos (0U)
+#define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001
+#define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed
+#define HCINT_HALTED_Pos (1U)
+#define HCINT_HALTED_Msk (0x1UL << HCINT_HALTED_Pos) // 0x00000002
+#define HCINT_HALTED HCINT_HALTED_Msk // Channel halted
+#define HCINT_AHB_ERR_Pos (2U)
+#define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004
+#define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error
#define HCINT_STALL_Pos (3U)
#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008
#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt
@@ -1664,18 +2005,27 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCINT_NYET_Pos (6U)
#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040
#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt
-#define HCINT_TXERR_Pos (7U)
-#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080
-#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error
-#define HCINT_BBERR_Pos (8U)
-#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100
-#define HCINT_BBERR HCINT_BBERR_Msk // Babble error
-#define HCINT_FRMOR_Pos (9U)
-#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200
-#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun
-#define HCINT_DTERR_Pos (10U)
-#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400
-#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error
+#define HCINT_XACT_ERR_Pos (7U)
+#define HCINT_XACT_ERR_Msk (0x1UL << HCINT_XACT_ERR_Pos) // 0x00000080
+#define HCINT_XACT_ERR HCINT_XACT_ERR_Msk // Transaction error
+#define HCINT_BABBLE_ERR_Pos (8U)
+#define HCINT_BABBLE_ERR_Msk (0x1UL << HCINT_BABBLE_ERR_Pos) // 0x00000100
+#define HCINT_BABBLE_ERR HCINT_BABBLE_ERR_Msk // Babble error
+#define HCINT_FARME_OVERRUN_Pos (9U)
+#define HCINT_FARME_OVERRUN_Msk (0x1UL << HCINT_FARME_OVERRUN_Pos) // 0x00000200
+#define HCINT_FARME_OVERRUN HCINT_FARME_OVERRUN_Msk // Frame overrun
+#define HCINT_DATATOGGLE_ERR_Pos (10U)
+#define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400
+#define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error
+#define HCINT_BUFFER_NA_Pos (11U)
+#define HCINT_BUFFER_NA_Msk (0x1UL << HCINT_BUFFER_NA_Pos) // 0x00000800
+#define HCINT_BUFFER_NA HCINT_BUFFER_NA_Msk // Buffer not available interrupt
+#define HCINT_XCS_XACT_ERR_Pos (12U)
+#define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000
+#define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error
+#define HCINT_DESC_ROLLOVER_Pos (13U)
+#define HCINT_DESC_ROLLOVER_Msk (0x1UL << HCINT_DESC_ROLLOVER_Pos) // 0x00002000
+#define HCINT_DESC_ROLLOVER HCINT_DESC_ROLLOVER_Msk // Descriptor rollover
/******************** Bit definition for DIEPINT register ********************/
#define DIEPINT_XFRC_Pos (0U)
@@ -1718,41 +2068,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000
#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt
-/******************** Bit definition for HCINTMSK register ********************/
-#define HCINTMSK_XFRCM_Pos (0U)
-#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001
-#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask
-#define HCINTMSK_CHHM_Pos (1U)
-#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002
-#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask
-#define HCINTMSK_AHBERR_Pos (2U)
-#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004
-#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error
-#define HCINTMSK_STALLM_Pos (3U)
-#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008
-#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask
-#define HCINTMSK_NAKM_Pos (4U)
-#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010
-#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask
-#define HCINTMSK_ACKM_Pos (5U)
-#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020
-#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask
-#define HCINTMSK_NYET_Pos (6U)
-#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040
-#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask
-#define HCINTMSK_TXERRM_Pos (7U)
-#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080
-#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask
-#define HCINTMSK_BBERRM_Pos (8U)
-#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100
-#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask
-#define HCINTMSK_FRMORM_Pos (9U)
-#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200
-#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask
-#define HCINTMSK_DTERRM_Pos (10U)
-#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400
-#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask
-
/******************** Bit definition for DIEPTSIZ register ********************/
#define DIEPTSIZ_XFRSIZ_Pos (0U)
@@ -1774,11 +2089,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_DPID_Pos (29U)
-#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000
-#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID
-#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000
-#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000
+#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)
@@ -1803,6 +2116,45 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DIEPTXF_INEPTXFD_Msk (0xFFFFUL << DIEPTXF_INEPTXFD_Pos) // 0xFFFF0000
#define DIEPTXF_INEPTXFD DIEPTXF_INEPTXFD_Msk // IN endpoint TxFIFO depth
+
+/******************** Bit definition for Common EPCTL register ********************/
+#define EPCTL_MPSIZ_Pos (0U)
+#define EPCTL_MPSIZ_Msk (0x7FFUL << EPCTL_MPSIZ_Pos) // 0x000007FF
+#define EPCTL_MPSIZ EPCTL_MPSIZ_Msk // Maximum packet size //Bit 1
+#define EPCTL_USBAEP_Pos (15U)
+#define EPCTL_USBAEP_Msk (0x1UL << EPCTL_USBAEP_Pos) // 0x00008000
+#define EPCTL_USBAEP EPCTL_USBAEP_Msk // USB active endpoint
+#define EPCTL_NAKSTS_Pos (17U)
+#define EPCTL_NAKSTS_Msk (0x1UL << EPCTL_NAKSTS_Pos) // 0x00020000
+#define EPCTL_NAKSTS EPCTL_NAKSTS_Msk // NAK status
+#define EPCTL_EPTYP_Pos (18U)
+#define EPCTL_EPTYP_Msk (0x3UL << EPCTL_EPTYP_Pos) // 0x000C0000
+#define EPCTL_EPTYP EPCTL_EPTYP_Msk // Endpoint type
+#define EPCTL_EPTYP_0 (0x1UL << EPCTL_EPTYP_Pos) // 0x00040000
+#define EPCTL_EPTYP_1 (0x2UL << EPCTL_EPTYP_Pos) // 0x00080000
+#define EPCTL_SNPM EPCTL_SNPM_Msk // Snoop mode
+#define EPCTL_STALL_Pos (21U)
+#define EPCTL_STALL_Msk (0x1UL << EPCTL_STALL_Pos) // 0x00200000
+#define EPCTL_STALL EPCTL_STALL_Msk // STALL handshake
+#define EPCTL_CNAK_Pos (26U)
+#define EPCTL_CNAK_Msk (0x1UL << EPCTL_CNAK_Pos) // 0x04000000
+#define EPCTL_CNAK EPCTL_CNAK_Msk // Clear NAK
+#define EPCTL_SNAK_Pos (27U)
+#define EPCTL_SNAK_Msk (0x1UL << EPCTL_SNAK_Pos) // 0x08000000
+#define EPCTL_SNAK EPCTL_SNAK_Msk // Set NAK
+#define EPCTL_SD0PID_SEVNFRM_Pos (28U)
+#define EPCTL_SD0PID_SEVNFRM_Msk (0x1UL << EPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000
+#define EPCTL_SD0PID_SEVNFRM EPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID
+#define EPCTL_SODDFRM_Pos (29U)
+#define EPCTL_SODDFRM_Msk (0x1UL << EPCTL_SODDFRM_Pos) // 0x20000000
+#define EPCTL_SODDFRM EPCTL_SODDFRM_Msk // Set odd frame
+#define EPCTL_EPDIS_Pos (30U)
+#define EPCTL_EPDIS_Msk (0x1UL << EPCTL_EPDIS_Pos) // 0x40000000
+#define EPCTL_EPDIS EPCTL_EPDIS_Msk // Endpoint disable
+#define EPCTL_EPENA_Pos (31U)
+#define EPCTL_EPENA_Msk (0x1UL << EPCTL_EPENA_Pos) // 0x80000000
+#define EPCTL_EPENA EPCTL_EPENA_Msk // Endpoint enable
+
/******************** Bit definition for DOEPCTL register ********************/
#define DOEPCTL_MPSIZ_Pos (0U)
#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF
@@ -1853,15 +2205,19 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DOEPINT_AHBERR_Pos (2U)
#define DOEPINT_AHBERR_Msk (0x1UL << DOEPINT_AHBERR_Pos) // 0x00000004
#define DOEPINT_AHBERR DOEPINT_AHBERR_Msk // AHB Error (AHBErr) during an OUT transaction
-#define DOEPINT_STUP_Pos (3U)
-#define DOEPINT_STUP_Msk (0x1UL << DOEPINT_STUP_Pos) // 0x00000008
-#define DOEPINT_STUP DOEPINT_STUP_Msk // SETUP phase done
+
+#define DOEPINT_SETUP_Pos (3U)
+#define DOEPINT_SETUP_Msk (0x1UL << DOEPINT_SETUP_Pos) // 0x00000008
+#define DOEPINT_SETUP DOEPINT_SETUP_Msk // SETUP phase done
+
#define DOEPINT_OTEPDIS_Pos (4U)
#define DOEPINT_OTEPDIS_Msk (0x1UL << DOEPINT_OTEPDIS_Pos) // 0x00000010
#define DOEPINT_OTEPDIS DOEPINT_OTEPDIS_Msk // OUT token received when endpoint disabled
-#define DOEPINT_OTEPSPR_Pos (5U)
-#define DOEPINT_OTEPSPR_Msk (0x1UL << DOEPINT_OTEPSPR_Pos) // 0x00000020
-#define DOEPINT_OTEPSPR DOEPINT_OTEPSPR_Msk // Status Phase Received For Control Write
+
+#define DOEPINT_STSPHSRX_Pos (5U)
+#define DOEPINT_STSPHSRX_Msk (0x1UL << DOEPINT_STSPHSRX_Pos) // 0x00000020
+#define DOEPINT_STSPHSRX DOEPINT_STSPHSRX_Msk // Status Phase Received For Control Write
+
#define DOEPINT_B2BSTUP_Pos (6U)
#define DOEPINT_B2BSTUP_Msk (0x1UL << DOEPINT_B2BSTUP_Pos) // 0x00000040
#define DOEPINT_B2BSTUP DOEPINT_B2BSTUP_Msk // Back-to-back SETUP packets received
@@ -1874,6 +2230,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DOEPINT_NYET_Pos (14U)
#define DOEPINT_NYET_Msk (0x1UL << DOEPINT_NYET_Pos) // 0x00004000
#define DOEPINT_NYET DOEPINT_NYET_Msk // NYET interrupt
+
#define DOEPINT_STPKTRX_Pos (15U)
#define DOEPINT_STPKTRX_Msk (0x1UL << DOEPINT_STPKTRX_Pos) // 0x00008000
#define DOEPINT_STPKTRX DOEPINT_STPKTRX_Msk // Setup Packet Received
@@ -1893,32 +2250,32 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000
/******************** Bit definition for PCGCTL register ********************/
-#define PCGCTL_IF_DEV_MODE TU_BIT(31)
-#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
-#define PCGCTL_P2HD_PRT_SPD_SHIFT 29
-#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
-#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
-#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
-#define PCGCTL_MAC_DEV_ADDR_SHIFT 20
-#define PCGCTL_MAX_TERMSEL TU_BIT(19)
-#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
-#define PCGCTL_MAX_XCVRSELECT_SHIFT 17
-#define PCGCTL_PORT_POWER TU_BIT(16)
-#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
-#define PCGCTL_PRT_CLK_SEL_SHIFT 14
-#define PCGCTL_ESS_REG_RESTORED TU_BIT(13)
-#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12)
-#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
-#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10)
-#define PCGCTL_RESTOREMODE TU_BIT(9)
-#define PCGCTL_RESETAFTSUSP TU_BIT(8)
-#define PCGCTL_DEEP_SLEEP TU_BIT(7)
-#define PCGCTL_PHY_IN_SLEEP TU_BIT(6)
-#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5)
-#define PCGCTL_RSTPDWNMODULE TU_BIT(3)
-#define PCGCTL_PWRCLMP TU_BIT(2)
-#define PCGCTL_GATEHCLK TU_BIT(1)
-#define PCGCTL_STOPPCLK TU_BIT(0)
+#define PCGCCTL_IF_DEV_MODE TU_BIT(31)
+#define PCGCCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
+#define PCGCCTL_P2HD_PRT_SPD_SHIFT 29
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
+#define PCGCCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
+#define PCGCCTL_MAC_DEV_ADDR_SHIFT 20
+#define PCGCCTL_MAX_TERMSEL TU_BIT(19)
+#define PCGCCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
+#define PCGCCTL_MAX_XCVRSELECT_SHIFT 17
+#define PCGCCTL_PORT_POWER TU_BIT(16)
+#define PCGCCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
+#define PCGCCTL_PRT_CLK_SEL_SHIFT 14
+#define PCGCCTL_ESS_REG_RESTORED TU_BIT(13)
+#define PCGCCTL_EXTND_HIBER_SWITCH TU_BIT(12)
+#define PCGCCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
+#define PCGCCTL_ENBL_EXTND_HIBER TU_BIT(10)
+#define PCGCCTL_RESTOREMODE TU_BIT(9)
+#define PCGCCTL_RESETAFTSUSP TU_BIT(8)
+#define PCGCCTL_DEEP_SLEEP TU_BIT(7)
+#define PCGCCTL_PHY_IN_SLEEP TU_BIT(6)
+#define PCGCCTL_ENBL_SLEEP_GATING TU_BIT(5)
+#define PCGCCTL_RSTPDWNMODULE TU_BIT(3)
+#define PCGCCTL_PWRCLMP TU_BIT(2)
+#define PCGCCTL_GATEHCLK TU_BIT(1)
+#define PCGCCTL_STOPPCLK TU_BIT(0)
#define PCGCTL1_TIMER (0x3ul << 1)
#define PCGCTL1_GATEEN TU_BIT(0)
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
new file mode 100644
index 000000000..ebbb6200a
--- /dev/null
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -0,0 +1,1360 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 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.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2)
+
+#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
+#endif
+
+// Debug level for DWC2
+#define DWC2_DEBUG 2
+
+#include "host/hcd.h"
+#include "dwc2_common.h"
+
+// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
+#ifndef CFG_TUH_DWC2_ENDPOINT_MAX
+#define CFG_TUH_DWC2_ENDPOINT_MAX 16
+#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 {
+ HPRT_W1_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE | HPRT_SUSPEND
+};
+
+enum {
+ HCD_XFER_ERROR_MAX = 3
+};
+
+enum {
+ HCD_XFER_PERIOD_SPLIT_NYET_MAX = 3
+};
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+// Host driver struct for each opened endpoint
+typedef struct {
+ union {
+ uint32_t hcchar;
+ dwc2_channel_char_t hcchar_bm;
+ };
+ union {
+ uint32_t hcsplt;
+ dwc2_channel_split_t hcsplt_bm;
+ };
+
+ 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 : 9;
+ };
+
+ uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit
+
+ uint8_t* buffer;
+ uint16_t buflen;
+} hcd_endpoint_t;
+
+// Additional info for each channel when it is active
+typedef struct {
+ volatile bool allocated;
+ uint8_t ep_id;
+ struct TU_ATTR_PACKED {
+ 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;
+
+ uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can
+ // be composed of multiple channel_xfer_start() (retry with NAK/NYET)
+ uint16_t fifo_bytes; // bytes written/read from/to FIFO (may not be transferred on USB bus).
+} hcd_xfer_t;
+
+typedef struct {
+ hcd_xfer_t xfer[DWC2_CHANNEL_COUNT_MAX];
+ hcd_endpoint_t edpt[CFG_TUH_DWC2_ENDPOINT_MAX];
+} hcd_data_t;
+
+hcd_data_t _hcd_data;
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+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) {
+ 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;
+ default:
+ speed = TUSB_SPEED_INVALID;
+ TU_BREAKPOINT();
+ break;
+ }
+ return speed;
+}
+
+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;
+}
+
+#if CFG_TUH_MEM_DCACHE_ENABLE
+bool hcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
+}
+
+bool hcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
+}
+
+bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
+// 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);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ if (!xfer->allocated) {
+ tu_memclr(xfer, sizeof(hcd_xfer_t));
+ xfer->allocated = true;
+ return ch_id;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// 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 uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
+ if (is_period) {
+ return dwc2->hptxsts_bm.req_queue_available;
+ } else {
+ return dwc2->hnptxsts_bm.req_queue_available;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->allocated = false;
+ dwc2->haintmsk &= ~TU_BIT(ch_id);
+}
+
+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)));
+ channel->hcintmsk |= HCINT_HALTED;
+ channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
+ return true;
+}
+
+// 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)));
+ 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);
+ 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)) {
+ return ch_id;
+ }
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+
+// Allocate a new endpoint
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) {
+ for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable == 0) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ edpt->hcchar_bm.enable = 1;
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// Find a endpoint that is opened previously with hcd_edpt_open()
+// Note: EP0 is bidirectional
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
+ for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ const dwc2_channel_char_t* hcchar_bm = &_hcd_data.edpt[i].hcchar_bm;
+ if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr &&
+ hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t cal_packet_count(uint16_t len, uint16_t ep_size) {
+ if (len == 0) {
+ return 1;
+ } else {
+ return tu_div_ceil(len, ep_size);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) {
+ if (packet_count & 0x01) {
+ return pid ^ 0x02; // toggle DATA0 and DATA1
+ } else {
+ return pid;
+ }
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+/* USB Data FIFO Layout
+
+ The FIFO is split up into
+ - EPInfo: for storing DMA metadata (check dcd_dwc2.c for more details)
+ - 1 RX FIFO: for receiving data
+ - 1 TX FIFO for non-periodic (NPTX)
+ - 1 TX FIFO for periodic (PTX)
+
+ We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
+ possible since the free space is located between the RX and TX FIFOs.
+
+ ----------------- ep_fifo_size
+ | HCDMAn |
+ |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ | Non-Periodic |
+ | TX FIFO |
+ |--------------|--- GNPTXFSIZ.addr (fixed size)
+ | Periodic |
+ | TX FIFO |
+ |--------------|--- HPTXFSIZ.addr (expandable downward)
+ | FREE |
+ | |
+ |--------------|-- GRXFSIZ (expandable upward)
+ | RX FIFO |
+ ---------------- 0
+*/
+
+/* Programming Guide 2.1.2 FIFO RAM allocation
+ * RX
+ * - Largest-EPsize/4 + 2 (status info). recommended x2 if high bandwidth or multiple ISO are used.
+ * - 2 for transfer complete and channel halted status
+ * - 1 for each Control/Bulk out endpoint to Handle NAK/NYET (i.e max is number of host channel)
+ *
+ * TX non-periodic (NPTX)
+ * - At least largest-EPsize/4, recommended x2
+ *
+ * TX periodic (PTX)
+ * - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt)
+*/
+static void dfifo_host_init(uint8_t rhport) {
+ const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // 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;
+ }
+
+ // fixed allocation for now, improve later:
+ // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
+ bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4;
+ uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
+
+ uint16_t nptxfsiz = 2 * nptx_largest;
+ uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch;
+ TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
+ uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
+
+ dwc2->gdfifocfg = (dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | dfifo_top;
+
+ dfifo_top -= rxfsiz;
+ dwc2->grxfsiz = rxfsiz;
+
+ dfifo_top -= nptxfsiz;
+ dwc2->gnptxfsiz = tu_u32_from_u16(nptxfsiz, dfifo_top);
+
+ dfifo_top -= ptxfsiz;
+ dwc2->hptxfsiz = tu_u32_from_u16(ptxfsiz, dfifo_top);
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ (void) cfg_id;
+ (void) cfg_param;
+
+ return true;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ tu_memclr(&_hcd_data, sizeof(_hcd_data));
+
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ const bool is_dma = dma_host_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+
+ //------------- 3.1 Host Initialization -------------//
+
+ // work at max supported speed
+ dwc2->hcfg &= ~HCFG_FSLS_ONLY;
+
+ // Enable HFIR reload
+ if (dwc2->gsnpsid >= DWC2_CORE_REV_2_92a) {
+ dwc2->hfir |= HFIR_RELOAD_CTRL;
+ }
+
+ // force host mode and wait for mode switch
+ dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
+ while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
+
+ // configure fixed-allocated fifo scheme
+ dfifo_host_init(rhport);
+
+ dwc2->hprt = HPRT_W1_MASK; // clear all write-1-clear bits
+ dwc2->hprt = HPRT_POWER; // turn on VBUS
+
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT;
+
+ // NPTX can hold at least 2 packet, change interrupt level to half-empty
+ uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
+
+ return true;
+}
+
+// Enable USB interrupt
+void hcd_int_enable (uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, true);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, false);
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hfnum & HFNUM_FRNUM_Msk;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hprt & HPRT_CONN_STATUS;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
+void hcd_port_reset(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
+ hprt |= HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; // skip w1c bits
+ hprt &= ~HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ return speed;
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable && edpt->hcchar_bm.dev_addr == dev_addr) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) {
+ 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);
+
+ // find a free endpoint
+ const uint8_t ep_id = edpt_alloc();
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
+ 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->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_*
+ hcchar_bm->err_multi_count = 0;
+ hcchar_bm->dev_addr = dev_addr;
+ hcchar_bm->odd_frame = 0;
+ hcchar_bm->disable = 0;
+ 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->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;
+
+ edpt->speed = devtree_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) {
+ edpt->uframe_interval <<= 3;
+ }
+ } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
+ if (devtree_info.speed == TUSB_SPEED_HIGH) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ } else {
+ edpt->uframe_interval = desc_ep->bInterval << 3;
+ }
+ }
+
+ return true;
+}
+
+// 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];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ edpt->next_pid = channel->hctsiz_bm.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.
+ * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the
+ * 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;
+
+ xfer->fifo_bytes = 0;
+ xfer->xferred_bytes += actual_bytes;
+ edpt->buffer += actual_bytes;
+ edpt->buflen -= actual_bytes;
+}
+
+static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[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);
+
+ // clear previous state
+ xfer->fifo_bytes = 0;
+
+ // hchar: restore but don't enable yet
+ if (is_period) {
+ hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ }
+ channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA);
+
+ // 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 &&
+ edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
+ hctsiz |= HCTSIZ_DOPING;
+ }
+ channel->hctsiz = hctsiz;
+ edpt->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) {
+ edpt->next_pid = HCTSIZ_PID_DATA1; // control data and status stage always start with DATA1
+ } else {
+ edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count);
+ }
+
+ channel->hcsplt = edpt->hcsplt;
+ channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts
+
+ if (dma_host_enabled(dwc2)) {
+ uint32_t hcintmsk = HCINT_HALTED;
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ channel->hcdma = (uint32_t) edpt->buffer;
+
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ hcd_dcache_clean(edpt->buffer, edpt->buflen);
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else {
+ uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR;
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
+ } else {
+ hcintmsk |= HCINT_NYET;
+ if (edpt->hcsplt_bm.split_en) {
+ hcintmsk |= HCINT_ACK;
+ }
+ }
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ // enable channel for slave mode:
+ // - OUT: it will enable corresponding FIFO channel
+ // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send
+ // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only
+ // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically.
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ channel->hcchar |= HCCHAR_CHENA;
+ if (edpt->buflen > 0) {
+ // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly
+ // And write packet in the interrupt handler
+ dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY);
+ }
+ }
+ }
+
+ return true;
+}
+
+// kick-off transfer with an endpoint
+static bool edpt_xfer_kickoff(dwc2_regs_t* dwc2, uint8_t ep_id) {
+ uint8_t ch_id = channel_alloc(dwc2);
+ TU_ASSERT(ch_id < 16); // all channel are in used
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->ep_id = ep_id;
+ xfer->result = XFER_RESULT_INVALID;
+
+ return channel_xfer_start(dwc2, ch_id);
+}
+
+// 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) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+
+ uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->buffer = buffer;
+ edpt->buflen = buflen;
+
+ if (ep_num == 0) {
+ // update ep_dir since control endpoint can switch direction
+ edpt->hcchar_bm.ep_dir = ep_dir;
+ }
+
+ return edpt_xfer_kickoff(dwc2, ep_id);
+}
+
+// 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) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+
+ // hcd_int_disable(rhport);
+
+ // Find enabled channeled and disable it, channel will be de-allocated in the interrupt handler
+ const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir);
+ if (ch_id < 16) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ channel_disable(dwc2, channel);
+ }
+
+ // hcd_int_enable(rhport);
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) {
+ uint8_t ep_id = edpt_find_opened(dev_addr, 0, TUSB_DIR_OUT);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened endpoint
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ edpt->next_pid = HCTSIZ_PID_SETUP;
+
+ return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8);
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->next_pid = HCTSIZ_PID_DATA0;
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+// HCD Event Handler
+//--------------------------------------------------------------------
+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];
+
+ if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
+ // 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)) {
+ 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
+ channel_send_in_token(dwc2, channel);
+ return;
+ } else {
+ // too many NYET, de-allocate channel with below code
+ xfer->period_split_nyet_count = 0;
+ }
+ }
+
+ // 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) {
+ // 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
+ channel_send_in_token(dwc2, channel);
+ }
+}
+
+#if CFG_TUSB_DEBUG
+TU_ATTR_ALWAYS_INLINE static inline void print_hcint(uint32_t hcint) {
+ const char* str[] = {
+ "XFRC", "HALTED", "AHBERR", "STALL",
+ "NAK", "ACK", "NYET", "XERR",
+ "BBLERR", "FRMOR", "DTERR", "BNA",
+ "XCSERR", "DESC_LST"
+ };
+
+ for(uint32_t i=0; i<14; i++) {
+ if (hcint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
+ }
+ TU_LOG1("\r\n");
+}
+#endif
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+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;
+
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_RX_DATA: {
+ // In packet received, pop this entry --> ACK interrupt
+ const uint16_t byte_count = grxstsp_bm.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];
+
+ if (byte_count) {
+ dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count);
+ xfer->xferred_bytes += byte_count;
+ xfer->fifo_bytes = byte_count;
+ }
+ break;
+ }
+
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // In transfer complete: After this entry is popped from the rx FIFO, dwc2 asserts a Transfer Completed
+ // interrupt --> handle_channel_irq()
+ break;
+
+ case GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR:
+ TU_ASSERT(0, ); // maybe try to change DToggle
+ break;
+
+ case GRXSTS_PKTSTS_HOST_CHANNEL_HALTED:
+ // triggered when channel.hcchar_bm.disable is set
+ // TODO handle later
+ break;
+
+ default: break; // ignore other status
+ }
+}
+
+// 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 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];
+ // skip writing to FIFO if channel is expecting halted.
+ if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.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;
+ 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);
+
+ // 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)) {
+ return true;
+ }
+
+ dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->fifo_bytes, xact_bytes);
+ xfer->fifo_bytes += xact_bytes;
+ }
+ }
+ }
+
+ return false; // no channel has pending data
+}
+
+static bool handle_channel_in_slave(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];
+ bool is_done = false;
+
+ // if (channel->hcsplt_bm.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);
+ // 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)
+ }
+
+ 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) {
+ // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
+ channel->hcsplt_bm.split_compl = 0;
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) {
+ if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ } else if (hcint & HCINT_BABBLE_ERR) {
+ xfer->result = XFER_RESULT_FAILED;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NYET) {
+ // restart complete split
+ channel->hcsplt_bm.split_compl = 1;
+ 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
+ }
+
+ 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) {
+ // start split is ACK --> do complete split
+ channel->hcintmsk |= HCINT_NYET;
+ channel->hcsplt_bm.split_compl = 1;
+ 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;
+ if (remain_packets) {
+ // still more packet to receive, also reset to start split
+ channel->hcsplt_bm.split_compl = 0;
+ 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) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // got here due to NAK or NYET
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_DATATOGGLE_ERR) {
+ xfer->err_count = 0;
+ TU_ASSERT(false);
+ }
+ return is_done;
+}
+
+static bool handle_channel_out_slave(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];
+ bool is_done = false;
+
+ if (hcint & HCINT_XFER_COMPLETE) {
+ is_done = true;
+ xfer->result = XFER_RESULT_SUCCESS;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } 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) {
+ // retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ } else {
+ edpt->do_ping = 1;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR)) {
+ // clean up transfer so far, disable and start again later
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ } else {
+ // NAK disable channel to flush all posted request and try again
+ edpt->do_ping = 1;
+ xfer->err_count = 0;
+ }
+ } else if (hcint & HCINT_HALTED) {
+ channel->hcintmsk &= ~HCINT_HALTED;
+ if (xfer->result != XFER_RESULT_INVALID) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // Got here due to NAK or NYET
+ TU_ASSERT(channel_xfer_start(dwc2, ch_id));
+ }
+ } 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;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+
+ if (is_done) {
+ xfer->xferred_bytes += xfer->fifo_bytes;
+ xfer->fifo_bytes = 0;
+ }
+
+ return is_done;
+}
+#endif
+
+#if CFG_TUH_DWC2_DMA_ENABLE
+static bool handle_channel_in_dma(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];
+
+ bool is_done = false;
+
+ // TU_LOG1("in hcint = %02lX\r\n", hcint);
+
+ 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 actual_len = edpt->buflen - remain_bytes;
+ xfer->xferred_bytes += actual_len;
+
+ is_done = true;
+
+ if (hcint & HCINT_STALL) {
+ 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) {
+ // 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;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ is_done = true;
+ xfer->result = XFER_RESULT_FAILED;
+ } else {
+ channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
+ channel->hcsplt_bm.split_compl = 0;
+ 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) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ 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) {
+ // 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
+ }
+ 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
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ // retry start-split in next binterval
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ }
+
+ return is_done;
+}
+
+static bool handle_channel_out_dma(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];
+
+ bool is_done = false;
+
+ // TU_LOG1("out hcint = %02lX\r\n", hcint);
+
+ if (hcint & HCINT_HALTED) {
+ if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) {
+ is_done = true;
+ xfer->err_count = 0;
+ if (hcint & HCINT_XFER_COMPLETE) {
+ xfer->result = XFER_RESULT_SUCCESS;
+ xfer->xferred_bytes += edpt->buflen;
+ } else {
+ xfer->result = XFER_RESULT_STALLED;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ }
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) {
+ xfer->err_count = 0;
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ } else {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ }
+ }
+ } else if (hcint & HCINT_NYET) {
+ if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ }
+
+ return is_done;
+}
+#endif
+
+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);
+
+ 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;
+
+ const uint32_t hcint = channel->hcint;
+ channel->hcint = hcint; // clear interrupt
+
+ bool is_done = false;
+ if (is_dma) {
+ #if CFG_TUH_DWC2_DMA_ENABLE
+ if (hcchar_bm.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);
+ if (is_done && (channel->hcdma > xfer->xferred_bytes)) {
+ // hcdma is increased by word --> need to align4
+ hcd_dcache_invalidate((void*) tu_align4(channel->hcdma - xfer->xferred_bytes), xfer->xferred_bytes);
+ }
+ }
+ #endif
+ } else {
+ #if CFG_TUH_DWC2_SLAVE_ENABLE
+ if (hcchar_bm.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);
+ }
+ #endif
+ }
+
+ 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);
+ channel_dealloc(dwc2, ch_id);
+ }
+ }
+ }
+}
+
+// SOF is enabled for scheduled periodic transfer
+static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
+ (void) in_isr;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ bool more_isr = false;
+
+ // If highspeed then SOF is 125us, else 1ms
+ const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
+
+ 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) {
+ edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
+ if (edpt->uframe_countdown == 0) {
+ if (!edpt_xfer_kickoff(dwc2, ep_id)) {
+ edpt->uframe_countdown = ucount; // failed to start, try again next frame
+ }
+ }
+
+ more_isr = true;
+ }
+ }
+
+ return more_isr;
+}
+
+// Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
+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;
+ uint32_t phy_clock;
+
+ if (gusbcfg_bm.phy_sel) {
+ phy_clock = 48; // dedicated FS is 48Mhz
+ if (speed == TUSB_SPEED_LOW) {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
+ } else {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
+ }
+ } else {
+ if (gusbcfg_bm.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;
+
+ // Enable UTMI+ low power mode 48Mhz external clock if not highspeed
+ if (speed == TUSB_SPEED_HIGH) {
+ dwc2->gusbcfg &= ~GUSBCFG_PHYLPCS;
+ } else {
+ dwc2->gusbcfg |= GUSBCFG_PHYLPCS;
+ // may need to reset port
+ }
+ }
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ;
+ }
+
+ dwc2->hcfg = hcfg;
+
+ uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk;
+ if (speed == TUSB_SPEED_HIGH) {
+ hfir |= 125*phy_clock;
+ } else {
+ hfir |= 1000*phy_clock;
+ }
+
+ dwc2->hfir = hfir;
+}
+
+/* Handle Host Port interrupt, possible source are:
+ - Connection Detection
+ - Enable Change
+ - Over Current Change
+*/
+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;
+
+ if (dwc2->hprt & HPRT_CONN_DETECT) {
+ // 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) {
+ // Port enable change
+ hprt |= HPRT_ENABLE_CHANGE;
+
+ if (hprt_bm.enable) {
+ // Port enable
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ port0_enable(dwc2, speed);
+ } else {
+ // TU_ASSERT(false, );
+ }
+ }
+
+ dwc2->hprt = hprt; // clear interrupt
+}
+
+/* Interrupt Hierarchy
+ HCINTn HPRT
+ | |
+ HAINT.CHn |
+ | |
+ GINTSTS : HCInt | PrtInt | NPTxFEmp | PTxFEmpp | RXFLVL | SOF
+*/
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint32_t gintmsk = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmsk;
+
+ // TU_LOG1_HEX(gintsts);
+
+ if (gintsts & GINTSTS_CONIDSTSCHNG) {
+ // Connector ID status change
+ dwc2->gintsts = GINTSTS_CONIDSTSCHNG;
+
+ //if (dwc2->gotgctl)
+ // dwc2->hprt = HPRT_POWER; // power on port to turn on VBUS
+ //dwc2->gintmsk |= GINTMSK_PRTIM;
+ // TODO wait for SRP if OTG
+ }
+
+ if (gintsts & GINTSTS_SOF) {
+ const bool more_sof = handle_sof_irq(rhport, in_isr);
+ if (!more_sof) {
+ dwc2->gintmsk &= ~GINTSTS_SOF;
+ }
+ }
+
+ if (gintsts & GINTSTS_HPRTINT) {
+ // Host port interrupt: source is cleared in HPRT register
+ // TU_LOG1_HEX(dwc2->hprt);
+ handle_hprt_irq(rhport, in_isr);
+ }
+
+ if (gintsts & GINTSTS_HCINT) {
+ // Host Channel interrupt: source is cleared in HCINT register
+ // must be handled after TX FIFO empty
+ handle_channel_irq(rhport, in_isr);
+ }
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+ // RxFIFO non-empty interrupt handling
+ if (gintsts & GINTSTS_RXFLVL) {
+ // RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading
+
+ do {
+ handle_rxflvl_irq(rhport); // read all packets
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
+
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ if (gintsts & GINTSTS_NPTX_FIFO_EMPTY) {
+ // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_nptxfe = handle_txfifo_empty(dwc2, false);
+ if (!more_nptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY;
+ }
+ }
+
+ if (gintsts & GINTSTS_PTX_FIFO_EMPTY) {
+ // PTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_ptxfe = handle_txfifo_empty(dwc2, true);
+ if (!more_ptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_PTX_FIFO_EMPTY;
+ }
+ }
+#endif
+}
+
+#endif
diff --git a/src/portable/template/dcd_template.c b/src/portable/template/dcd_template.c
index 25ee507c1..3738ac0cb 100644
--- a/src/portable/template/dcd_template.c
+++ b/src/portable/template/dcd_template.c
@@ -40,54 +40,45 @@
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
- (void) rhport;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport; (void) rh_init;
+ return true;
}
// Enable device interrupt
-void dcd_int_enable (uint8_t rhport)
-{
+void dcd_int_enable (uint8_t rhport) {
(void) rhport;
}
// Disable device interrupt
-void dcd_int_disable (uint8_t rhport)
-{
+void dcd_int_disable (uint8_t rhport) {
(void) rhport;
}
// Receive Set Address request, mcu port must also include status IN response
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr) {
(void) rhport;
(void) dev_addr;
}
// Wake up host
-void dcd_remote_wakeup (uint8_t rhport)
-{
+void dcd_remote_wakeup (uint8_t rhport) {
(void) rhport;
}
// Connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
(void) rhport;
}
// Disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
(void) rhport;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
(void) en;
-
- // TODO implement later
}
//--------------------------------------------------------------------+
@@ -95,8 +86,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Configure endpoint's registers according to descriptor
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
(void) rhport;
(void) ep_desc;
return false;
@@ -118,14 +108,12 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
return false;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
+void dcd_edpt_close_all (uint8_t rhport) {
(void) rhport;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) {
(void) rhport;
(void) ep_addr;
(void) buffer;
@@ -134,8 +122,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) {
(void) rhport;
(void) ep_addr;
(void) ff;
@@ -144,19 +131,15 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
}
// Stall endpoint
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
(void) ep_addr;
}
// clear stall, data toggle is also reset to DATA0
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
(void) ep_addr;
}
-
-
#endif
diff --git a/src/portable/template/hcd_template.c b/src/portable/template/hcd_template.c
index b073d6057..d8bca196f 100644
--- a/src/portable/template/hcd_template.c
+++ b/src/portable/template/hcd_template.c
@@ -44,9 +44,9 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
}
// Initialize controller to host mode
-bool hcd_init(uint8_t rhport) {
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
-
+ (void) rh_init;
return false;
}
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index 6b60bc657..3752ae251 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -130,9 +130,8 @@ static void enable_functional_reset(const bool enable)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
- (void) rhport;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport; (void) rh_init;
USBKEYPID = USBKEY;
@@ -164,6 +163,8 @@ void dcd_init (uint8_t rhport)
}
USBKEYPID = 0;
+
+ return true;
}
// There is no "USB peripheral interrupt disable" bit on MSP430, so we have
@@ -699,7 +700,11 @@ static void handle_bus_power_event(void *param) {
// A successful lock is indicated by all PLL-related interrupt flags being cleared.
if(!USBPLLIR) {
- dcd_init(0); // Re-initialize the USB module.
+ const tusb_rhport_init_t rhport_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_FULL
+ };
+ dcd_init(0, &rhport_init); // Re-initialize the USB module.
}
} else { // Event caused by removal of bus power.
USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c
index 8f40e3349..a03c94558 100644
--- a/src/portable/valentyusb/eptri/dcd_eptri.c
+++ b/src/portable/valentyusb/eptri/dcd_eptri.c
@@ -336,9 +336,9 @@ static void dcd_reset(void)
}
// Initializes the USB peripheral for device mode and enables it.
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
usb_pullup_out_write(0);
@@ -352,6 +352,8 @@ void dcd_init(uint8_t rhport)
// Turn on the external pullup
usb_pullup_out_write(1);
+
+ return true;
}
// Enables or disables the USB device interrupt(s). May be used to
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
index 9ed370b40..4e8e25a00 100644
--- a/src/portable/wch/dcd_ch32_usbfs.c
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -123,7 +123,8 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
}
/* public functions */
-void dcd_init(uint8_t rhport) {
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
// init registers
USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN;
USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN;
@@ -153,6 +154,8 @@ void dcd_init(uint8_t rhport) {
EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
dcd_connect(rhport);
+
+ return true;
}
void dcd_int_handler(uint8_t rhport) {
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index 622f9c508..f8bf3c889 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -131,8 +131,9 @@ static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer
ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK);
}
-void dcd_init(uint8_t rhport) {
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
memset(&xfer_status, 0, sizeof(xfer_status));
@@ -170,6 +171,8 @@ void dcd_init(uint8_t rhport) {
USBHSD->DEV_AD = 0;
USBHSD->CONTROL |= USBHS_DEV_PU_EN;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport) {