summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGordon McNab <[email protected]>2021-10-05 13:54:47 +0100
committerGordon McNab <[email protected]>2021-10-05 13:54:47 +0100
commit62c613f6d2d4e3e2cf0918f75e302d48a3df673e (patch)
tree3f5ff1055feae03c081448572536fb21305027ef /src
parent36dc25a22d05cf9826944e363dd9ca4eb3416661 (diff)
Add initial port for FT9xx series from Bridgetek.
Add FT90X and FT93X to the list of devices in tusb_option.h. 1700 for FT90x and 1701 for FT93x. Set endpoint attributes for FT90x and FT93x in dcd_attr.h. Add FT90x routines for USB device in src/portable/bridgetek/ft90x/dcd_ft90x.c The location for hardware header files and libraries is hw/mcu/bridgetek/ft90x/hardware. There are no files in the repository, but files will be linked as a submodule in the future. The required files can be copied from or linked to the location "C:/Program Files(x86)/Bridgetek/FT9xx Toolchain/Toolchain/hardware" once the toolchain is installed. Makefile for the MM900EV1B board for developing with an FT900 device is present. Use "BOARD=mm900ev1b".
Diffstat (limited to 'src')
-rw-r--r--src/device/dcd_attr.h7
-rw-r--r--src/portable/bridgetek/ft90x/dcd_ft90x.c1101
-rw-r--r--src/tusb_option.h4
3 files changed, 1112 insertions, 0 deletions
diff --git a/src/device/dcd_attr.h b/src/device/dcd_attr.h
index a35fc0ac5..638a17814 100644
--- a/src/device/dcd_attr.h
+++ b/src/device/dcd_attr.h
@@ -151,6 +151,13 @@
#elif TU_CHECK_MCU(GD32VF103)
#define DCD_ATTR_ENDPOINT_MAX 4
+//------------- BridgeTek -------------//
+#elif TU_CHECK_MCU(FT90X)
+ #define DCD_ATTR_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(FT93X)
+ #define DCD_ATTR_ENDPOINT_MAX 16
+
#else
#warning "DCD_ATTR_ENDPOINT_MAX is not defined for this MCU, default to 8"
#define DCD_ATTR_ENDPOINT_MAX 8
diff --git a/src/portable/bridgetek/ft90x/dcd_ft90x.c b/src/portable/bridgetek/ft90x/dcd_ft90x.c
new file mode 100644
index 000000000..ac13f11d1
--- /dev/null
+++ b/src/portable/bridgetek/ft90x/dcd_ft90x.c
@@ -0,0 +1,1101 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (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"
+#include "bsp/board.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_FT90X
+
+#define USBD_USE_STREAMS
+
+#include <stdint.h>
+#include <errno.h>
+#include <ft900.h>
+#include <registers/ft900_registers.h>
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+// Board code will determine the state of VBUS from USB host.
+extern int8_t board_ft90x_vbus(void);
+
+// Static array to store an incoming SETUP request for processing by tinyusb.
+static uint8_t _ft90x_setup_packet[8];
+
+struct ft90x_xfer_state
+{
+ volatile int16_t total_size; // Total transfer size in bytes for this transfer.
+ volatile int16_t remain_size; // Total remaining in transfer.
+ volatile uint8_t *buff_ptr; // Pointer to buffer to transmit from or receive to.
+ volatile uint8_t valid; // Transfer is pending and total_size, remain_size, and buff_ptr are valid.
+
+ uint8_t type; // Endpoint type. Of type USBD_ENDPOINT_TYPE from endpoint descriptor.
+ uint8_t dir; // Endpoint direction. TUSB_DIR_OUT or TUSB_DIR_IN. For control endpoint this is the current direction.
+ uint16_t buff_size; // Actual size of buffer RAM used by endpoint.
+ uint16_t size; // Max packet size for endpoint from endpoint descriptor.
+};
+// Endpoint description array for each endpoint.
+static struct ft90x_xfer_state ep_xfer[USBD_MAX_ENDPOINT_COUNT];
+// USB speed.
+static tusb_speed_t _speed;
+
+// Interrupt handlers.
+void _ft90x_usbd_ISR(void); // Interrupt handler for USB device.
+void ft90x_usbd_pm_ISR(void); // Interrupt handler for USB device for power management (called by board).
+
+// Internal functions forward declarations.
+static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
+static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
+static void _ft90x_reset_edpts(void);
+static inline void _ft90x_phy_enable(bool en);
+static void _ft90x_usb_speed(void);
+static void _dcd_ft90x_attach(void);
+static void _dcd_ft90x_detach(void) __attribute__((unused));
+static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length);
+static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length);
+
+// Internal functions.
+
+// Manage an OUT transfer from the host.
+// This can be up-to the maximum packet size of the endpoint.
+// Continuation of a transfer beyond the maximum packet size is performed
+// by the interrupt handler.
+static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
+{
+ //Note: this is called from only the interrupt handler when an OUT transfer is called.
+ uint16_t ep_size = ep_xfer[ep_number].size;
+ (void)ep_size;
+ if (xfer_bytes > ep_size)
+ {
+ xfer_bytes = ep_size;
+ }
+
+ // Wait until the endpoint has finished - it should be complete!
+ //while (!(USBD_EP_SR_REG(ep_number) & MASK_USBD_EPxSR_OPRDY))
+ //;
+
+ // Send the first packet of max packet size
+ xfer_bytes = _ft90x_dusb_out(ep_number, (uint8_t *)buffer, xfer_bytes);
+ if (ep_number == USBD_EP_0)
+ {
+ // Set flags to indicate data ready.
+ USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_OPRDY);
+ }
+ else
+ {
+ USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_OPRDY);
+ }
+
+ return xfer_bytes;
+}
+
+// Manage an IN transfer to the host.
+// This can be up-to the maximum packet size of the endpoint.
+// Continuation of a transfer beyond the maximum packet size is performed
+// by the interrupt handler.
+static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
+{
+ //Note: this may be called from the interrupt handler or from normal code.
+ uint8_t end = 0;
+ uint16_t ep_size = ep_xfer[ep_number].size;
+ (void)ep_size;
+ if ((xfer_bytes == 0) || (xfer_bytes < ep_size))
+ {
+ end = 1;
+ }
+ else
+ {
+ xfer_bytes = ep_size;
+ }
+
+ if (ep_number == USBD_EP_0)
+ {
+ // An IN direction SETUP can be interrupted by an OUT packet.
+ // This will result in a STALL generated by the silicon.
+ while (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_STALL)
+ {
+ // Clear the STALL and finish the transaction.
+ USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_STALL);
+ }
+ }
+ else
+ {
+ uint8_t sr_reg;
+ // If there is data to transmit then wait until the IN buffer
+ // for the endpoint is empty.
+ do
+ {
+ sr_reg = USBD_EP_SR_REG(ep_number);
+ } while (sr_reg & MASK_USBD_EPxSR_INPRDY);
+
+ }
+
+ xfer_bytes = _ft90x_dusb_in(ep_number, (uint8_t *)buffer, xfer_bytes);
+
+ if (ep_number == USBD_EP_0)
+ {
+ if (end)
+ {
+ // Set flags to indicate data ready and transfer complete.
+ USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_INPRDY | MASK_USBD_EP0SR_DATAEND;
+ }
+ else
+ {
+ // Set flags to indicate data ready.
+ USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_INPRDY);
+ }
+ }
+ else
+ {
+ // Set flags to indicate data ready.
+ USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_INPRDY);
+ }
+
+ return xfer_bytes;
+}
+
+// Reset all endpoints to a default state.
+// Control endpoint enabled and ready. All others disabled.
+static void _ft90x_reset_edpts(void)
+{
+ // Disable all endpoints and remove configuration values.
+ // Clear settings.
+ tu_memclr(ep_xfer, sizeof(ep_xfer));
+ for (int i = 0; i < USBD_MAX_ENDPOINT_COUNT; i++)
+ {
+ // Disable hardware.
+ USBD_EP_CR_REG(i) = 0;
+ }
+
+ // Setup the control endpoint only.
+#if CFG_TUD_ENDPOINT0_SIZE == 64
+ USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_64 << BIT_USBD_EP0_MAX_SIZE);
+#elif CFG_TUD_ENDPOINT0_SIZE == 32
+ USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_32 << BIT_USBD_EP0_MAX_SIZE);
+#elif CFG_TUD_ENDPOINT0_SIZE == 16
+ USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_16 << BIT_USBD_EP0_MAX_SIZE);
+#elif CFG_TUD_ENDPOINT0_SIZE == 8
+ USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_8 << BIT_USBD_EP0_MAX_SIZE);
+#else
+#error "CFG_TUD_ENDPOINT0_SIZE must be defined with a value of 8, 16, 32 or 64."
+#endif
+ // Configure the control endpoint.
+ ep_xfer[USBD_EP_0].size = CFG_TUD_ENDPOINT0_SIZE;
+ ep_xfer[USBD_EP_0].type = TUSB_XFER_CONTROL;
+
+ // Enable interrupts from USB device control.
+ USBD_REG(cmie) = MASK_USBD_CMIE_ALL;
+ // Enable interrupts on EP0.
+ USBD_REG(epie) = (MASK_USBD_EPIE_EP0IE);
+}
+
+// Enable or disable the USB PHY.
+static inline void _ft90x_phy_enable(bool en)
+{
+ if (en)
+ SYS->PMCFG_L |= MASK_SYS_PMCFG_DEV_PHY_EN;
+ else
+ SYS->PMCFG_L &= ~MASK_SYS_PMCFG_DEV_PHY_EN;
+}
+
+// Safely connect to the USB.
+static void _dcd_ft90x_attach(void)
+{
+ uint8_t reg;
+
+ CRITICAL_SECTION_BEGIN
+ // Disable device responses.
+ USBD_REG(faddr) = 0;
+
+ // Reset USB Device.
+ SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL;
+ // Disable device connect/disconnect/host reset detection.
+ SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_DIS_DEV;
+ SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_CONN_DEV;
+ SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN);
+
+ // Enable Chip USB device clock/PM configuration.
+ sys_enable(sys_device_usb_device);
+ CRITICAL_SECTION_END;
+
+ // Wait a short time to get started.
+ delayms(1);
+
+ CRITICAL_SECTION_BEGIN
+ // Turn off the device enable bit.
+#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED
+ USBD_REG(fctrl) = 0;
+#else
+ //Set the full speed only bit if required.
+ USBD_REG(fctrl) = MASK_USBD_FCTRL_MODE_FS_ONLY;
+#endif
+
+ // Clear first reset and suspend interrupts.
+ do
+ {
+ reg = USBD_REG(cmif);
+ USBD_REG(cmif) = reg;
+ } while (reg);
+ // Clear any endpoint interrupts.
+ reg = USBD_REG(epif);
+ USBD_REG(epif) = reg;
+
+ // Disable all interrupts from USB device control before attaching interrupt.
+ USBD_REG(cmie) = 0;
+ CRITICAL_SECTION_END;
+
+ // Enable device connect/disconnect/host reset detection.
+ // Set device detect and remote wakeup enable interrupt enables.
+ SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN;
+
+#if defined(__FT930__)
+ // Setup VBUS detect
+ SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN;
+#endif
+}
+
+// Gracefully disconnect from the USB.
+static void _dcd_ft90x_detach(void)
+{
+ // Disable device connect/disconnect/host reset detection.
+ SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN);
+
+#if defined(__FT930__)
+ // Disable VBUS detection.
+ SYS->MSC0CFG = SYS->MSC0CFG & (~MASK_SYS_MSC0CFG_USB_VBUS_EN);
+#endif
+ CRITICAL_SECTION_BEGIN
+ USBD_REG(epie) = 0;
+ USBD_REG(cmie) = 0;
+ CRITICAL_SECTION_END;
+
+ // Disable the USB function.
+ USBD_REG(fctrl) = 0;
+ delayms(1);
+
+ // Disable USB PHY
+ dcd_disconnect(0);
+ delayms(1);
+
+ // Disable Chip USB device clock/PM configuration.
+ sys_disable(sys_device_usb_device);
+
+ // Reset USB Device... Needed for Back voltage D+ to be <400mV
+ SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL;
+
+ delayms(1);
+ // Set device detect and remote wakeup enable interrupt enables.
+ SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN;
+
+#if defined(__FT930__)
+ // Setup VBUS detect
+ SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN;
+#endif
+}
+
+// Determine the speed of the USB to which we are connected.
+// Set the speed of the PHY accordingly.
+// High speed can be disabled through CFG_TUSB_RHPORT0_MODE settings.
+static void _ft90x_usb_speed(void)
+{
+ uint8_t fctrl_val;
+
+ // If USB device function is already enabled then disable it.
+ if (USBD_REG(fctrl) & MASK_USBD_FCTRL_USB_DEV_EN) {
+ USBD_REG(fctrl) = (USBD_REG(fctrl) & (~MASK_USBD_FCTRL_USB_DEV_EN));
+ delayus(200);
+ }
+
+#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED
+
+ /* Detect high or full speed */
+ fctrl_val = MASK_USBD_FCTRL_USB_DEV_EN;
+#if defined(__FT900__)
+ if (!sys_check_ft900_revB())//if 90x series is rev C
+ {
+ fctrl_val |= MASK_USBD_FCTRL_IMP_PERF;
+ }
+#endif
+ USBD_REG(fctrl) = fctrl_val;
+
+#if defined(__FT930__)
+ delayus(200);
+
+ _speed = (SYS->MSC0CFG & MASK_SYS_MSC0CFG_HIGH_SPED_MODE) ?
+ TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
+#else /* __FT930__ */
+ /* Detection by SOF */
+ while (!(USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ));
+ USBD_REG(cmif) = MASK_USBD_CMIF_SOFIRQ;
+ delayus(125 + 5);
+ _speed = (USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ) ?
+ TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
+ dcd_event_bus_reset(0, _speed, true);
+
+#endif /* !__FT930__ */
+
+#else /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */
+
+ /* User force set to full speed */
+ _speed = TUSB_SPEED_FULL;
+ fctrl_val =
+ MASK_USBD_FCTRL_USB_DEV_EN | MASK_USBD_FCTRL_MODE_FS_ONLY;
+#if defined(__FT900__)
+ if (!sys_check_ft900_revB())//if 90x series is rev C
+ {
+ fctrl_val |= MASK_USBD_FCTRL_IMP_PERF;
+ }
+#endif
+ USBD_REG(fctrl) = fctrl_val;
+ dcd_event_bus_reset(0, _speed, true);
+ return;
+
+#endif /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */
+}
+
+// Send a buffer to the USB IN FIFO.
+// When the macro USBD_USE_STREAMS is defined this will stream a buffer of data
+// to the FIFO using the most efficient MCU streamout combination.
+// If streaming is disabled then it will send each byte of the buffer in turn
+// to the FIFO. The is no reason to not stream.
+// The total number of bytes sent to the FIFO is returned.
+static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length)
+{
+ uint16_t bytes_read = 0;
+ uint16_t buff_size = length;
+
+#ifdef USBD_USE_STREAMS
+ volatile uint8_t *data_reg;
+
+ data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo);
+ if (buff_size)
+ {
+ if (((uint32_t)buffer) % 4 == 0)
+ {
+ uint16_t aligned = buff_size & (~3);
+ uint16_t left = buff_size & 3;
+
+ if (aligned)
+ {
+ __asm__ volatile("streamout.l %0,%1,%2"
+ :
+ : "r"(data_reg), "r"(buffer), "r"(aligned));
+ buffer += aligned;
+ }
+ if (left)
+ {
+ __asm__ volatile("streamout.b %0,%1,%2"
+ :
+ : "r"(data_reg), "r"(buffer), "r"(left));
+ }
+ }
+ else
+ {
+ __asm__ volatile("streamout.b %0,%1,%2"
+ :
+ : "r"(data_reg), "r"(buffer), "r"(buff_size));
+ }
+ bytes_read = buff_size;
+ }
+#else // USBD_USE_STREAMS
+
+ bytes_read = buff_size;
+ while (buff_size--)
+ {
+ USBD_EP_FIFO_REG(ep_number) = *buffer++;
+ };
+
+#endif // USBD_USE_STREAMS
+
+ return bytes_read;
+}
+
+// Receive a buffer from the USB OUT FIFO.
+// When the macro USBD_USE_STREAMS is defined this will stream from the FIFO
+// to a buffer of data using the most efficient MCU streamin combination.
+// If streaming is disabled then it will receive each byte from the FIFO in turn
+// to the buffer. The is no reason to not stream.
+// The total number of bytes received from the FIFO is returned.
+static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length)
+{
+#ifdef USBD_USE_STREAMS
+ volatile uint8_t *data_reg;
+#endif // USBD_USE_STREAMS
+ uint16_t bytes_read = 0;
+ uint16_t buff_size = length;
+
+ if (length > 0)
+ {
+ if (ep_number == USBD_EP_0)
+ {
+ buff_size = USBD_EP_CNT_REG(USBD_EP_0);
+ }
+ else
+ {
+ if (USBD_EP_SR_REG(ep_number) & (MASK_USBD_EPxSR_OPRDY))
+ {
+ buff_size = USBD_EP_CNT_REG(ep_number);
+ }
+ }
+ }
+
+ // Only read as many bytes as we have space for.
+ if (buff_size > length)
+ buff_size = length;
+
+#ifdef USBD_USE_STREAMS
+ data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo);
+ if (buff_size)
+ {
+ if ((uint32_t)buffer % 4 == 0)
+ {
+ uint16_t aligned = buff_size & (~3);
+ uint16_t left = buff_size & 3;
+
+ if (aligned)
+ {
+ __asm__ volatile("streamin.l %0,%1,%2"
+ :
+ : "r"(buffer), "r"(data_reg), "r"(aligned));
+ buffer += aligned;
+ }
+ if (left)
+ {
+ __asm__ volatile("streamin.b %0,%1,%2"
+ :
+ : "r"(buffer), "r"(data_reg), "r"(left));
+ }
+ }
+ else
+ {
+ __asm__ volatile("streamin.b %0,%1,%2"
+ :
+ : "r"(buffer), "r"(data_reg), "r"(buff_size));
+ }
+ bytes_read = buff_size;
+ }
+#else // USBD_USE_STREAMS
+
+ bytes_read = buff_size;
+ while (buff_size--)
+ {
+ *buffer++ = USBD_EP_FIFO_REG(ep_number);
+ }
+
+#endif // USBD_USE_STREAMS
+
+ return bytes_read;
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+
+// Initialize controller to device mode
+void dcd_init(uint8_t rhport)
+{
+ TU_LOG2("FT90x initialisation\r\n");
+
+ _dcd_ft90x_attach();
+
+ //_ft90x_reset_edpts(void);//tu_memclr(ep_xfer, sizeof(ep_xfer));
+
+ interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft90x_usbd_ISR);
+
+ dcd_connect(rhport);
+}
+
+// Enable device interrupt
+void dcd_int_enable(uint8_t rhport)
+{
+ (void)rhport;
+ TU_LOG3("FT90x int enable\r\n");
+
+ // Peripheral devices interrupt enable.
+ interrupt_enable_globally();
+}
+
+// Disable device interrupt
+void dcd_int_disable(uint8_t rhport)
+{
+ (void)rhport;
+ TU_LOG3("FT90x int disable\r\n");
+
+ // Peripheral devices interrupt disable.
+ interrupt_disable_globally();
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void)rhport;
+ (void)dev_addr;
+
+ // Respond with status. There is no checking that the address is in range.
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ // Set the update bit for the address register.
+ dev_addr |= 0x80;
+
+ // Modify the address register within a critical section.
+ CRITICAL_SECTION_BEGIN
+ {
+ USBD_REG(faddr) = dev_addr;
+ }
+ CRITICAL_SECTION_END;
+}
+
+// Invoked when a control transfer's status stage is complete.
+// May help DCD to prepare for next control transfer, this API is optional.
+#if 0 // never called
+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 )
+ {
+ if (request->bRequest == TUSB_REQ_SET_ADDRESS)
+ {
+ }
+ else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION)
+ {
+ }
+ }
+}
+#endif // 0
+
+// Wake up host
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void)rhport;
+
+ SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
+
+ // Atleast 2 ms of delay needed for RESUME Data K state.
+ delayms(2);
+
+ SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
+
+ // Enable USB PHY and determine current bus speed.
+ dcd_connect(0);
+}
+
+// Connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ (void)rhport;
+ TU_LOG2("FT90x connect\r\n");
+
+ CRITICAL_SECTION_BEGIN
+ // Is device connected?
+ if (board_ft90x_vbus())
+ {
+ // Clear/disable address register.
+ USBD_REG(faddr) = 0;
+ _ft90x_phy_enable(true);
+
+ // Determine bus speed and signal speed to tusb.
+ _ft90x_usb_speed();
+ }
+ CRITICAL_SECTION_END;
+
+ // Restore default endpoint state.
+ _ft90x_reset_edpts();
+}
+
+// Disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ (void)rhport;
+ TU_LOG2("FT90x disconnect\r\n");
+
+ // Disable the USB PHY.
+ _ft90x_phy_enable(false);
+}
+
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Configure endpoint's registers according to descriptor
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
+{
+ (void)rhport;
+ uint8_t const ep_number = tu_edpt_number(ep_desc->bEndpointAddress);
+ uint8_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+ uint8_t const ep_type = ep_desc->bmAttributes.xfer;
+ uint16_t const ep_size = ep_desc->wMaxPacketSize.size;
+ uint16_t ep_buff_size;
+ uint8_t ep_reg_size = USBD_EP_MAX_SIZE_8;
+ uint8_t ep_reg_data = 0;
+ int16_t total_ram;
+
+ TU_LOG2("FT90x endpoint open %d %c\r\n", ep_number, ep_dir?'I':'O');
+
+ // Check that the requested endpoint number is allowable.
+ if (ep_number >= USBD_MAX_ENDPOINT_COUNT)
+ {
+ TU_LOG1("FT90x endpoint not valid: requested %d max %d\r\n", ep_number, USBD_MAX_ENDPOINT_COUNT);
+ return false;
+ }
+
+ // Calculate the physical size of the endpoint as a power of 2. This may be more than
+ // the requested size.
+ while (ep_desc->wMaxPacketSize.size > (8 * (1 << ep_reg_size)))
+ {
+ ep_reg_size++;
+ }
+ if (ep_reg_size > USBD_EP_MAX_SIZE_1024)
+ {
+ TU_LOG1("FT90x endpoint size not valid: requested %d max 1024\r\n", ep_desc->wMaxPacketSize.size);
+ return false;
+ }
+ // Calculate actual amount of buffer RAM used by this endpoint. This may be more than the
+ // requested size.
+ ep_buff_size = 8 << ep_reg_size;
+
+ if (ep_number > 0)
+ {
+ // Set EP cmd parameters...
+ ep_reg_data |= (ep_reg_size << BIT_USBD_EP_MAX_SIZE);
+
+ if (ep_xfer[ep_number].type != USBD_EP_TYPE_DISABLED)
+ {
+ // This could be because an endpoint has been assigned with the same number.
+ // On FT90x, IN and OUT endpoints may not have the same number. e.g. There
+ // cannot been an 0x81 and 0x01 endpoint.
+ TU_LOG1("FT90x endpoint %d already assigned\r\n", ep_number);
+ return false;
+ }
+
+ // Check that there is enough buffer RAM to allocate to this new endpoint.
+ // Available buffer RAM depends on the device revision.
+ // The IN and OUT buffer RAM should be the same size.
+ if (ep_dir == USBD_DIR_IN)
+ total_ram = USBD_RAMTOTAL_IN;
+ else
+ total_ram = USBD_RAMTOTAL_OUT;
+ // Work out how much has been allocated to existing endpoints.
+ // The total RAM allocated shoudl alsyes be a positive number as this
+ // algorithm should not let it go below zero.
+ for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
+ {
+ if (ep_xfer[i].type != USBD_EP_TYPE_DISABLED)
+ {
+ if (ep_xfer[i].dir == ep_dir)
+ {
+ total_ram -= ep_xfer[i].buff_size;
+ }
+ }
+ }
+ // The control endpoint is taken into account as well.
+ total_ram -= ep_xfer[0].buff_size;
+ // Make sure we have enough space. The corner case is having zero bytes
+ // free which means that total_ram must be signed as zero bytes free is
+ // allowable.
+ if (total_ram < ep_buff_size)
+ {
+ TU_LOG1("FT90x insufficient buffer RAM for endpoint %d\r\n", ep_number);
+ return false;
+ }
+
+ // Set the type of this endpoint in the control register.
+ if (ep_type == USBD_EP_BULK)
+ ep_reg_data |= (USBD_EP_DIS_BULK << BIT_USBD_EP_CONTROL_DIS);
+ else if (ep_type == USBD_EP_INT)
+ ep_reg_data |= (USBD_EP_DIS_INT << BIT_USBD_EP_CONTROL_DIS);
+ else if (ep_type == USBD_EP_ISOC)
+ ep_reg_data |= (USBD_EP_DIS_ISO << BIT_USBD_EP_CONTROL_DIS);
+ // Set the direction of this endpoint in the control register.
+ if (ep_dir == USBD_DIR_IN)
+ ep_reg_data |= MASK_USBD_EPxCR_DIR;
+ // Do not perform double buffering.
+ //if (<double buffering flag> != USBD_DB_OFF)
+ //ep_reg_data |= MASK_USBD_EPxCR_DB;
+ // Set the control endpoint for this endpoint.
+ USBD_EP_CR_REG(ep_number) = ep_reg_data;
+ TU_LOG2("FT90x endpoint setting %x\r\n", ep_reg_data);
+ }
+ else
+ {
+ // Set the control register for endpoint zero.
+ USBD_EP_CR_REG(USBD_EP_0) = (ep_reg_size << BIT_USBD_EP0_MAX_SIZE);
+ }
+
+ // Store the endpoint characteristics for later reference.
+ ep_xfer[ep_number].dir = ep_dir;
+ ep_xfer[ep_number].type = ep_type;
+ ep_xfer[ep_number].size = ep_size;
+ ep_xfer[ep_number].buff_size = ep_buff_size;
+
+ CRITICAL_SECTION_BEGIN
+ // Clear register transaction continuation and signalling state.
+ ep_xfer[ep_number].valid = 0;
+ ep_xfer[ep_number].buff_ptr = NULL;
+ ep_xfer[ep_number].total_size = 0;
+ ep_xfer[ep_number].remain_size = 0;
+ CRITICAL_SECTION_END
+
+ return true;
+}
+
+// Close all endpoints.
+void dcd_edpt_close_all(uint8_t rhport)
+{
+ (void)rhport;
+ // Reset the endpoint configurations.
+ _ft90x_reset_edpts();
+}
+
+// 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)
+{
+ (void)rhport;
+ uint8_t ep_number = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ uint16_t xfer_bytes;
+ bool status = false;
+
+ // We will attempt to transfer the buffer. If it is less than or equal to the endpoint
+ // maximum packet size then the whole buffer will be transferred. If it is larger then
+ // the interrupt handler will transfer the remainder.
+ // ep_xfer is used to tell the interrupt handler what to do.
+ // ep_xfer can be used at interrupt level to continue transfers.
+ CRITICAL_SECTION_BEGIN
+ // Transfer currently in progress.
+ if (ep_xfer[ep_number].valid == 0)
+ {
+ status = true;
+
+ ep_xfer[ep_number].total_size = total_bytes;
+ ep_xfer[ep_number].remain_size = total_bytes;
+ ep_xfer[ep_number].buff_ptr = buffer;
+ ep_xfer[ep_number].valid = 1;
+
+ if (ep_number == USBD_EP_0)
+ {
+ ep_xfer[USBD_EP_0].dir = dir;
+ }
+ else
+ {
+ // Enable the interrupt for this endpoint allowing the interrupt handler to report
+ // continue the transfer and signal completion.
+ USBD_REG(epie) = USBD_REG(epie) | (1 << ep_number);
+ }
+
+ if (dir == TUSB_DIR_IN)
+ {
+ // For IN transfers send the first packet as a starter. Interrupt handler to complete
+ // this if it is larger than one packet.
+ xfer_bytes = _ft90x_edpt_xfer_in(ep_number, buffer, total_bytes);
+
+ ep_xfer[ep_number].buff_ptr += xfer_bytes;
+ ep_xfer[ep_number].remain_size -= xfer_bytes;
+ }
+ }
+ CRITICAL_SECTION_END
+
+ return status;
+}
+
+// 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)
+{
+ (void)rhport;
+ (void)ep_addr;
+ (void)ff;
+ (void)total_bytes;
+ bool status = false;
+ return status;
+}
+
+// Stall endpoint (non-control endpoint)
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t ep_number = tu_edpt_number(ep_addr);
+ (void)rhport;
+
+ CRITICAL_SECTION_BEGIN
+ if (ep_number == USBD_EP_0)
+ {
+ USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) |
+ MASK_USBD_EP0CR_SDSTL;
+ }
+ else
+ {
+ USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) |
+ MASK_USBD_EPxCR_SDSTL;
+ USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE |
+ MASK_USBD_EPxSR_FIFO_FLUSH;
+ }
+ CRITICAL_SECTION_END
+}
+
+// Clear stall (non-control endpoint), data toggle is also reset to DATA0
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t ep_number = tu_edpt_number(ep_addr);
+ (void)rhport;
+
+ if (ep_number > USBD_EP_0)
+ {
+ CRITICAL_SECTION_BEGIN
+ USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) &
+ (~MASK_USBD_EPxCR_SDSTL);
+ USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE;
+
+ // Allow transfers to restart.
+ ep_xfer[ep_number].valid = 0;
+ ep_xfer[ep_number].remain_size = 0;
+ CRITICAL_SECTION_END
+ }
+}
+
+// Interrupt handling.
+
+void _ft90x_usbd_ISR(void)
+{
+ tud_int_handler(0); // Resolves to dcd_int_handler().
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void)rhport;
+
+ // Read the Common Interrupt Flag Register.
+ uint8_t cmif = USBD_REG(cmif);
+ // Read the Endpoint Interrupt Flag Register.
+#if defined(__FT930__)
+ // This is 16 bits on FT93x.
+ uint16_t epif = USBD_REG(epif);
+#else
+ // This is 8 bits on FT90x.
+ uint8_t epif = USBD_REG(epif);
+#endif
+
+ if (cmif & MASK_USBD_CMIF_ALL)
+ {
+ // Clear all CMIF bits.
+ USBD_REG(cmif) = MASK_USBD_CMIF_ALL;
+ if (cmif & MASK_USBD_CMIF_PHYIRQ) //Handle PHY interrupt
+ {
+ }
+ if (cmif & MASK_USBD_CMIF_PIDIRQ) //Handle PIDIRQ interrupt
+ {
+ }
+ if (cmif & MASK_USBD_CMIF_CRC16IRQ) //Handle CRC16IRQ interrupt
+ {
+ }
+ if (cmif & MASK_USBD_CMIF_CRC5IRQ) //Handle CRC5 interrupt
+ {
+ }
+ if (cmif & MASK_USBD_CMIF_RSTIRQ) //Handle Reset interrupt
+ {
+ // Reset endpoints to default state.
+ _ft90x_reset_edpts();
+ dcd_event_bus_reset(0, _speed, true);
+ }
+ if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+ if (cmif & MASK_USBD_CMIF_RESIRQ) //Handle Resume interrupt
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+ if (cmif & MASK_USBD_CMIF_SOFIRQ) //Handle SOF interrupt
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+ }
+ // Handle endpoint interrupts.
+ if (epif)
+ {
+ uint16_t xfer_bytes;
+
+ // Check for EP0 interrupts pending.
+ if (epif & MASK_USBD_EPIF_EP0IRQ)
+ {
+ // Clear interrupt register.
+ USBD_REG(epif) = MASK_USBD_EPIF_EP0IRQ;
+
+ // Test for an incoming SETUP request on the control endpoint.
+ if (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_SETUP)
+ {
+ // If protocol STALL, End the STALL signalling.
+ if (USBD_EP_CR_REG(USBD_EP_0) & MASK_USBD_EP0CR_SDSTL)
+ {
+ // STALL end.
+ USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) &
+ (~MASK_USBD_EP0CR_SDSTL);
+ // Clear STALL send.
+ USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_STALL;
+ }
+
+ // Host has sent a SETUP packet. Recieve this into the setup packet store.
+ _ft90x_dusb_out(USBD_EP_0, (uint8_t *)_ft90x_setup_packet, sizeof(USB_device_request));
+
+ // Send the packet to tinyusb.
+ dcd_event_setup_received(0, _ft90x_setup_packet, true);
+
+ // Clear the interrupt that signals a SETUP packet is received.
+ USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_SETUP);
+
+ // Allow new transfers on the control endpoint.
+ ep_xfer[USBD_EP_0].valid = 0;
+ return;
+ }
+ else
+ {
+ // Check for a complete or partially complete transfers on EP0.
+ if (ep_xfer[USBD_EP_0].valid)
+ {
+ xfer_bytes = (uint16_t)ep_xfer[USBD_EP_0].total_size;
+
+ // Transfer incoming data from an OUT packet to the buffer supplied.
+ if (ep_xfer[USBD_EP_0].dir == TUSB_DIR_OUT)
+ {
+ xfer_bytes = _ft90x_edpt_xfer_out(USBD_EP_0, (uint8_t *)ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes);
+ }
+ // Now signal completion of data packet.
+ dcd_event_xfer_complete(0, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true);
+
+ // Allow new transfers on the control endpoint.
+ ep_xfer[USBD_EP_0].valid = 0;
+ }
+ }
+ }
+ else // !(epif & MASK_USBD_EPIF_EP0IRQ)
+ {
+ // Mask out currently disabled endpoints.
+ epif &= USBD_REG(epie);
+
+ // Handle complete and partially complete transfers for each endpoint.
+ for (uint8_t ep_number = 1; ep_number < USBD_MAX_ENDPOINT_COUNT; ep_number++)
+ {
+ if ((epif & MASK_USBD_EPIF_IRQ(ep_number)) == 0)
+ {
+ // No pending interrupt for this endpoint.
+ continue;
+ }
+
+ if (ep_xfer[ep_number].valid)
+ {
+ xfer_bytes = 0;
+ uint8_t ep_dirmask = (ep_xfer[ep_number].dir ? TUSB_DIR_IN_MASK : 0);
+
+ // Clear interrupt register for this endpoint.
+ USBD_REG(epif) = MASK_USBD_EPIF_IRQ(ep_number);
+
+ // Start or continue an OUT transfer.
+ if (ep_xfer[ep_number].dir == TUSB_DIR_OUT)
+ {
+ xfer_bytes = _ft90x_edpt_xfer_out(ep_number,
+ (uint8_t *)ep_xfer[ep_number].buff_ptr,
+ (uint16_t)ep_xfer[ep_number].remain_size);
+
+ ep_xfer[ep_number].buff_ptr += xfer_bytes;
+ ep_xfer[ep_number].remain_size -= xfer_bytes;
+ }
+ // continue an IN transfer
+ else // if (ep_xfer[ep_number].dir == TUSB_DIR_IN)
+ {
+ if (ep_xfer[ep_number].remain_size > 0)
+ {
+ xfer_bytes = _ft90x_edpt_xfer_in(ep_number,
+ (uint8_t *)ep_xfer[ep_number].buff_ptr,
+ (uint16_t)ep_xfer[ep_number].remain_size);
+
+ ep_xfer[ep_number].buff_ptr += xfer_bytes;
+ ep_xfer[ep_number].remain_size -= xfer_bytes;
+ }
+ }
+
+ // When the transfer is complete...
+ if (ep_xfer[ep_number].remain_size == 0)
+ {
+ // Signal tinyUSB.
+ dcd_event_xfer_complete(0, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true);
+
+ // Allow new transfers on this endpoint.
+ ep_xfer[ep_number].valid = 0;
+
+ // Disable the interrupt for this endpoint now it is complete.
+ USBD_REG(epie) = USBD_REG(epie) & (~(1 << ep_number));
+ }
+ }
+ }
+ }
+ }
+}
+
+// Power management interrupt handler.
+// This handles USB device related power management interrupts only.
+void ft90x_usbd_pm_ISR(void)
+{
+ uint16_t pmcfg = SYS->PMCFG_H;
+
+ // Main interrupt handler is responible for
+ if (pmcfg & MASK_SYS_PMCFG_DEV_CONN_DEV)
+ {
+ // Signal connection interrupt
+ SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+
+ if (pmcfg & MASK_SYS_PMCFG_DEV_DIS_DEV)
+ {
+ // Signal disconnection interrupt
+ SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ if (pmcfg & MASK_SYS_PMCFG_HOST_RST_DEV)
+ {
+ // Signal Host Reset interrupt
+ SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
+ dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+ }
+
+ if (pmcfg & MASK_SYS_PMCFG_HOST_RESUME_DEV)
+ {
+ // Signal Host Resume interrupt
+ SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
+ if (!(SYS->MSC0CFG & MASK_SYS_MSC0CFG_DEV_RMWAKEUP))
+ {
+ // If we are driving K-state on Device USB port;
+ // We must maintain the 1ms requirement before resuming the phy
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+ }
+}
+
+#endif
diff --git a/src/tusb_option.h b/src/tusb_option.h
index a00f3489f..d04cb54f0 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -126,6 +126,10 @@
// GigaDevice
#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103
+// BridgeTek
+#define OPT_MCU_FT90X 1700 ///< BridgeTek FT90x
+#define OPT_MCU_FT93X 1701 ///< BridgeTek FT93x
+
//--------------------------------------------------------------------+
// Supported OS
//--------------------------------------------------------------------+