summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-10-17 15:56:12 +0700
committerhathach <[email protected]>2024-10-17 15:56:12 +0700
commitb5a4f18879110d9f5f8fe27c473818f2416e40e4 (patch)
tree48d8843c14e9803f98ffb9952fdeba420fff49f9
parent8d9d3d9a2ac51378e7cb973628972e93ba356e1d (diff)
get hpri triggered
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c76
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h16
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h27
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c156
-rw-r--r--src/tusb.c2
6 files changed, 217 insertions, 64 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 6760af3f3..55d8f5766 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -486,8 +486,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Enable required interrupts
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;
+ // Enable global interrupt
+ dwc2->gahbcfg |= GAHBCFG_GINT;
dcd_connect(rhport);
return true;
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index 50d0c28b3..12dcdb7c6 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -44,18 +44,21 @@ 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)) {}
+ 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;
+ }
- // wait for device mode ?
+ while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
}
bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) {
- (void) dwc2;
+ (void)dwc2;
#if CFG_TUD_ENABLED
if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
@@ -71,11 +74,14 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init
return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
}
-static void phy_fs_init(dwc2_regs_t* dwc2) {
+static void phy_fs_init(dwc2_regs_t* dwc2, tusb_role_t role) {
TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n");
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
// Select FS PHY
- dwc2->gusbcfg |= GUSBCFG_PHYSEL;
+ gusbcfg |= GUSBCFG_PHYSEL;
+ dwc2->gusbcfg = gusbcfg;
// MCU specific PHY init before reset
dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
@@ -86,13 +92,33 @@ static void phy_fs_init(dwc2_regs_t* 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);
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= 5u << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // FS/LS PHY Clock Select
+ if (role == TUSB_ROLE_HOST) {
+ uint32_t hcfg = dwc2->hcfg;
+ hcfg |= HCFG_FSLS_ONLY;
+ hcfg &= ~HCFG_FSLS_PHYCLK_SEL;
+
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI &&
+ dwc2->ghwcfg2_bm.fs_phy_type == GHWCFG2_FSPHY_DEDICATED) {
+ // dedicated FS PHY with 48 mhz
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
+ } else {
+ // shared HS PHY running at full speed
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ;
+ }
+ dwc2->hcfg = hcfg;
+ }
// MCU specific PHY update post reset
dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
}
-static void phy_hs_init(dwc2_regs_t* dwc2) {
+static void phy_hs_init(dwc2_regs_t* dwc2, tusb_role_t role) {
+ (void) role;
uint32_t gusbcfg = dwc2->gusbcfg;
// De-select FS PHY
@@ -123,8 +149,8 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// Set 16-bit interface if supported
if (dwc2->ghwcfg4_bm.phy_data_width) {
- gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
- }else {
+ gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ } else {
gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
}
}
@@ -138,6 +164,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// Reset core after selecting PHY
reset_core(dwc2);
+ if (role == TUSB_ROLE_HOST) {
+ dwc2->hcfg &= ~HCFG_FSLS_ONLY;
+ }
+
// 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
@@ -162,7 +192,7 @@ static bool check_dwc2(dwc2_regs_t* dwc2) {
#endif
// For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it)
- (void) dwc2;
+ (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);
@@ -175,16 +205,16 @@ static bool check_dwc2(dwc2_regs_t* dwc2) {
//
//--------------------------------------------------------------------
bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
+ (void)rh_init;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// Check Synopsys ID register, failed if controller clock/power is not enabled
TU_ASSERT(check_dwc2(dwc2));
if (dwc2_core_is_highspeed(dwc2, rh_init)) {
- phy_hs_init(dwc2); // Highspeed
+ phy_hs_init(dwc2, rh_init->role);
} else {
- phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present
+ phy_fs_init(dwc2, rh_init->role);
}
/* Set HS/FS Timeout Calibration to 7 (max available value).
@@ -211,20 +241,20 @@ bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
int_mask = dwc2->gotgint;
dwc2->gotgint |= int_mask;
- dwc2->gintmsk = GINTMSK_OTGINT;
+ dwc2->gintmsk = 0;
- if (dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) {
+ if (dwc2_dma_enabled(dwc2, rh_init->role)) {
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, and not possible to switch on-the-fly
dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
- }else {
+ } else {
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
- // Enable global interrupt
- dwc2->gahbcfg |= GAHBCFG_GINT;
+ // Configure TX FIFO empty level for interrupt. Default is complete empty
+ dwc2->gahbcfg |= GAHBCFG_TXFELVL;
return true;
}
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index 395b6d870..8719c4820 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -124,13 +124,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 c8f6a5d1f..f99bf3e85 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -566,14 +566,15 @@ 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_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)
@@ -828,8 +829,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
@@ -1475,9 +1476,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#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_DETECTEDT_Pos (1U)
-#define HPRT_CONN_DETECTEDT_Msk (0x1UL << HPRT_CONN_DETECTEDT_Pos) // 0x00000002
-#define HPRT_CONN_DETECTEDT HPRT_CONN_DETECTEDT_Msk // Port connect detected
+#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
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index b383b4069..97068d4c5 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -34,6 +34,10 @@
#include "host/hcd.h"
#include "dwc2_common.h"
+enum {
+ HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_EN_CHANGE | HPRT_OVER_CURRENT_CHANGE
+};
+
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
@@ -54,40 +58,41 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Core Initialization
TU_ASSERT(dwc2_core_init(rhport, rh_init));
+ //------------- 3.1 Host Initialization -------------//
+
+ // max speed
+ // if (dwc2_core_is_highspeed(dwc2, rh_init)) {
+ // dwc2->hcfg &= ~HCFG_FSLS_ONLY;
+ // } else {
+ // dwc2->hcfg |= HCFG_FSLS_ONLY;
+ // }
+
// force host mode
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
- //------------- 3.1 Host Initialization -------------//
+ dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits
+ dwc2->hprt = HPRT_POWER; // port power on -> drive VBUS
+
// Enable required interrupts
dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_PRTIM | GINTMSK_WUIM;
-
- // max speed
- if (dwc2_core_is_highspeed(dwc2, rh_init)) {
- dwc2->hcfg &= ~HCFG_FSLSS;
- } else {
- dwc2->hcfg |= HCFG_FSLSS;
- }
-
- // port power on -> drive VBUS
- dwc2->hprt = HPRT_POWER;
+ dwc2->gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
return true;
}
// Enable USB interrupt
void hcd_int_enable (uint8_t rhport) {
- (void) rhport;
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, true);
}
// Disable USB interrupt
void hcd_int_disable(uint8_t rhport) {
- (void) rhport;
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, false);
}
// Get frame number (1ms)
uint32_t hcd_frame_number(uint8_t rhport) {
(void) rhport;
-
return 0;
}
@@ -97,20 +102,23 @@ uint32_t hcd_frame_number(uint8_t rhport) {
// Get the current connect status of roothub port
bool hcd_port_connect_status(uint8_t rhport) {
- (void) rhport;
-
- return false;
+ 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) {
- (void) rhport;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2->hprt = HPRT_RESET;
}
// Complete bus reset sequence, may be required by some controllers
void hcd_port_reset_end(uint8_t rhport) {
- (void) rhport;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; // skip w1c bits
+ hprt &= ~HPRT_RESET;
+ dwc2->hprt = hprt;
}
// Get port link speed
@@ -182,6 +190,101 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
// HCD Event Handler
//--------------------------------------------------------------------
+#if 1
+static void handle_rxflvl_irq(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ volatile uint32_t const* rx_fifo = dwc2->fifo[0];
+
+ // Pop control word off FIFO
+ uint32_t const grxstsp = dwc2->grxstsp;
+ uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos;
+ uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos;
+ uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos;
+ // dwc2_epout_t* epout = &dwc2->epout[epnum];
+
+ (void) epnum; (void) bcnt; (void) rx_fifo;
+
+ TU_LOG1_INT(pktsts);
+
+ // switch (pktsts) {
+ // // Global OUT NAK: do nothing
+ // case GRXSTS_PKTSTS_GLOBALOUTNAK:
+ // break;
+ //
+ // case GRXSTS_PKTSTS_SETUPRX:
+ // // 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);
+ // break;
+ //
+ // case GRXSTS_PKTSTS_SETUPDONE:
+ // // 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: {
+ // // Out packet received
+ // 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;
+ // }
+ //
+ // // 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;
+ // }
+ // }
+ // break;
+ // }
+ //
+ // case GRXSTS_PKTSTS_OUTDONE:
+ // /* 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:
+ // TU_BREAKPOINT();
+ // break;
+ // }
+}
+#endif
+
+/* Handle Host Port interrupt, possible source are:
+ - Connection Detection
+ - Enable Change
+ - Over Current Change
+*/
+TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt;
+
+ if (hprt & HPRT_CONN_DETECT) {
+ // Port Connect Detect
+ dwc2->hprt = HPRT_CONN_DETECT; // clear
+
+ if (hprt & HPRT_CONN_STATUS) {
+ hcd_event_device_attach(rhport, in_isr);
+ } else {
+ hcd_event_device_remove(rhport, in_isr);
+ }
+ }
+}
+
/* Interrupt Hierarchy
HCINTn.XferCompl HCINTMSKn.XferComplMsk
@@ -201,7 +304,6 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
IRQn
*/
void hcd_int_handler(uint8_t rhport, bool in_isr) {
- (void) in_isr;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint32_t int_mask = dwc2->gintmsk;
@@ -209,7 +311,21 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
if (int_status & GINTSTS_HPRTINT) {
TU_LOG1_HEX(dwc2->hprt);
+ handle_hprt_irq(rhport, in_isr);
}
+
+ // RxFIFO non-empty interrupt handling.
+ if (int_status & GINTSTS_RXFLVL) {
+ // RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
+
+ do {
+ handle_rxflvl_irq(rhport); // read all packets
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
+
+ dwc2->gintmsk |= GINTMSK_RXFLVLM;
+ }
+
}
#endif
diff --git a/src/tusb.c b/src/tusb.c
index e6f8055b7..6a0cc0805 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -77,6 +77,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// new API with explicit rhport and role
TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID);
+ _rhport_role[rhport] = rh_init->role;
#if CFG_TUD_ENABLED
if (rh_init->role == TUSB_ROLE_DEVICE) {
@@ -90,7 +91,6 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
}
#endif
- _rhport_role[rhport] = rh_init->role;
return true;
}