summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthew Tran <[email protected]>2023-11-19 18:26:40 -0800
committerhathach <[email protected]>2024-05-15 20:01:00 +0700
commitdb60fa1c63ae58c205e43e144253dab6815bb2d1 (patch)
treef15653fde2d8eb2e2c43eee8eec0bc687a068720 /src
parentcf532d14792411fd40ad3433fe0ce9959887ef4b (diff)
add CH32V20x USB OTG/FS driver
Diffstat (limited to 'src')
-rw-r--r--src/class/video/video_device.c2
-rw-r--r--src/common/tusb_mcu.h4
-rw-r--r--src/portable/wch/ch32_usbfs_reg.h77
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c304
-rw-r--r--src/tusb_option.h1
5 files changed, 387 insertions, 1 deletions
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 4218835aa..974289b69 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -609,7 +609,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
tusb_desc_cs_video_fmt_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum);
tusb_desc_cs_video_frm_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum);
- uint_fast32_t interval, interval_ms;
+ uint_fast32_t interval, interval_ms = 0;
switch (request) {
case VIDEO_REQUEST_GET_MAX: {
uint_fast32_t min_interval, max_interval;
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 3a7d6ad33..eab112015 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -410,6 +410,10 @@
#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_RHPORT_HIGHSPEED 1
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
+ #define TUP_DCD_ENDPOINT_MAX 8
+
#endif
diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h
new file mode 100644
index 000000000..3b8e49ae2
--- /dev/null
+++ b/src/portable/wch/ch32_usbfs_reg.h
@@ -0,0 +1,77 @@
+#ifndef USB_CH32_USBFS_REG_H
+#define USB_CH32_USBFS_REG_H
+
+#if (CFG_TUSB_MCU == OPT_MCU_CH32V20X)
+#include <ch32v20x.h>
+#endif
+
+// CTRL
+#define USBFS_CTRL_DMA_EN (1 << 0)
+#define USBFS_CTRL_CLR_ALL (1 << 1)
+#define USBFS_CTRL_RESET_SIE (1 << 2)
+#define USBFS_CTRL_INT_BUSY (1 << 3)
+#define USBFS_CTRL_SYS_CTRL (1 << 4)
+#define USBFS_CTRL_DEV_PUEN (1 << 5)
+#define USBFS_CTRL_LOW_SPEED (1 << 6)
+#define USBFS_CTRL_HOST_MODE (1 << 7)
+
+// INT_EN
+#define USBFS_INT_EN_BUS_RST (1 << 0)
+#define USBFS_INT_EN_DETECT (1 << 0)
+#define USBFS_INT_EN_TRANSFER (1 << 1)
+#define USBFS_INT_EN_SUSPEND (1 << 2)
+#define USBFS_INT_EN_HST_SOF (1 << 3)
+#define USBFS_INT_EN_FIFO_OV (1 << 4)
+#define USBFS_INT_EN_DEV_NAK (1 << 6)
+#define USBFS_INT_EN_DEV_SOF (1 << 7)
+
+// INT_FG
+#define USBFS_INT_FG_BUS_RST (1 << 0)
+#define USBFS_INT_FG_DETECT (1 << 0)
+#define USBFS_INT_FG_TRANSFER (1 << 1)
+#define USBFS_INT_FG_SUSPEND (1 << 2)
+#define USBFS_INT_FG_HST_SOF (1 << 3)
+#define USBFS_INT_FG_FIFO_OV (1 << 4)
+#define USBFS_INT_FG_SIE_FREE (1 << 5)
+#define USBFS_INT_FG_TOG_OK (1 << 6)
+#define USBFS_INT_FG_IS_NAK (1 << 7)
+
+// INT_ST
+#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F)
+#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03)
+
+// UDEV_CTRL
+#define USBFS_UDEV_CTRL_PORT_EN (1 << 0)
+#define USBFS_UDEV_CTRL_GP_BIT (1 << 1)
+#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2)
+#define USBFS_UDEV_CTRL_DM_PIN (1 << 4)
+#define USBFS_UDEV_CTRL_DP_PIN (1 << 5)
+#define USBFS_UDEV_CTRL_PD_DIS (1 << 7)
+
+// TX_CTRL
+#define USBFS_EP_T_RES_MASK (3 << 0)
+#define USBFS_EP_T_TOG (1 << 2)
+#define USBFS_EP_T_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_T_RES_ACK (0 << 0)
+#define USBFS_EP_T_RES_NYET (1 << 0)
+#define USBFS_EP_T_RES_NAK (2 << 0)
+#define USBFS_EP_T_RES_STALL (3 << 0)
+
+// RX_CTRL
+#define USBFS_EP_R_RES_MASK (3 << 0)
+#define USBFS_EP_R_TOG (1 << 2)
+#define USBFS_EP_R_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_R_RES_ACK (0 << 0)
+#define USBFS_EP_R_RES_NYET (1 << 0)
+#define USBFS_EP_R_RES_NAK (2 << 0)
+#define USBFS_EP_R_RES_STALL (3 << 0)
+
+// token PID
+#define PID_OUT 0
+#define PID_SOF 1
+#define PID_IN 2
+#define PID_SETUP 3
+
+#endif // USB_CH32_USBFS_REG_H
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
new file mode 100644
index 000000000..256c46696
--- /dev/null
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -0,0 +1,304 @@
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_CH32V20X)
+
+#include <stdio.h>
+#include "device/dcd.h"
+#include "ch32_usbfs_reg.h"
+
+/* private defines */
+#define EP_MAX (8)
+
+#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep])
+#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
+#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
+#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
+
+/* private data */
+struct usb_xfer {
+ bool valid;
+ uint8_t *buffer;
+ size_t len;
+ size_t processed_len;
+ size_t max_size;
+};
+
+static struct {
+ bool ep0_tog;
+ bool isochronous[EP_MAX];
+ struct usb_xfer xfer[EP_MAX][2];
+ TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
+ TU_ATTR_ALIGNED(4) struct {
+ // OUT transfers >64 bytes will overwrite queued IN data!
+ uint8_t out[64];
+ uint8_t in[1023];
+ uint8_t pad;
+ } ep3_buffer;
+} data;
+
+/* private helpers */
+static void update_in(uint8_t rhport, uint8_t ep, bool force) {
+ struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_IN];
+ if (xfer->valid) {
+ if (force || xfer->len) {
+ size_t len = TU_MIN(xfer->max_size, xfer->len);
+ if (ep == 0) {
+ memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
+ } else if (ep == 3) {
+ memcpy(data.ep3_buffer.in, xfer->buffer, len);
+ } else {
+ memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ EP_TX_LEN(ep) = len;
+ if (ep == 0) {
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0);
+ data.ep0_tog = !data.ep0_tog;
+ } else if (data.isochronous[ep]) {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK;
+ }
+ } else {
+ xfer->valid = false;
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK;
+ dcd_event_xfer_complete(rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len,
+ XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+
+static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
+ struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_OUT];
+ if (xfer->valid) {
+ size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
+ if (ep == 3) {
+ memcpy(xfer->buffer, data.ep3_buffer.out, len);
+ } else {
+ memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ if (xfer->len == 0 || len < xfer->max_size) {
+ xfer->valid = false;
+ dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ if (ep == 0) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ }
+}
+
+/* public functions */
+void dcd_init(uint8_t rhport) {
+ // 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;
+ USBOTG_FS->DEV_ADDR = 0x00;
+
+ USBOTG_FS->INT_FG = 0xFF;
+ USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
+
+ // setup endpoint 0
+ EP_DMA(0) = (uint32_t) &data.buffer[0][0];
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ // enable other endpoints but NAK everything
+ USBOTG_FS->UEP4_1_MOD = 0xCC;
+ USBOTG_FS->UEP2_3_MOD = 0xCC;
+ USBOTG_FS->UEP5_6_MOD = 0xCC;
+ USBOTG_FS->UEP7_MOD = 0x0C;
+
+ for (uint8_t ep = 1; ep < EP_MAX; ep++) {
+ EP_DMA(ep) = (uint32_t) &data.buffer[ep][0];
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK;
+ }
+ EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
+
+ dcd_connect(rhport);
+}
+
+void dcd_int_handler(uint8_t rhport) {
+ (void) rhport;
+ uint8_t status = USBOTG_FS->INT_FG;
+ if (status & USBFS_INT_FG_TRANSFER) {
+ uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
+ uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
+
+ switch (token) {
+ case PID_OUT: {
+ uint16_t rx_len = USBOTG_FS->RX_LEN;
+ update_out(rhport, ep, rx_len);
+ break;
+ }
+
+ case PID_IN:
+ update_in(rhport, ep, false);
+ break;
+
+ case PID_SETUP:
+ data.ep0_tog = true;
+ dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
+ break;
+ }
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER;
+ } else if (status & USBFS_INT_FG_BUS_RST) {
+ data.ep0_tog = true;
+ data.xfer[0][TUSB_DIR_OUT].max_size = 64;
+ data.xfer[0][TUSB_DIR_IN].max_size = 64;
+
+ dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+
+ USBOTG_FS->DEV_ADDR = 0x00;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST;
+ } else if (status & USBFS_INT_FG_SUSPEND) {
+ dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPEND };
+ dcd_event_handler(&event, true);
+ USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND;
+ }
+}
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBHD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBHD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void) dev_addr;
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response
+}
+
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
+ (void) rhport;
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue;
+ }
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
+ uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ TU_ASSERT(ep < EP_MAX);
+
+ data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS;
+ data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
+
+ if (ep != 0) {
+ if (dir == TUSB_DIR_OUT) {
+ if (data.isochronous[ep]) {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET;
+ } else {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ }
+ }
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ (void) ep_addr;
+ // TODO optional
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+
+ struct usb_xfer *xfer = &data.xfer[ep][dir];
+ xfer->valid = true;
+ xfer->buffer = buffer;
+ xfer->len = total_bytes;
+ xfer->processed_len = 0;
+
+ if (dir == TUSB_DIR_IN) {
+ update_in(rhport, ep, true);
+ }
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL;
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK;
+ }
+ }
+}
+
+#endif // CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_CH32V20X)
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 058427e0e..3d04825a4 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -181,6 +181,7 @@
// WCH
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
+#define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X
// NXP LPC MCX