summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-10-25 19:00:45 +0700
committerhathach <[email protected]>2024-10-25 19:00:45 +0700
commit07abc722b69ea95c6234c3dfc9a1473b43150915 (patch)
tree89c3234ed2b77f0de064c782c99f86ba7a91d425 /src
parent063661e3a32429e2d5f74c097da72aa3679cc889 (diff)
hcd able to send setup packet
Diffstat (limited to 'src')
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c99
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c63
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h110
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c163
5 files changed, 287 insertions, 152 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 0731a88c1..b389b0f74 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -94,7 +94,7 @@ static void dma_setup_prepare(uint8_t rhport) {
//--------------------------------------------------------------------+
-/* 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
@@ -109,7 +109,7 @@ static void dma_setup_prepare(uint8_t rhport) {
possible since the free space is located between the RX and TX FIFOs.
---------------- ep_fifo_size
- | EPInfo DMA |
+ | DxEPIDMAn |
|-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
| IN FIFO 0 | control EP
|-------------|
@@ -167,7 +167,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
}
// 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;
}
@@ -205,59 +205,10 @@ static void dfifo_device_init(uint8_t rhport) {
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) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
@@ -404,7 +355,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
+ ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
if(dma_device_enabled(dwc2)) {
dep->diepdma = (uintptr_t)xfer->buffer;
@@ -648,11 +599,10 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// 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++;
+ uint16_t num_packets = tu_div_ceil(total_bytes, xfer->max_size);
+ if (num_packets == 0) {
+ num_packets = 1; // zero length packet still count as 1
+ }
// Schedule packets to be sent within interrupt
edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
@@ -758,7 +708,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
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);
+ dfifo_read_packet(dwc2, xfer->buffer, bcnt);
// Increment pointer to xfer data
xfer->buffer += bcnt;
@@ -863,8 +813,7 @@ static void handle_epout_irq(uint8_t rhport) {
static void handle_epin_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- dwc2_epin_t* epin = dwc2->epin;
+ const uint8_t ep_count = _dwc2_controller[rhport].ep_count;
// DAINT for a given EP clears when DIEPINTx is cleared.
// IEPINT will be cleared when DAINT's out bits are cleared.
@@ -872,9 +821,10 @@ static void handle_epin_irq(uint8_t rhport) {
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);
+ dwc2_epin_t* epin = &dwc2->epin[n];
- if (epin[n].diepint & DIEPINT_XFRC) {
- epin[n].diepint = DIEPINT_XFRC;
+ if (epin->diepint & DIEPINT_XFRC) {
+ epin->diepint = DIEPINT_XFRC;
// EP0 can only handle one packet
if ((n == 0) && ep0_pending[TUSB_DIR_IN]) {
@@ -889,39 +839,38 @@ static void handle_epin_irq(uint8_t rhport) {
}
// XFER FIFO empty
- if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) {
+ if ((epin->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)
-
- uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos;
+ // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
+ const uint16_t remain_packets = epin->dieptsiz_bm.packet_count;
// 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;
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = epin->dieptsiz_bm.xfer_size;
// Packet can not be larger than ep max size
- uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
+ const uint16_t packet_size = tu_min16(remain_bytes, xfer->max_size);
// 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;
+ if (packet_size > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
+ }
// 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);
-
- // Increment pointer to xfer data
+ dfifo_write_packet(dwc2, n, xfer->buffer, packet_size);
xfer->buffer += packet_size;
}
}
// Turn off TXFE if all bytes are written.
- if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) {
+ if (epin->dieptsiz_bm.xfer_size == 0) {
dwc2->diepempmsk &= ~(1 << n);
}
}
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index c5990c53f..1527932a5 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -158,7 +158,8 @@ static bool check_dwc2(dwc2_regs_t* dwc2) {
// For some reason: GD32VF103 gsnpsid 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;
+ 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
@@ -200,7 +201,7 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
TU_ASSERT(check_dwc2(dwc2));
// disable global interrupt
- // dwc2->gahbcfg &= ~GAHBCFG_GINT;
+ dwc2->gahbcfg &= ~GAHBCFG_GINT;
if (is_highspeed) {
phy_hs_init(dwc2);
@@ -241,8 +242,8 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
- // Configure TX FIFO empty level for interrupt. Default is complete empty
- dwc2->gahbcfg |= GAHBCFG_TXFELVL;
+ // (non-periodic) TX FIFO empty level for interrupt is complete empty
+ dwc2->gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL;
return true;
}
@@ -260,4 +261,58 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
//
// }
+//--------------------------------------------------------------------
+// DFIFO
+//--------------------------------------------------------------------
+// Read a single data packet from receive DFIFO
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) {
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+
+ // Reading full available 32 bit words from fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ tu_unaligned_write32(dst, *rx_fifo);
+ dst += 4;
+ }
+
+ // Read the remaining 1-3 bytes from fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem != 0) {
+ const uint32_t tmp = *rx_fifo;
+ dst[0] = tu_u32_byte0(tmp);
+ if (bytes_rem > 1) {
+ dst[1] = tu_u32_byte1(tmp);
+ }
+ if (bytes_rem > 2) {
+ dst[2] = tu_u32_byte2(tmp);
+ }
+ }
+}
+
+// Write a single data packet to DFIFO
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
+
+ // Pushing full available 32 bit words to fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ *tx_fifo = tu_unaligned_read32(src);
+ src += 4;
+ }
+
+ // Write the remaining 1-3 bytes into fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
+ uint32_t tmp_word = src[0];
+ if (bytes_rem > 1) {
+ tmp_word |= (src[1] << 8);
+ }
+ if (bytes_rem > 2) {
+ tmp_word |= (src[2] << 16);
+ }
+
+ *tx_fifo = tmp_word;
+ }
+}
+
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index fff21ca5c..18b93894f 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -84,12 +84,16 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8
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
//--------------------------------------------------------------------+
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index ed335f1a3..36e917d43 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -146,7 +146,7 @@ enum {
};
//--------------------------------------------------------------------
-// Register bitfield definitions
+// Common Register Bitfield
//--------------------------------------------------------------------
typedef struct TU_ATTR_PACKED {
uint32_t ses_req_scs : 1; // 0 Session request success
@@ -324,6 +324,20 @@ 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 queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OUT
+ // 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_token : 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 conn_status : 1; // 0 Port connect status
uint32_t conn_detected : 1; // 1 Port connect detected
@@ -368,6 +382,13 @@ typedef struct TU_ATTR_PACKED {
} 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
+} dwc2_channel_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size");
+
// Host Channel
typedef struct {
union {
@@ -380,35 +401,55 @@ typedef struct {
};
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;
+//--------------------------------------------------------------------
+// Device Register Bitfield
+//--------------------------------------------------------------------
+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");
+
// 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
+ 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
+ union {
+ volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size
+ volatile dwc2_ep_tsize_t dieptsiz_bm;
+ };
+ 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;
// Endpoint OUT
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
+ 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
+ union {
+ volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size
+ volatile dwc2_ep_tsize_t doeptsiz_bm;
+ };
+ volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address
+ uint32_t reserved18[2]; // B18..B1C
} dwc2_epout_t;
+// Universal Endpoint
typedef struct {
union {
volatile uint32_t diepctl;
@@ -424,6 +465,7 @@ typedef struct {
union {
volatile uint32_t dieptsiz;
volatile uint32_t doeptsiz;
+ volatile dwc2_ep_tsize_t deptsiz_bm;
};
union {
volatile uint32_t diepdma;
@@ -460,7 +502,11 @@ typedef struct {
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 {
@@ -791,14 +837,12 @@ 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)
@@ -1009,9 +1053,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
@@ -1060,15 +1104,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_CONIDSTSCHNG_Pos (28U)
-#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000
-#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change
+#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
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index f37bf717d..f388776c5 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -35,14 +35,15 @@
#include "dwc2_common.h"
// DWC2 has limit number of channel, in order to support all endpoints we can store channel char/split to swap later on
-#ifndef CFG_TUH_DWC2_CHANNEL_MAX
-#define CFG_TUH_DWC2_CHANNEL_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB)
+#ifndef CFG_TUH_DWC2_ENDPOINT_MAX
+#define CFG_TUH_DWC2_ENDPOINT_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB)
#endif
enum {
HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE
};
+// Host driver for each opened endpoint
typedef struct {
union {
uint32_t hcchar;
@@ -53,11 +54,14 @@ typedef struct {
dwc2_channel_split_t hcsplt_bm;
};
+ uint8_t* buf;
+ uint16_t total_len;
uint8_t next_data_toggle;
+ bool pending_tx;
} hcd_pipe_t;
typedef struct {
- hcd_pipe_t pipe[CFG_TUH_DWC2_CHANNEL_MAX];
+ hcd_pipe_t pipe[CFG_TUH_DWC2_ENDPOINT_MAX];
} dwc2_hcd_t;
dwc2_hcd_t _hcd_data;
@@ -94,7 +98,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc
possible since the free space is located between the RX and TX FIFOs.
----------------- ep_fifo_size
- | EPInfo DMA |
+ | HCDMAn |
|--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
| Non-Periodic |
| TX FIFO |
@@ -214,8 +218,12 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
dwc2->hprt = HPRT_POWER; // turn on VBUS
// Enable required interrupts
- dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM; // | GINTMSK_WUIM;
- dwc2->gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM ; // | GINTMSK_WUIM;
+
+ // 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;
}
@@ -290,7 +298,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
hcd_devtree_get_info(dev_addr, &devtree_info);
// find a free pipe
- for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) {
+ for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
hcd_pipe_t* pipe = &_hcd_data.pipe[i];
dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm;
dwc2_channel_split_t* hcsplt_bm = &pipe->hcsplt_bm;
@@ -334,25 +342,40 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t find_free_channel(dwc2_regs_t* dwc2)
return TUSB_INDEX_INVALID_8;
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_addr) {
- for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) {
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(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.pipe[i].hcchar_bm;
+ // find enabled pipe: note EP0 is bidirectional
if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr &&
- ep_addr == tu_edpt_addr(hcchar_bm->ep_num, hcchar_bm->ep_dir)) {
+ hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) {
return i;
}
}
return TUSB_INDEX_INVALID_8;
}
+void schedule_out_packet(dwc2_regs_t* dwc2, uint8_t pipe_id, uint8_t ch_id) {
+ // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly.
+ // And write packet in the interrupt handler
+ hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ (void) channel;
+ const uint8_t ep_type = pipe->hcchar_bm.ep_type;
+ const bool is_periodic = ep_type == TUSB_XFER_INTERRUPT || ep_type == TUSB_XFER_ISOCHRONOUS;
+ pipe->pending_tx = true;
+ dwc2->gintmsk |= (is_periodic ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY);
+}
+
// 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);
- uint8_t pipe_id = find_opened_pipe(dev_addr, ep_addr);
- TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ uint8_t pipe_id = find_opened_pipe(dev_addr, ep_num, ep_dir);
+ TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe
hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id];
- const dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm;
+ dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm;
uint8_t ch_id = find_free_channel(dwc2);
TU_ASSERT(ch_id < 16); // all channel are in use
@@ -362,25 +385,44 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
channel->hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL |
HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR;
- const uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size);
+ uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size);
+ if (packet_count == 0) {
+ packet_count = 1; // zero length packet still count as 1
+ }
channel->hctsiz = (pipe->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen;
// Control transfer always start with DATA1 for data and status stage. May has issue with ZLP
- if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || tu_edpt_number(ep_addr) == 0) {
+ if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || ep_num == 0) {
pipe->next_data_toggle = HCTSIZ_PID_DATA1;
} else {
pipe->next_data_toggle = HCTSIZ_PID_DATA0;
}
+ // TODO support split transaction
+ channel->hcsplt = pipe->hcsplt;
+
+ hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction
+ channel->hcchar = pipe->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet
+
if (dma_host_enabled(dwc2)) {
channel->hcdma = (uint32_t) buffer;
} else {
- TU_ASSERT(false); // not yet support
- }
+ if (ep_dir == TUSB_DIR_IN) {
+ TU_ASSERT(false); // not yet support
+ } else {
+ pipe->buf = buffer;
+ pipe->total_len = buflen;
- // TODO support split transaction
- channel->hcsplt = pipe->hcsplt;
- channel->hcchar = pipe->hcchar; // kick-off transfer
+ channel->hcchar |= HCCHAR_CHENA; // enable channel before writing to FIFO
+
+ if (ep_dir == TUSB_DIR_OUT && buflen > 0) {
+ schedule_out_packet(dwc2, pipe_id, ch_id);
+ } else {
+ TU_ASSERT(false); // not yet support
+ }
+ }
+ }
return true;
}
@@ -397,8 +439,8 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
// 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 pipe_id = find_opened_pipe(dev_addr, 0);
- TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe
+ uint8_t pipe_id = find_opened_pipe(dev_addr, 0, TUSB_DIR_OUT);
+ TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe
hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id];
pipe->next_data_toggle = HCTSIZ_PID_SETUP;
@@ -529,25 +571,57 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) {
}
}
+bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
+ bool ff_written = false;
+ volatile uint32_t* tx_sts = is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts;
+
+ // find which channel have pending packet
+ for (uint8_t ch_id = 0; ch_id < 32; ch_id++) {
+ if (tu_bit_test(dwc2->haintmsk, ch_id)) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
+ if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ uint8_t pipe_id = find_opened_pipe(hcchar_bm.dev_addr, hcchar_bm.ep_num, TUSB_DIR_OUT);
+ if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) {
+ hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id];
+ if (pipe->pending_tx) {
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size;
+ const uint16_t packet_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size);
+
+ // check if there is enough space in FIFO
+ if (packet_bytes > (*tx_sts & HPTXSTS_PTXQSAV)) {
+ break;
+ }
+
+ dfifo_write_packet(dwc2, ch_id, pipe->buf, packet_bytes);
+ pipe->buf += packet_bytes;
+
+ if (channel->hctsiz_bm.xfer_size == 0) {
+ pipe->pending_tx = false; // all data has been written
+ }
+
+ ff_written = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return ff_written;
+}
+
/* Interrupt Hierarchy
- HCINTn.XferCompl HCINTMSKn.XferComplMsk HPRT ConnDetect PrtEnChng OverCurChng
- | | | | |
- +---------- AND --------+ +------------ OR -----------+
- | |
- HAINT.CHn HAINTMSK.CHn |
- | | |
- +---------- AND --------+ |
- | |
- GINTSTS.HCInt GINTMSK.HCInt GINTSTS.PrtInt GINTMSK.PrtInt
- | | | |
- +---------- AND ---------+ +---------- AND ---------+
- | |
- +-------------------- OR ---------------------------+
- |
- GAHBCFG.GblIntrMsk
- |
- IRQn
- */
+ HCINTn HPRT
+ | |
+ HAINT.CHn |
+ | |
+ GINTSTS HCInt PrtInt NPTxFEmp PTxFEmpp RXFLVL
+
+
+*/
void hcd_int_handler(uint8_t rhport, bool in_isr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint32_t int_mask = dwc2->gintmsk;
@@ -577,6 +651,15 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
handle_channel_irq(rhport, in_isr);
}
+ if (int_status & GINTSTS_NPTX_FIFO_EMPTY) {
+ // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool ff_written = handle_txfifo_empty(dwc2, false);
+ if (!ff_written) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY;
+ }
+ }
+
// RxFIFO non-empty interrupt handling.
if (int_status & GINTSTS_RXFLVL) {
// RXFLVL bit is read-only