diff --git a/hw/bsp/brtmm90x/boards/mm900ev1b/board.h b/hw/bsp/brtmm90x/boards/mm900ev1b/board.h new file mode 100644 index 000000000..dfb566289 --- /dev/null +++ b/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 diff --git a/hw/bsp/brtmm90x/family.c b/hw/bsp/brtmm90x/family.c new file mode 100644 index 000000000..010704d92 --- /dev/null +++ b/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 +#include +#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; +} diff --git a/hw/bsp/brtmm90x/family.mk b/hw/bsp/brtmm90x/family.mk new file mode 100644 index 000000000..57a141506 --- /dev/null +++ b/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) diff --git a/hw/mcu/bridgetek/ft90x/Readme.md b/hw/mcu/bridgetek/ft90x/Readme.md new file mode 100644 index 000000000..e197f182e --- /dev/null +++ b/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 `/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` diff --git a/hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S b/hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S new file mode 100644 index 000000000..62fa266ee --- /dev/null +++ b/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 diff --git a/hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld b/hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld new file mode 100644 index 000000000..d1b4d46cd --- /dev/null +++ b/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) } +} 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 +#include +#include +#include + +#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 ( != 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 //--------------------------------------------------------------------+