From 37c5eeb51de532dfbd46ae528d4d99220932d6bd Mon Sep 17 00:00:00 2001 From: Gordon McNab Date: Wed, 6 Oct 2021 16:50:55 +0100 Subject: Rename directories to indicate that FT930 and FT900 devices are both covered by the same SDK and src/portable code. Board makefile includes __FT900__ macro for FT90x and __FT930__ macro for boards with FT93x. --- hw/bsp/brtmm90x/family.c | 6 +- hw/bsp/brtmm90x/family.mk | 14 +- hw/mcu/bridgetek/ft90x/Readme.md | 6 - hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S | 286 ----- .../bridgetek/ft90x/hardware/scripts/ldscript.ld | 94 -- hw/mcu/bridgetek/ft9xx/Readme.md | 7 + hw/mcu/bridgetek/ft9xx/hardware/scripts/crt0.S | 286 +++++ .../bridgetek/ft9xx/hardware/scripts/ldscript.ld | 94 ++ src/portable/bridgetek/ft90x/dcd_ft90x.c | 1101 -------------------- src/portable/bridgetek/ft9xx/dcd_ft9xx.c | 1099 +++++++++++++++++++ 10 files changed, 1496 insertions(+), 1497 deletions(-) delete mode 100644 hw/mcu/bridgetek/ft90x/Readme.md delete mode 100644 hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S delete mode 100644 hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld create mode 100644 hw/mcu/bridgetek/ft9xx/Readme.md create mode 100644 hw/mcu/bridgetek/ft9xx/hardware/scripts/crt0.S create mode 100644 hw/mcu/bridgetek/ft9xx/hardware/scripts/ldscript.ld delete mode 100644 src/portable/bridgetek/ft90x/dcd_ft90x.c create mode 100644 src/portable/bridgetek/ft9xx/dcd_ft9xx.c diff --git a/hw/bsp/brtmm90x/family.c b/hw/bsp/brtmm90x/family.c index 010704d92..6352f2324 100644 --- a/hw/bsp/brtmm90x/family.c +++ b/hw/bsp/brtmm90x/family.c @@ -28,7 +28,6 @@ #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. @@ -67,16 +66,17 @@ void board_init(void) #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_function(GPIO_ETH_LED1, pad_gpio5); /* ETH LED1 */ 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); + interrupt_attach(interrupt_timers, (int8_t)interrupt_timers, timer_ISR); // Setup VBUS detect GPIO. If the device is connected then this // will set the MASK_SYS_PMCFG_DEV_DETECT_EN bit in PMCFG. diff --git a/hw/bsp/brtmm90x/family.mk b/hw/bsp/brtmm90x/family.mk index 57a141506..42769c254 100644 --- a/hw/bsp/brtmm90x/family.mk +++ b/hw/bsp/brtmm90x/family.mk @@ -1,10 +1,10 @@ CROSS_COMPILE = ft32-elf- -DEPS_SUBMODULES += hw/mcu/bridgetek/ft90x/hardware +DEPS_SUBMODULES += hw/mcu/bridgetek/ft9xx/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 +FT9XX_SDK = $(TOP)/hw/mcu/bridgetek/ft9xx/hardware CFLAGS += \ -D__FT900__ \ @@ -19,17 +19,17 @@ 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 +LDINC += $(FT9XX_SDK)/lib/Release LIBS += -lft900 -LD_FILE = hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld +LD_FILE = hw/mcu/bridgetek/ft9xx/hardware/scripts/ldscript.ld LDFLAGS += $(addprefix -L,$(LDINC)) \ -Xlinker --entry=_start \ -Wl,-lc -SRC_C += src/portable/bridgetek/ft90x/dcd_ft90x.c +SRC_C += src/portable/bridgetek/ft9xx/dcd_ft9xx.c -#SRC_S += hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S +#SRC_S += hw/mcu/bridgetek/ft9xx/hardware/scripts/crt0.S INC += \ - $(FT90X_SDK)/include \ + $(FT9XX_SDK)/include \ $(TOP)/$(BOARD_PATH) diff --git a/hw/mcu/bridgetek/ft90x/Readme.md b/hw/mcu/bridgetek/ft90x/Readme.md deleted file mode 100644 index e197f182e..000000000 --- a/hw/mcu/bridgetek/ft90x/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# 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 deleted file mode 100644 index 62fa266ee..000000000 --- a/hw/mcu/bridgetek/ft90x/hardware/scripts/crt0.S +++ /dev/null @@ -1,286 +0,0 @@ -.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 deleted file mode 100644 index d1b4d46cd..000000000 --- a/hw/mcu/bridgetek/ft90x/hardware/scripts/ldscript.ld +++ /dev/null @@ -1,94 +0,0 @@ -/* 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/hw/mcu/bridgetek/ft9xx/Readme.md b/hw/mcu/bridgetek/ft9xx/Readme.md new file mode 100644 index 000000000..1db91058c --- /dev/null +++ b/hw/mcu/bridgetek/ft9xx/Readme.md @@ -0,0 +1,7 @@ +# BridgeTek FT9xx MCU + +**BridgeTek** provides a hardware abstraction library with software source code for the SDKs for FT9xx software family. + +Whole SDK repository is installed as part of the FT9xx Toolchain and can be downloaded from BridgeTek web page `https://www.brtchip.com`. + +Registers definition files, and included peripheral register definition files have licenses that allow for redistribution. diff --git a/hw/mcu/bridgetek/ft9xx/hardware/scripts/crt0.S b/hw/mcu/bridgetek/ft9xx/hardware/scripts/crt0.S new file mode 100644 index 000000000..62fa266ee --- /dev/null +++ b/hw/mcu/bridgetek/ft9xx/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/ft9xx/hardware/scripts/ldscript.ld b/hw/mcu/bridgetek/ft9xx/hardware/scripts/ldscript.ld new file mode 100644 index 000000000..d1b4d46cd --- /dev/null +++ b/hw/mcu/bridgetek/ft9xx/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/portable/bridgetek/ft90x/dcd_ft90x.c b/src/portable/bridgetek/ft90x/dcd_ft90x.c deleted file mode 100644 index ac13f11d1..000000000 --- a/src/portable/bridgetek/ft90x/dcd_ft90x.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * 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/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c new file mode 100644 index 000000000..69b51fac7 --- /dev/null +++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c @@ -0,0 +1,1099 @@ +/* + * 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(); + + 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 -- cgit v1.3.1