summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-11-01 17:54:10 +0700
committerhathach <[email protected]>2024-11-01 17:54:10 +0700
commitb7ff10f59c48024e65341a494c51c471d4e8337e (patch)
treee39f5ac021dbd6e3f80327cfde1f691880a78194 /src/portable
parent4c8ce9733aafa96c8810be3b291ff1afea6888b7 (diff)
rename and add both CFG_TUH_DWC2_SLAVE_ENABLE/CFG_TUH_DWC2_DMA_ENABLE better out dma handle
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c10
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c9
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h2
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c232
4 files changed, 133 insertions, 120 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 9b5c476e4..4cf725bfe 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -412,8 +412,14 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// 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));
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed));
+
+ if (dma_device_enabled(dwc2)) {
+ // 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;
+ }
// Device Initialization
dcd_disconnect(rhport);
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index 66dc59b11..a3c7e80cb 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -194,7 +194,7 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
* In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
*
*/
-bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// Check Synopsys ID register, failed if controller clock/power is not enabled
@@ -235,13 +235,6 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
dwc2->gintmsk = 0;
- 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 |= GINTMSK_RXFLVLM;
- }
-
return true;
}
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index 18b93894f..3f7f23c3a 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -73,7 +73,7 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
}
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);
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed);
void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
//--------------------------------------------------------------------+
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index c35252a8d..02f6fa54a 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -83,7 +83,7 @@ typedef struct {
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_start_xfer() (retry with NAK/NYET)
+ // be composed of multiple channel_xfer_start() (retry with NAK/NYET)
uint8_t* buf_start;
uint16_t buf_len;
uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus).
@@ -116,7 +116,7 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hpr
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 && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+ return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
// Allocate a channel for new transfer
@@ -321,8 +321,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// 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));
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed));
+
+ if (dma_host_enabled(dwc2)) {
+ // 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;
+ }
//------------- 3.1 Host Initialization -------------//
@@ -471,7 +477,29 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
return true;
}
-bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) {
+// clean up channel after part of transfer is done but the whole urb is not complete
+static void channel_xfer_cleanup(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];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX, );
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+
+ /* 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. (Can't use the hctsiz.xfersize field because that reflects the number of
+ * bytes transferred via the AHB, not the USB). */
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size);
+ const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
+ xfer->xferred_bytes += actual_bytes;
+ xfer->buf_start += actual_bytes;
+ xfer->buf_len -= actual_bytes;
+ xfer->out_fifo_bytes = 0;
+}
+
+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;
@@ -573,7 +601,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
edpt->hcchar_bm.ep_dir = ep_dir;
}
- return channel_start_xfer(dwc2, ch_id);
+ return channel_xfer_start(dwc2, ch_id);
}
// Abort a queued transfer. Note: it can only abort transfer that has not been started
@@ -627,7 +655,7 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
//--------------------------------------------------------------------
// HCD Event Handler
//--------------------------------------------------------------------
-
+#if CFG_TUH_DWC2_SLAVE_ENABLE
static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
@@ -683,72 +711,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
}
}
-/* 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 & ~HPRT_W1C_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
- // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
- const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed);
- uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
-
- const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
- uint32_t clock = 60;
- if (gusbcfg_bm.phy_sel) {
- // dedicated FS is 48Mhz
- clock = 48;
- hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
- } else {
- // UTMI+ or ULPI
- if (gusbcfg_bm.ulpi_utmi_sel) {
- clock = 60; // ULPI 8-bit is 60Mhz
- } else if (gusbcfg_bm.phy_if16) {
- clock = 30; // UTMI+ 16-bit is 30Mhz
- } else {
- clock = 60; // UTMI+ 8-bit is 60Mhz
- }
- 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*clock;
- } else {
- hfir |= 1000*clock;
- }
-
- dwc2->hfir = hfir;
- }
- }
-
- dwc2->hprt = hprt; // clear interrupt
-}
-
-bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
+static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
bool is_done = false;
@@ -786,7 +749,7 @@ bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, u
return is_done;
}
-bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
+static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
(void) is_period;
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
@@ -800,24 +763,8 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period,
xfer->result = XFER_RESULT_STALLED;
channel_disable(dwc2, channel, is_period);
} else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) {
- TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
- hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
- edpt->next_pid = channel->hctsiz_bm.pid; // save PID
-
- /* Rewind buffer pointer and total bytes to retry later
- * 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. (Can't use the hctsiz.xfersize field because that reflects the number of
- * bytes transferred via the AHB, not the USB).
- */
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
- const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size);
- const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
- xfer->xferred_bytes += actual_bytes;
- xfer->buf_start += actual_bytes;
- xfer->buf_len -= actual_bytes;
- xfer->out_fifo_bytes = 0;
-
+ // clean up transfer so far, disable and start again later
+ channel_xfer_cleanup(dwc2, ch_id);
channel_disable(dwc2, channel, is_period);
if (hcint & HCINT_XACT_ERR) {
xfer->err_count++;
@@ -836,7 +783,7 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period,
} else {
// Got here due to NAK or NYET -> Retry transfer with do PING (for highspeed)
xfer->do_ping = 1;
- TU_ASSERT(channel_start_xfer(dwc2, ch_id));
+ TU_ASSERT(channel_xfer_start(dwc2, ch_id));
}
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
@@ -850,8 +797,10 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period,
return is_done;
}
+#endif
-bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
+#if CFG_TUH_DWC2_DMA_ENABLE
+static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
(void) is_period;
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
@@ -890,7 +839,7 @@ bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uin
return is_done;
}
-bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
+static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) {
(void) is_period;
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
@@ -905,27 +854,24 @@ bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, ui
xfer->xferred_bytes += xfer->buf_len;
} else {
xfer->result = XFER_RESULT_STALLED;
- const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
- const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size);
- const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
- xfer->xferred_bytes += actual_bytes;
- xfer->buf_start += actual_bytes;
- xfer->buf_len -= actual_bytes;
+ channel_xfer_cleanup(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;
- // re-init channel, re-wind buffer pointer
- TU_ASSERT(false);
+ // clean up transfer so far and start again
+ channel_xfer_cleanup(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 {
- // re-init channel, re-wind buffer pointer
- TU_ASSERT(false);
+ // clean up transfer so far and start again
+ channel_xfer_cleanup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
}
}
}
@@ -936,8 +882,9 @@ bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, ui
return is_done;
}
+#endif
-void handle_channel_irq(uint8_t rhport, bool in_isr) {
+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);
@@ -978,7 +925,7 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) {
}
// return true if there is still pending data and need more ISR
-bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
+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);
@@ -1008,6 +955,71 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
return false; // all data written
}
+/* 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_W1C_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
+ // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
+ const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed);
+ uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
+
+ const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
+ uint32_t clock = 60;
+ if (gusbcfg_bm.phy_sel) {
+ // dedicated FS is 48Mhz
+ clock = 48;
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
+ } else {
+ // UTMI+ or ULPI
+ if (gusbcfg_bm.ulpi_utmi_sel) {
+ clock = 60; // ULPI 8-bit is 60Mhz
+ } else if (gusbcfg_bm.phy_if16) {
+ clock = 30; // UTMI+ 16-bit is 30Mhz
+ } else {
+ clock = 60; // UTMI+ 8-bit is 60Mhz
+ }
+ 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*clock;
+ } else {
+ hfir |= 1000*clock;
+ }
+
+ dwc2->hfir = hfir;
+ }
+ }
+
+ dwc2->hprt = hprt; // clear interrupt
+}
+
/* Interrupt Hierarchy
HCINTn HPRT
| |
@@ -1040,17 +1052,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
handle_hprt_irq(rhport, in_isr);
}
+#if CFG_TUH_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling, must be handled before HCINT
if (int_status & GINTSTS_RXFLVL) {
// RXFLVL bit is read-only
- dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
+ dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading
do {
handle_rxflvl_irq(rhport); // read all packets
} while(dwc2->gintsts & GINTSTS_RXFLVL);
- dwc2->gintmsk |= GINTMSK_RXFLVLM;
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
}
+#endif
if (int_status & GINTSTS_NPTX_FIFO_EMPTY) {
// NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written