Просмотр исходного кода

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".

Gordon McNab 4 лет назад
Родитель
Сommit
62c613f6d2

+ 56 - 0
hw/bsp/brtmm90x/boards/mm900ev1b/board.h

@@ -0,0 +1,56 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (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.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if defined(__FT900__)
+#define GPIO_UART0_TX 48
+#define GPIO_UART0_RX 49
+#define GPIO_ETH_LED0 61
+#define GPIO_ETH_LED1 62
+#define GPIO_REMOTE_WAKEUP_PIN 18
+#define USBD_VBUS_DTC_PIN 3
+#elif defined(__FT930__)
+#define GPIO_UART0_TX 23
+#define GPIO_UART0_RX 22
+#define GPIO_ETH_LED0 4
+#define GPIO_ETH_LED1 5
+#define GPIO_REMOTE_WAKEUP_PIN 12
+#define USBD_VBUS_DTC_PIN 39
+#endif
+
+//#define GPIO_REMOTE_WAKEUP
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif

+ 210 - 0
hw/bsp/brtmm90x/family.c

@@ -0,0 +1,210 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright 2019 Sony Semiconductor Solutions Corporation
+ *
+ * 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 <registers/ft900_registers.h>
+#include <ft900.h>
+#include "bsp/board.h"
+#include "board.h"
+//#include "src/device/dcd.h"
+
+#if TUSB_OPT_DEVICE_ENABLED
+int8_t board_ft90x_vbus(void); // Board specific implementation of VBUS detection for USB device.
+extern void ft90x_usbd_pm_ISR(uint16_t pmcfg); // Interrupt handler for USB device power management
+#endif
+
+#ifdef GPIO_REMOTE_WAKEUP
+void gpio_ISR(void);
+#endif
+void timer_ISR(void);
+volatile unsigned int timer_ms = 0;
+void board_pm_ISR(void);
+
+#define WELCOME_MSG "\x1B[2J\x1B[H" \
+                    "MM900EV1B board\r\n"
+
+// Initialize on-board peripherals : led, button, uart and USB
+void board_init(void)
+{
+	sys_reset_all();
+    // Enable the UART Device.
+    sys_enable(sys_device_uart0);
+    // Set UART0 GPIO functions to UART0_TXD and UART0_RXD.
+    gpio_function(GPIO_UART0_TX, pad_uart0_txd); /* UART0 TXD */
+    gpio_function(GPIO_UART0_RX, pad_uart0_rxd); /* UART0 RXD */
+    uart_open(UART0,                             /* Device */
+              1,                                 /* Prescaler = 1 */
+              UART_DIVIDER_19200_BAUD,           /* Divider = 1302 */
+              uart_data_bits_8,                  /* No. Data Bits */
+              uart_parity_none,                  /* Parity */
+              uart_stop_bits_1);                 /* No. Stop Bits */
+	// Print out a welcome message.
+    // Use sizeof to avoid pulling in strlen unnecessarily.
+    board_uart_write(WELCOME_MSG, sizeof(WELCOME_MSG));
+
+#if 1
+    gpio_function(GPIO_ETH_LED0, pad_gpio4); /* ETH LED0 */
+    gpio_dir(GPIO_ETH_LED0, pad_dir_open_drain);
+    gpio_function(GPIO_ETH_LED1, pad_gpio5); /* ETH LED0 */
+    gpio_dir(GPIO_ETH_LED1, pad_dir_output);
+#endif
+	sys_enable(sys_device_timer_wdt);
+	interrupt_attach(interrupt_timers, (int8_t)interrupt_timers, timer_ISR);
+	/* Timer A = 1ms */
+	timer_prescaler(timer_select_a, 1000);
+	timer_init(timer_select_a, 100, timer_direction_down, timer_prescaler_select_on, timer_mode_continuous);
+	timer_enable_interrupt(timer_select_a);
+	timer_start(timer_select_a);
+
+    // Setup VBUS detect GPIO. If the device is connected then this
+    // will set the MASK_SYS_PMCFG_DEV_DETECT_EN bit in PMCFG.
+    gpio_interrupt_disable(USBD_VBUS_DTC_PIN);
+    gpio_function(USBD_VBUS_DTC_PIN, pad_vbus_dtc);
+    gpio_pull(USBD_VBUS_DTC_PIN, pad_pull_pulldown);
+    gpio_dir(USBD_VBUS_DTC_PIN, pad_dir_input);
+
+    interrupt_attach(interrupt_0, (int8_t)interrupt_0, board_pm_ISR);
+
+#ifdef GPIO_REMOTE_WAKEUP
+    //Configuring GPIO pin to wakeup.
+    // Set up the wakeup pin.
+    gpio_dir(GPIO_REMOTE_WAKEUP_PIN, pad_dir_input);
+    gpio_pull(GPIO_REMOTE_WAKEUP_PIN, pad_pull_pullup);
+
+    // Attach an interrupt handler.
+    interrupt_attach(interrupt_gpio, (uint8_t)interrupt_gpio, gpio_ISR);
+    gpio_interrupt_enable(GPIO_REMOTE_WAKEUP_PIN, gpio_int_edge_falling);
+#endif
+
+	uart_disable_interrupt(UART0, uart_interrupt_tx);
+	uart_disable_interrupt(UART0, uart_interrupt_rx);
+
+    // Enable all peripheral interrupts.
+    interrupt_enable_globally();
+
+    TU_LOG1("MM900EV1B board setup complete\r\n");
+};
+
+void timer_ISR(void)
+{
+    if (timer_is_interrupted(timer_select_a))
+    {
+        timer_ms++;
+    }
+}
+
+#ifdef GPIO_REMOTE_WAKEUP
+void gpio_ISR(void)
+{
+    if (gpio_is_interrupted(GPIO_REMOTE_WAKEUP_PIN))
+    {
+    }
+}
+#endif
+
+/* Power management ISR */
+void board_pm_ISR(void)
+{
+    uint16_t pmcfg = SYS->PMCFG_H;
+
+#if defined(__FT930__)
+    if (pmcfg & MASK_SYS_PMCFG_SLAVE_PERI_IRQ_PEND)
+    {
+        // Clear d2xx hw engine wakeup.
+        SYS->PMCFG_H = MASK_SYS_PMCFG_SLAVE_PERI_IRQ_PEND;
+    }
+#endif
+    if (pmcfg & MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND)
+    {
+        // Clear GPIO wakeup pending.
+        SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
+    }
+
+#if defined(__FT900__)
+    // USB device power management interrupts.
+    if (pmcfg & (MASK_SYS_PMCFG_DEV_CONN_DEV |
+              MASK_SYS_PMCFG_DEV_DIS_DEV |
+              MASK_SYS_PMCFG_HOST_RST_DEV |
+              MASK_SYS_PMCFG_HOST_RESUME_DEV)
+    )
+    {
+#if TUSB_OPT_DEVICE_ENABLED
+        ft90x_usbd_pm_ISR(pmcfg);
+#endif
+    }
+#endif
+}
+
+#if TUSB_OPT_DEVICE_ENABLED
+int8_t board_ft90x_vbus(void)
+{
+	return gpio_read(USBD_VBUS_DTC_PIN);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Board porting API
+//--------------------------------------------------------------------+
+
+// Turn LED on or off
+void board_led_write(bool state)
+{
+    gpio_write(GPIO_ETH_LED0, state);
+}
+
+// Get the current state of button
+// a '1' means active (pressed), a '0' means inactive.
+uint32_t board_button_read(void)
+{
+    return 0;
+}
+
+// Get characters from UART
+int board_uart_read(uint8_t *buf, int len)
+{
+    int r = uart_readn(UART0, (uint8_t *)buf, len);
+
+    return r;
+}
+
+// Send characters to UART
+int board_uart_write(void const *buf, int len)
+{
+    int r = uart_writen(UART0, (uint8_t *)buf, len);
+
+    return r;
+}
+
+// Get current milliseconds
+uint32_t board_millis(void)
+{
+    uint32_t safe_ms;
+    
+    CRITICAL_SECTION_BEGIN
+    safe_ms = timer_ms;
+    CRITICAL_SECTION_END
+
+    return safe_ms;
+}

+ 35 - 0
hw/bsp/brtmm90x/family.mk

@@ -0,0 +1,35 @@
+
+CROSS_COMPILE = ft32-elf-
+DEPS_SUBMODULES += hw/mcu/bridgetek/ft90x/hardware
+SKIP_NANOLIB = 1
+
+# This is installed at "C:/Program Files(x86)/Bridgetek/FT9xx Toolchain/Toolchain/hardware"
+FT90X_SDK = $(TOP)/hw/mcu/bridgetek/ft90x/hardware
+
+CFLAGS += \
+	-D__FT900__ \
+	-fvar-tracking \
+	-fvar-tracking-assignments \
+	-fmessage-length=0 \
+	-ffunction-sections \
+	-DCFG_TUSB_MCU=OPT_MCU_FT90X 
+
+# lwip/src/core/raw.c:334:43: error: declaration of 'recv' shadows a global declaration
+CFLAGS += -Wno-error=shadow
+CFLAGS:=$(filter-out -Wcast-function-type,$(CFLAGS))
+
+# All source paths should be relative to the top level.
+LDINC += $(FT90X_SDK)/lib/Release
+LIBS += -lft900
+LD_FILE = hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld
+LDFLAGS += $(addprefix -L,$(LDINC)) \
+	-Xlinker --entry=_start \
+	-Wl,-lc
+
+SRC_C += src/portable/bridgetek/ft90x/dcd_ft90x.c 
+
+#SRC_S += hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S
+
+INC += \
+	$(FT90X_SDK)/include \
+	$(TOP)/$(BOARD_PATH)

+ 6 - 0
hw/mcu/bridgetek/ft90x/Readme.md

@@ -0,0 +1,6 @@
+# BridgeTek FT9xx MCU
+
+**BridgeTek** provides a hardware abstraction library with software source code for the SDKs for FT9xx software family.
+The pre-built libraries and the source code can be redistributed.
+Registers definition files `<sdk>/include/registers/ft900_registers.h` and included peripheral register definition files have licenses that allow for redistribution.  
+Whole SDK repository is installed as part of the FT9xx Toolchain and can be downloaded from BridgeTek web page `https://www.brtchip.com`

+ 286 - 0
hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S

@@ -0,0 +1,286 @@
+.equ SYS_REGMSC0CFG_B3  , 0x1001b
+.equ SYS_REGIRQCTL_B3   , 0x100e3
+.equ MAILBOX_MEMORY     , 0x13000
+
+.equ	IS_IMG_SDBL_PRESENT, 0
+.equ	IS_IMG_D2XX_PRESENT, 0
+.equ	IS_IMG_DLOG_PRESENT, 0
+
+.section .crt0
+.global _start
+
+_start:
+# START Interrupt Vector Table [[
+        jmp     __PMSIZE-4         # RESET Vector
+        jmp     interrupt_33	   # Watchdog reset vector
+        jmp     interrupt_0
+        jmp     interrupt_1
+        jmp     interrupt_2
+        jmp     interrupt_3
+        jmp     interrupt_4
+        jmp     interrupt_5
+        jmp     interrupt_6
+        jmp     interrupt_7
+        jmp     interrupt_8
+        jmp     interrupt_9
+        jmp     interrupt_10
+        jmp     interrupt_11
+        jmp     interrupt_12
+        jmp     interrupt_13
+        jmp     interrupt_14
+        jmp     interrupt_15
+        jmp     interrupt_16
+        jmp     interrupt_17
+        jmp     interrupt_18
+        jmp     interrupt_19
+        jmp     interrupt_20
+        jmp     interrupt_21
+        jmp     interrupt_22
+        jmp     interrupt_23
+        jmp     interrupt_24
+        jmp     interrupt_25
+        jmp     interrupt_26
+        jmp     interrupt_27
+        jmp     interrupt_28
+        jmp     interrupt_29
+        jmp     interrupt_30
+        jmp     interrupt_31
+        jmp     __PMSIZE-8				#Interrupt vector 32 (NMI)
+# ]] END Interrupt Vector Table
+
+codestart:
+        jmp    init
+        
+.global _exithook
+_exithook:               # Debugger uses '_exithook' at 0x90 to catch program exit
+        return
+        
+init:
+        # Disable all interrupts
+        ldk     $r0,0x80
+.ifdef __FT930__
+        sta.b   0x10123, $r0
+.else
+        sta.b   0x100e3,$r0
+.endif
+        
+        # Reset all peripherals
+        # lda.l   $r0, 0x10018
+        # bins.l  $r0, $r0, 0x23F  # Set bit 31
+        # sta.l   0x10018, $r0
+        
+        # Initialize DATA by copying from program memory
+        ldk.l   $r0,__data_load_start
+        ldk.l   $r1,__data_load_end
+        ldk.l   $r2,0   # Will use __data after binutils patch
+
+        jmp     .dscopy
+.dsloop:
+        # Copy PM[$r0] to RAM $r2
+        lpmi.l  $r3,$r0,0
+        sti.l   $r2,0,$r3
+        add.l   $r0,$r0,4
+        add.l   $r2,$r2,4
+.dscopy:
+        cmp.l   $r0,$r1
+        jmpc    lt,.dsloop
+
+        # Zero BSS
+        ldk.l   $r0,_bss_start
+        ldk.l   $r2,_end
+        sub.l   $r2,$r2,$r0
+        ldk.l   $r1,0
+        ldk    $r3,32764
+1:
+        cmp    $r2,$r3
+        jmpc   lt,2f
+        memset $r0,$r1,$r3
+        add    $r0,$r0,$r3
+        sub    $r2,$r2,$r3
+        jmp    1b
+2:
+        memset $r0,$r1,$r2
+.ifdef __FT930__
+/*##############################################################*/
+		# copy UserConfig DATA from flash to mailbox memory
+/*##############################################################*/
+        ldk.l   $r0,D2XX_Struct_start  /*start of d2xx config in PM memory */
+        ldk.l   $r1,D2XX_Struct_end /*end of d2xx config in PM memory */
+        ldk.l   $r2,D2XXTEST_UserD2xxConfig /* RAM cache where the d2xx config from PM to be copied*/
+        jmp     .configcopy
+
+.configloop:
+        # Copy PM[$r0] to RAM[$r2]
+        lpmi.l  $r3,$r0,0
+        sti.l   $r2,0,$r3
+        # Increment
+        add.l   $r0,$r0,4
+        add.l   $r2,$r2,4
+.configcopy:
+        cmp.l   $r0,$r1
+        jmpc    lt,.configloop
+
+        ldk.l   $r1,D2XX_Struct_start
+        ldk.l   $r2,D2XX_Struct_end
+        #compute size
+        sub.l   $r2,$r2,$r1
+        ldk.l   $r1,D2XXTEST_UserD2xxConfig
+        ldk.l   $r0,MAILBOX_MEMORY		/* D2xx config from RAM cache to be copied to Mailbox memory */
+        # Copy RAM[$r1] to Mailbox $r0, for $r2 bytes
+        streamouti.b   $r0,$r1,$r2
+/*############################################################*/
+.endif
+        sub.l   $sp,$sp,24  # Space for the caller argument frame
+        call    main
+
+.equ EXITEXIT    , 0x1fffc
+
+.global _exit
+_exit:
+        sta.l   EXITEXIT,$r0    # simulator end of test
+        jmp     _exithook
+
+#_watchdog_isr:
+#        ldk     $sp,__RAMSIZE
+#        jmp     __PMSIZE-4
+
+# Macro to construct the interrupt stub code.
+# it just saves r0, loads r0 with the int vector
+# and branches to interrupt_common.
+
+.macro  inth i=0
+interrupt_\i:
+        push    $r0     # {
+        lda     $r0,(vector_table + 4 * \i)
+        jmp     interrupt_common
+.endm
+
+        inth    0
+        inth    1
+        inth    2
+        inth    3
+        inth    4
+        inth    5
+        inth    6
+        inth    7
+        inth    8
+        inth    9
+        inth    10
+        inth    11
+        inth    12
+        inth    13
+        inth    14
+        inth    15
+        inth    16
+        inth    17
+        inth    18
+        inth    19
+        inth    20
+        inth    21
+        inth    22
+        inth    23
+        inth    24
+        inth    25
+        inth    26
+        inth    27
+        inth    28
+        inth    29
+        inth    30
+        inth    31
+        inth    32
+        inth    33
+
+        # On entry: r0, already saved, holds the handler function
+interrupt_common:
+        push    $r1     # {
+        push    $r2     # {
+        push    $r3     # {
+        push    $r4     # {
+        push    $r5     # {
+        push    $r6     # {
+        push    $r7     # {
+        push    $r8     # {
+        push    $r9     # {
+        push    $r10    # {
+        push    $r11    # {
+        push    $r12    # {
+        push    $cc     # {
+
+        calli   $r0
+
+        pop     $cc     # }
+        pop     $r12    # }
+        pop     $r11    # }
+        pop     $r10    # }
+        pop     $r9     # }
+        pop     $r8     # }
+        pop     $r7     # }
+        pop     $r6     # }
+        pop     $r5     # }
+        pop     $r4     # }
+        pop     $r3     # }
+        pop     $r2     # }
+        pop     $r1     # }
+        pop     $r0     # } matching push in interrupt_0-31 above
+        reti
+
+        # Null function for unassigned interrupt to point at
+.global     nullvector
+nullvector:
+        return
+
+.section .data
+.global vector_table
+	.align (4)			# assembler alignment is in the power of 2 (in this case 2^4)
+vector_table:
+        .rept 34
+                .long   nullvector
+        .endr
+
+
+.section .text
+.global __gxx_personality_sj0
+__gxx_personality_sj0:
+
+
+	.section ._flash_d2xx_config
+.global __pD2XXDefaultConfiguration
+	.align (10)
+
+D2XX_partition_start = .
+
+.if IS_IMG_D2XX_PRESENT
+.ifdef __FT930__
+.include "ft930_d2xx_default_config.inc"
+.else
+.include "ft900_d2xx_default_config.inc"
+.endif
+.endif
+
+D2XX_partition_end = .
+
+	.section ._flash_dlog_partition
+	.align (10)
+.global __dlog_partition
+__dlog_partition:
+dlog_partition_start = .
+.if IS_IMG_DLOG_PRESENT
+	.long	0xF7D1D106
+	.rept  (0x1000-4)
+	.byte	0xFF
+	.endr
+.endif
+dlog_partition_end = .
+
+	.section ._pm
+.global	__sdbl_partition_sizeof
+.global __D2XX_partition_sizeof
+.global __dlog_partition_sizeof
+	.if IS_IMG_SDBL_PRESENT
+__sdbl_partition_sizeof = 0x2000
+	.else
+__sdbl_partition_sizeof = 0
+	.endif
+
+__D2XX_partition_sizeof = D2XX_partition_end - D2XX_partition_start
+__dlog_partition_sizeof = dlog_partition_end - dlog_partition_start

+ 94 - 0
hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld

@@ -0,0 +1,94 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-ft32")
+OUTPUT_ARCH(ft32)
+SEARCH_DIR("/data/win8/ft32/ft32-elf/lib");
+/* Allow the command line to override the memory region sizes.  */
+__PMSIZE = DEFINED(__PMSIZE)  ? __PMSIZE : 256K;
+__RAMSIZE = DEFINED(__RAMSIZE) ? __RAMSIZE : 64K;
+MEMORY
+{
+  flash     (rx)   : ORIGIN = 0,        LENGTH = __PMSIZE
+  ram       (rw!x) : ORIGIN = 0x800000, LENGTH = __RAMSIZE
+}
+SECTIONS
+{
+  .text :
+  {
+    *(.text*)
+    *(.strings)
+    *(._pm*)
+    *(.init)
+    *(.fini)
+     _etext = . ;
+    . = ALIGN(4);
+  }  > flash
+  .tors :
+  {
+    ___ctors = . ;
+    *(.ctors)
+    ___ctors_end = . ;
+    ___dtors = . ;
+    *(.dtors)
+    ___dtors_end = . ;
+    . = ALIGN(4);
+  } > ram
+  .data :  AT (ADDR (.text) + SIZEOF (.text))
+  {
+    *(.data)
+    *(.data*)
+    *(.rodata)
+    *(.rodata*)
+     _edata = . ;
+    . = ALIGN(4);
+  }  > ram
+  .bss   SIZEOF(.data) + ADDR(.data) :
+  {
+     _bss_start = . ;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+     _end = . ;
+    . = ALIGN(4);
+  }  > ram
+   __data_load_start = LOADADDR(.data);
+   __data_load_end = __data_load_start + SIZEOF(.data);
+  .stab 0 (NOLOAD) :
+  {
+    *(.stab)
+  }
+  .stabstr 0 (NOLOAD) :
+  {
+    *(.stabstr)
+  }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end ) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* DWARF 3 */
+  .debug_pubtypes 0 : { *(.debug_pubtypes) }
+  .debug_ranges   0 : { *(.debug_ranges) }
+  /* DWARF Extension.  */
+  .debug_macro    0 : { *(.debug_macro) }
+  .debug_addr     0 : { *(.debug_addr) }
+}

+ 7 - 0
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

+ 1101 - 0
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

+ 4 - 0
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
 //--------------------------------------------------------------------+