2021-10-05 14:54:47 +02:00
/*
* The MIT License ( MIT )
*
2021-10-06 17:54:52 +02:00
* Copyright 2021 Bridgetek Pte Ltd
2021-10-05 14:54:47 +02:00
*
* 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 .
*/
2021-10-07 18:00:28 +02:00
/*
* Contains code adapted from Bridgetek Pte Ltd via license terms stated
* in https : //brtchip.com/BRTSourceCodeLicenseAgreement
*/
2022-01-17 08:50:11 +01:00
# include "tusb_option.h"
2022-02-23 15:46:40 +01:00
# if CFG_TUD_ENABLED && \
2022-01-17 08:50:11 +01:00
( CFG_TUSB_MCU = = OPT_MCU_FT90X | | CFG_TUSB_MCU = = OPT_MCU_FT93X )
2021-10-27 13:30:51 +02:00
# include <stdint.h>
# include <ft900.h>
# include <registers/ft900_registers.h>
2021-10-06 18:13:44 +02:00
# include "board.h"
2021-10-05 14:54:47 +02:00
# include "bsp/board.h"
# define USBD_USE_STREAMS
# 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
{
2021-12-15 13:23:58 +01:00
volatile uint8_t valid ; // Transfer is pending and total_size, remain_size, and buff_ptr are valid.
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.
2021-10-05 14:54:47 +02:00
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 ;
2021-12-15 13:23:58 +01:00
2021-10-05 14:54:47 +02:00
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 ;
}
2021-12-15 13:23:58 +01:00
// Reset all non-control endpoints to a default state.
// Control endpoint is always enabled and ready. All others disabled.
2021-10-05 14:54:47 +02:00
static void _ft90x_reset_edpts ( void )
{
// Disable all endpoints and remove configuration values.
2021-12-15 13:23:58 +01:00
for ( int i = 1 ; i < USBD_MAX_ENDPOINT_COUNT ; i + + )
2021-10-05 14:54:47 +02:00
{
2021-12-15 13:23:58 +01:00
// Clear settings.
tu_memclr ( & ep_xfer [ i ] , sizeof ( struct ft90x_xfer_state ) ) ;
2021-10-05 14:54:47 +02:00
// Disable hardware.
USBD_EP_CR_REG ( i ) = 0 ;
}
// Enable interrupts from USB device control.
USBD_REG ( cmie ) = MASK_USBD_CMIE_ALL ;
}
// 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.
2022-06-01 18:53:40 +02:00
# if BOARD_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED
2021-10-05 14:54:47 +02:00
USBD_REG ( fctrl ) = 0 ;
2022-06-01 18:53:40 +02:00
# else // BOARD_TUD_MAX_SPEED == OPT_MODE_FULL_SPEED
2021-10-05 14:54:47 +02:00
//Set the full speed only bit if required.
USBD_REG ( fctrl ) = MASK_USBD_FCTRL_MODE_FS_ONLY ;
2022-06-01 18:53:40 +02:00
# endif // BOARD_TUD_MAX_SPEED
2021-10-05 14:54:47 +02:00
// 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
2021-12-15 13:23:58 +01:00
// Disable interrupts from USB.
2021-10-05 14:54:47 +02:00
USBD_REG ( epie ) = 0 ;
USBD_REG ( cmie ) = 0 ;
2021-12-15 13:23:58 +01:00
// Turn off the device enable bit.
USBD_REG ( fctrl ) = 0 ;
2021-10-05 14:54:47 +02:00
CRITICAL_SECTION_END ;
delayms ( 1 ) ;
// Disable USB PHY
2022-06-01 18:53:40 +02:00
dcd_disconnect ( BOARD_TUD_RHPORT ) ;
2021-10-05 14:54:47 +02:00
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.
2022-06-06 07:51:10 +02:00
// High speed can be disabled through CFG_TUSB_RHPORT0_MODE or CFG_TUD_MAX_SPEED settings.
2021-10-05 14:54:47 +02:00
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 ) ;
}
2022-06-01 18:53:40 +02:00
# if BOARD_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED
2021-10-05 14:54:47 +02:00
/* 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 ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_reset ( BOARD_TUD_RHPORT , _speed , true ) ;
2021-10-05 14:54:47 +02:00
# endif /* !__FT930__ */
2022-06-01 18:53:40 +02:00
# else // BOARD_TUD_MAX_SPEED == OPT_MODE_FULL_SPEED
2021-10-05 14:54:47 +02:00
/* 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 ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_reset ( BOARD_TUD_RHPORT , _speed , true ) ;
2021-10-05 14:54:47 +02:00
return ;
2022-06-01 18:53:40 +02:00
# endif // BOARD_TUD_MAX_SPEED
2021-10-05 14:54:47 +02:00
}
// 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 ;
2021-12-08 10:34:29 +01:00
data_reg = ( volatile uint8_t * ) & ( USBD - > ep [ ep_number ] . epxfifo ) ;
2021-10-05 14:54:47 +02:00
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
2021-12-08 10:34:29 +01:00
data_reg = ( volatile uint8_t * ) & ( USBD - > ep [ ep_number ] . epxfifo ) ;
2021-10-05 14:54:47 +02:00
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.
2021-10-07 18:00:28 +02:00
dcd_edpt_xfer ( rhport , tu_edpt_addr ( USBD_EP_0 , TUSB_DIR_IN ) , NULL , 0 ) ;
2021-10-05 14:54:47 +02:00
// 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 ;
2022-12-04 07:58:47 +01:00
// At least 2 ms of delay needed for RESUME Data K state.
2021-10-05 14:54:47 +02:00
delayms ( 2 ) ;
SYS - > MSC0CFG & = ~ MASK_SYS_MSC0CFG_DEV_RMWAKEUP ;
// Enable USB PHY and determine current bus speed.
2021-10-07 18:00:28 +02:00
dcd_connect ( rhport ) ;
2021-10-05 14:54:47 +02:00
}
// 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 ( ) ;
}
2021-12-15 13:23:58 +01:00
// 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
CRITICAL_SECTION_END ;
// 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 on EP0.
USBD_REG ( epie ) = ( MASK_USBD_EPIE_EP0IE ) ;
2021-10-05 14:54:47 +02:00
// 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 ) ;
}
2022-03-07 17:05:05 +01:00
void dcd_sof_enable ( uint8_t rhport , bool en )
{
( void ) rhport ;
( void ) en ;
// TODO implement later
}
2021-10-05 14:54:47 +02:00
//--------------------------------------------------------------------+
// 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 ;
2021-12-08 10:34:29 +01:00
uint16_t const ep_size = tu_edpt_packet_size ( ep_desc ) ; // Mask size per packet, bits 10..0.
2021-10-05 14:54:47 +02:00
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.
2021-12-08 10:34:29 +01:00
while ( ep_size > ( 8 * ( 1 < < ep_reg_size ) ) )
2021-10-05 14:54:47 +02:00
{
ep_reg_size + + ;
}
if ( ep_reg_size > USBD_EP_MAX_SIZE_1024 )
{
2021-12-08 10:34:29 +01:00
TU_LOG1 ( " FT90x endpoint size not valid: requested %d max 1024 \r \n " , ep_size ) ;
2021-10-05 14:54:47 +02:00
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.
2022-12-04 07:58:47 +01:00
// The total RAM allocated should always be a positive number as this
2021-10-05 14:54:47 +02:00
// algorithm should not let it go below zero.
for ( int i = 1 ; i < USBD_MAX_ENDPOINT_COUNT ; i + + )
{
if ( ep_xfer [ i ] . type ! = USBD_EP_TYPE_DISABLED )
{
if ( ep_xfer [ i ] . dir = = ep_dir )
{
total_ram - = ep_xfer [ i ] . buff_size ;
}
}
}
// The control endpoint is taken into account as well.
total_ram - = ep_xfer [ 0 ] . buff_size ;
// Make sure we have enough space. The corner case is having zero bytes
// free which means that total_ram must be signed as zero bytes free is
// allowable.
if ( total_ram < ep_buff_size )
{
TU_LOG1 ( " FT90x insufficient buffer RAM for endpoint %d \r \n " , ep_number ) ;
return false ;
}
// Set the type of this endpoint in the control register.
if ( ep_type = = USBD_EP_BULK )
ep_reg_data | = ( USBD_EP_DIS_BULK < < BIT_USBD_EP_CONTROL_DIS ) ;
else if ( ep_type = = USBD_EP_INT )
ep_reg_data | = ( USBD_EP_DIS_INT < < BIT_USBD_EP_CONTROL_DIS ) ;
else if ( ep_type = = USBD_EP_ISOC )
ep_reg_data | = ( USBD_EP_DIS_ISO < < BIT_USBD_EP_CONTROL_DIS ) ;
// Set the direction of this endpoint in the control register.
if ( ep_dir = = USBD_DIR_IN )
ep_reg_data | = MASK_USBD_EPxCR_DIR ;
// Do not perform double buffering.
//if (<double buffering flag> != USBD_DB_OFF)
//ep_reg_data |= MASK_USBD_EPxCR_DB;
// Set the control endpoint for this endpoint.
USBD_EP_CR_REG ( ep_number ) = ep_reg_data ;
TU_LOG2 ( " FT90x endpoint setting %x \r \n " , ep_reg_data ) ;
}
else
{
// Set the control register for endpoint zero.
USBD_EP_CR_REG ( USBD_EP_0 ) = ( ep_reg_size < < BIT_USBD_EP0_MAX_SIZE ) ;
}
// Store the endpoint characteristics for later reference.
ep_xfer [ ep_number ] . dir = ep_dir ;
ep_xfer [ ep_number ] . type = ep_type ;
ep_xfer [ ep_number ] . size = ep_size ;
ep_xfer [ ep_number ] . buff_size = ep_buff_size ;
CRITICAL_SECTION_BEGIN
// Clear register transaction continuation and signalling state.
ep_xfer [ ep_number ] . valid = 0 ;
ep_xfer [ ep_number ] . buff_ptr = NULL ;
ep_xfer [ ep_number ] . total_size = 0 ;
ep_xfer [ ep_number ] . remain_size = 0 ;
CRITICAL_SECTION_END
return true ;
}
// Close all endpoints.
void dcd_edpt_close_all ( uint8_t rhport )
{
( void ) rhport ;
// Reset the endpoint configurations.
_ft90x_reset_edpts ( ) ;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer ( uint8_t rhport , uint8_t ep_addr , uint8_t * buffer , uint16_t total_bytes )
{
( void ) rhport ;
uint8_t ep_number = tu_edpt_number ( ep_addr ) ;
uint8_t dir = tu_edpt_dir ( ep_addr ) ;
uint16_t xfer_bytes ;
bool status = false ;
// We will attempt to transfer the buffer. If it is less than or equal to the endpoint
// maximum packet size then the whole buffer will be transferred. If it is larger then
// the interrupt handler will transfer the remainder.
// ep_xfer is used to tell the interrupt handler what to do.
// ep_xfer can be used at interrupt level to continue transfers.
CRITICAL_SECTION_BEGIN
// Transfer currently in progress.
if ( ep_xfer [ ep_number ] . valid = = 0 )
{
status = true ;
ep_xfer [ ep_number ] . total_size = total_bytes ;
ep_xfer [ ep_number ] . remain_size = total_bytes ;
ep_xfer [ ep_number ] . buff_ptr = buffer ;
ep_xfer [ ep_number ] . valid = 1 ;
if ( ep_number = = USBD_EP_0 )
{
ep_xfer [ USBD_EP_0 ] . dir = dir ;
}
else
{
// Enable the interrupt for this endpoint allowing the interrupt handler to report
// continue the transfer and signal completion.
USBD_REG ( epie ) = USBD_REG ( epie ) | ( 1 < < ep_number ) ;
}
if ( dir = = TUSB_DIR_IN )
{
// For IN transfers send the first packet as a starter. Interrupt handler to complete
// this if it is larger than one packet.
xfer_bytes = _ft90x_edpt_xfer_in ( ep_number , buffer , total_bytes ) ;
ep_xfer [ ep_number ] . buff_ptr + = xfer_bytes ;
ep_xfer [ ep_number ] . remain_size - = xfer_bytes ;
}
}
CRITICAL_SECTION_END
return status ;
}
// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
bool dcd_edpt_xfer_fifo ( uint8_t rhport , uint8_t ep_addr , tu_fifo_t * ff , uint16_t total_bytes )
{
( void ) rhport ;
( void ) ep_addr ;
( void ) ff ;
( void ) total_bytes ;
bool status = false ;
return status ;
}
// Stall endpoint (non-control endpoint)
void dcd_edpt_stall ( uint8_t rhport , uint8_t ep_addr )
{
uint8_t ep_number = tu_edpt_number ( ep_addr ) ;
( void ) rhport ;
CRITICAL_SECTION_BEGIN
if ( ep_number = = USBD_EP_0 )
{
USBD_EP_CR_REG ( USBD_EP_0 ) = USBD_EP_CR_REG ( USBD_EP_0 ) |
MASK_USBD_EP0CR_SDSTL ;
}
else
{
USBD_EP_CR_REG ( ep_number ) = USBD_EP_CR_REG ( ep_number ) |
MASK_USBD_EPxCR_SDSTL ;
USBD_EP_SR_REG ( ep_number ) = MASK_USBD_EPxSR_CLR_TOGGLE |
MASK_USBD_EPxSR_FIFO_FLUSH ;
}
CRITICAL_SECTION_END
}
// Clear stall (non-control endpoint), data toggle is also reset to DATA0
void dcd_edpt_clear_stall ( uint8_t rhport , uint8_t ep_addr )
{
uint8_t ep_number = tu_edpt_number ( ep_addr ) ;
( void ) rhport ;
if ( ep_number > USBD_EP_0 )
{
CRITICAL_SECTION_BEGIN
USBD_EP_CR_REG ( ep_number ) = USBD_EP_CR_REG ( ep_number ) &
( ~ MASK_USBD_EPxCR_SDSTL ) ;
USBD_EP_SR_REG ( ep_number ) = MASK_USBD_EPxSR_CLR_TOGGLE ;
// Allow transfers to restart.
ep_xfer [ ep_number ] . valid = 0 ;
ep_xfer [ ep_number ] . remain_size = 0 ;
CRITICAL_SECTION_END
}
}
// Interrupt handling.
void _ft90x_usbd_ISR ( void )
{
2022-06-01 18:53:40 +02:00
tud_int_handler ( BOARD_TUD_RHPORT ) ; // Resolves to dcd_int_handler().
2021-10-05 14:54:47 +02:00
}
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 ( ) ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_reset ( BOARD_TUD_RHPORT , _speed , true ) ;
2021-10-05 14:54:47 +02:00
}
if ( cmif & MASK_USBD_CMIF_SUSIRQ ) //Handle Suspend interrupt
{
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_SUSPEND , true ) ;
2021-10-05 14:54:47 +02:00
}
if ( cmif & MASK_USBD_CMIF_RESIRQ ) //Handle Resume interrupt
{
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_RESUME , true ) ;
2021-10-05 14:54:47 +02:00
}
if ( cmif & MASK_USBD_CMIF_SOFIRQ ) //Handle SOF interrupt
{
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_SOF , true ) ;
2021-10-05 14:54:47 +02:00
}
}
// 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 ;
}
2022-12-04 07:58:47 +01:00
// Host has sent a SETUP packet. Receive this into the setup packet store.
2021-10-05 14:54:47 +02:00
_ft90x_dusb_out ( USBD_EP_0 , ( uint8_t * ) _ft90x_setup_packet , sizeof ( USB_device_request ) ) ;
// Send the packet to tinyusb.
2022-06-01 18:53:40 +02:00
dcd_event_setup_received ( BOARD_TUD_RHPORT , _ft90x_setup_packet , true ) ;
2021-10-05 14:54:47 +02:00
// 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.
2022-06-01 18:53:40 +02:00
dcd_event_xfer_complete ( BOARD_TUD_RHPORT , ( ep_xfer [ USBD_EP_0 ] . dir ? TUSB_DIR_IN_MASK : 0 ) , xfer_bytes , XFER_RESULT_SUCCESS , true ) ;
2021-10-05 14:54:47 +02:00
// 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.
2022-06-01 18:53:40 +02:00
dcd_event_xfer_complete ( BOARD_TUD_RHPORT , ep_number | ep_dirmask , ep_xfer [ ep_number ] . total_size , XFER_RESULT_SUCCESS , true ) ;
2021-10-05 14:54:47 +02:00
// 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 ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_RESUME , true ) ;
2021-10-05 14:54:47 +02:00
}
if ( pmcfg & MASK_SYS_PMCFG_DEV_DIS_DEV )
{
// Signal disconnection interrupt
SYS - > PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_UNPLUGGED , true ) ;
2021-10-05 14:54:47 +02:00
}
if ( pmcfg & MASK_SYS_PMCFG_HOST_RST_DEV )
{
// Signal Host Reset interrupt
SYS - > PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND ;
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_BUS_RESET , true ) ;
2021-10-05 14:54:47 +02:00
}
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
2022-06-01 18:53:40 +02:00
dcd_event_bus_signal ( BOARD_TUD_RHPORT , DCD_EVENT_RESUME , true ) ;
2021-10-05 14:54:47 +02:00
}
}
}
# endif