1111 lines
33 KiB
C
1111 lines
33 KiB
C
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright 2021 Bridgetek Pte Ltd
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Contains code adapted from Bridgetek Pte Ltd via license terms stated
|
|
* in https://brtchip.com/BRTSourceCodeLicenseAgreement
|
|
*/
|
|
|
|
#include "tusb_option.h"
|
|
|
|
#if CFG_TUD_ENABLED && \
|
|
(CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X)
|
|
|
|
#include <stdint.h>
|
|
#include <ft900.h>
|
|
#include <registers/ft900_registers.h>
|
|
|
|
#include "board.h"
|
|
#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
|
|
{
|
|
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.
|
|
|
|
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 non-control endpoints to a default state.
|
|
// Control endpoint is always enabled and ready. All others disabled.
|
|
static void _ft90x_reset_edpts(void)
|
|
{
|
|
// Disable all endpoints and remove configuration values.
|
|
for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
|
|
{
|
|
// Clear settings.
|
|
tu_memclr(&ep_xfer[i], sizeof(struct ft90x_xfer_state));
|
|
// 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.
|
|
#if BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_HIGH_SPEED
|
|
USBD_REG(fctrl) = 0;
|
|
#else // BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_FULL_SPEED
|
|
//Set the full speed only bit if required.
|
|
USBD_REG(fctrl) = MASK_USBD_FCTRL_MODE_FS_ONLY;
|
|
#endif // BOARD_DEVICE_RHPORT_SPEED
|
|
|
|
// 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
|
|
// Disable interrupts from USB.
|
|
USBD_REG(epie) = 0;
|
|
USBD_REG(cmie) = 0;
|
|
// Turn off the device enable bit.
|
|
USBD_REG(fctrl) = 0;
|
|
CRITICAL_SECTION_END;
|
|
|
|
delayms(1);
|
|
|
|
// Disable USB PHY
|
|
dcd_disconnect(BOARD_DEVICE_RHPORT_NUM);
|
|
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 BOARD_DEVICE_RHPORT_SPEED == 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(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
|
|
|
#endif /* !__FT930__ */
|
|
|
|
#else // BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_FULL_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(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
|
return;
|
|
|
|
#endif // BOARD_DEVICE_RHPORT_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 = (volatile 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 = (volatile 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(USBD_EP_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(rhport);
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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 = tu_edpt_packet_size(ep_desc); // Mask size per packet, bits 10..0.
|
|
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_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_size);
|
|
return false;
|
|
}
|
|
// Calculate actual amount of buffer RAM used by this endpoint. This may be more than the
|
|
// requested size.
|
|
ep_buff_size = 8 << ep_reg_size;
|
|
|
|
if (ep_number > 0)
|
|
{
|
|
// Set EP cmd parameters...
|
|
ep_reg_data |= (ep_reg_size << BIT_USBD_EP_MAX_SIZE);
|
|
|
|
if (ep_xfer[ep_number].type != USBD_EP_TYPE_DISABLED)
|
|
{
|
|
// This could be because an endpoint has been assigned with the same number.
|
|
// On FT90x, IN and OUT endpoints may not have the same number. e.g. There
|
|
// cannot been an 0x81 and 0x01 endpoint.
|
|
TU_LOG1("FT90x endpoint %d already assigned\r\n", ep_number);
|
|
return false;
|
|
}
|
|
|
|
// Check that there is enough buffer RAM to allocate to this new endpoint.
|
|
// Available buffer RAM depends on the device revision.
|
|
// The IN and OUT buffer RAM should be the same size.
|
|
if (ep_dir == USBD_DIR_IN)
|
|
total_ram = USBD_RAMTOTAL_IN;
|
|
else
|
|
total_ram = USBD_RAMTOTAL_OUT;
|
|
// Work out how much has been allocated to existing endpoints.
|
|
// The total RAM allocated shoudl alsyes be a positive number as this
|
|
// algorithm should not let it go below zero.
|
|
for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
|
|
{
|
|
if (ep_xfer[i].type != USBD_EP_TYPE_DISABLED)
|
|
{
|
|
if (ep_xfer[i].dir == ep_dir)
|
|
{
|
|
total_ram -= ep_xfer[i].buff_size;
|
|
}
|
|
}
|
|
}
|
|
// The control endpoint is taken into account as well.
|
|
total_ram -= ep_xfer[0].buff_size;
|
|
// Make sure we have enough space. The corner case is having zero bytes
|
|
// free which means that total_ram must be signed as zero bytes free is
|
|
// allowable.
|
|
if (total_ram < ep_buff_size)
|
|
{
|
|
TU_LOG1("FT90x insufficient buffer RAM for endpoint %d\r\n", ep_number);
|
|
return false;
|
|
}
|
|
|
|
// Set the type of this endpoint in the control register.
|
|
if (ep_type == USBD_EP_BULK)
|
|
ep_reg_data |= (USBD_EP_DIS_BULK << BIT_USBD_EP_CONTROL_DIS);
|
|
else if (ep_type == USBD_EP_INT)
|
|
ep_reg_data |= (USBD_EP_DIS_INT << BIT_USBD_EP_CONTROL_DIS);
|
|
else if (ep_type == USBD_EP_ISOC)
|
|
ep_reg_data |= (USBD_EP_DIS_ISO << BIT_USBD_EP_CONTROL_DIS);
|
|
// Set the direction of this endpoint in the control register.
|
|
if (ep_dir == USBD_DIR_IN)
|
|
ep_reg_data |= MASK_USBD_EPxCR_DIR;
|
|
// Do not perform double buffering.
|
|
//if (<double buffering flag> != USBD_DB_OFF)
|
|
//ep_reg_data |= MASK_USBD_EPxCR_DB;
|
|
// Set the control endpoint for this endpoint.
|
|
USBD_EP_CR_REG(ep_number) = ep_reg_data;
|
|
TU_LOG2("FT90x endpoint setting %x\r\n", ep_reg_data);
|
|
}
|
|
else
|
|
{
|
|
// Set the control register for endpoint zero.
|
|
USBD_EP_CR_REG(USBD_EP_0) = (ep_reg_size << BIT_USBD_EP0_MAX_SIZE);
|
|
}
|
|
|
|
// Store the endpoint characteristics for later reference.
|
|
ep_xfer[ep_number].dir = ep_dir;
|
|
ep_xfer[ep_number].type = ep_type;
|
|
ep_xfer[ep_number].size = ep_size;
|
|
ep_xfer[ep_number].buff_size = ep_buff_size;
|
|
|
|
CRITICAL_SECTION_BEGIN
|
|
// Clear register transaction continuation and signalling state.
|
|
ep_xfer[ep_number].valid = 0;
|
|
ep_xfer[ep_number].buff_ptr = NULL;
|
|
ep_xfer[ep_number].total_size = 0;
|
|
ep_xfer[ep_number].remain_size = 0;
|
|
CRITICAL_SECTION_END
|
|
|
|
return true;
|
|
}
|
|
|
|
// Close all endpoints.
|
|
void dcd_edpt_close_all(uint8_t rhport)
|
|
{
|
|
(void)rhport;
|
|
// Reset the endpoint configurations.
|
|
_ft90x_reset_edpts();
|
|
}
|
|
|
|
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
|
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
|
|
{
|
|
(void)rhport;
|
|
uint8_t ep_number = tu_edpt_number(ep_addr);
|
|
uint8_t dir = tu_edpt_dir(ep_addr);
|
|
uint16_t xfer_bytes;
|
|
bool status = false;
|
|
|
|
// We will attempt to transfer the buffer. If it is less than or equal to the endpoint
|
|
// maximum packet size then the whole buffer will be transferred. If it is larger then
|
|
// the interrupt handler will transfer the remainder.
|
|
// ep_xfer is used to tell the interrupt handler what to do.
|
|
// ep_xfer can be used at interrupt level to continue transfers.
|
|
CRITICAL_SECTION_BEGIN
|
|
// Transfer currently in progress.
|
|
if (ep_xfer[ep_number].valid == 0)
|
|
{
|
|
status = true;
|
|
|
|
ep_xfer[ep_number].total_size = total_bytes;
|
|
ep_xfer[ep_number].remain_size = total_bytes;
|
|
ep_xfer[ep_number].buff_ptr = buffer;
|
|
ep_xfer[ep_number].valid = 1;
|
|
|
|
if (ep_number == USBD_EP_0)
|
|
{
|
|
ep_xfer[USBD_EP_0].dir = dir;
|
|
}
|
|
else
|
|
{
|
|
// Enable the interrupt for this endpoint allowing the interrupt handler to report
|
|
// continue the transfer and signal completion.
|
|
USBD_REG(epie) = USBD_REG(epie) | (1 << ep_number);
|
|
}
|
|
|
|
if (dir == TUSB_DIR_IN)
|
|
{
|
|
// For IN transfers send the first packet as a starter. Interrupt handler to complete
|
|
// this if it is larger than one packet.
|
|
xfer_bytes = _ft90x_edpt_xfer_in(ep_number, buffer, total_bytes);
|
|
|
|
ep_xfer[ep_number].buff_ptr += xfer_bytes;
|
|
ep_xfer[ep_number].remain_size -= xfer_bytes;
|
|
}
|
|
}
|
|
CRITICAL_SECTION_END
|
|
|
|
return status;
|
|
}
|
|
|
|
// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
|
|
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes)
|
|
{
|
|
(void)rhport;
|
|
(void)ep_addr;
|
|
(void)ff;
|
|
(void)total_bytes;
|
|
bool status = false;
|
|
return status;
|
|
}
|
|
|
|
// Stall endpoint (non-control endpoint)
|
|
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
|
{
|
|
uint8_t ep_number = tu_edpt_number(ep_addr);
|
|
(void)rhport;
|
|
|
|
CRITICAL_SECTION_BEGIN
|
|
if (ep_number == USBD_EP_0)
|
|
{
|
|
USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) |
|
|
MASK_USBD_EP0CR_SDSTL;
|
|
}
|
|
else
|
|
{
|
|
USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) |
|
|
MASK_USBD_EPxCR_SDSTL;
|
|
USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE |
|
|
MASK_USBD_EPxSR_FIFO_FLUSH;
|
|
}
|
|
CRITICAL_SECTION_END
|
|
}
|
|
|
|
// Clear stall (non-control endpoint), data toggle is also reset to DATA0
|
|
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
|
{
|
|
uint8_t ep_number = tu_edpt_number(ep_addr);
|
|
(void)rhport;
|
|
|
|
if (ep_number > USBD_EP_0)
|
|
{
|
|
CRITICAL_SECTION_BEGIN
|
|
USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) &
|
|
(~MASK_USBD_EPxCR_SDSTL);
|
|
USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE;
|
|
|
|
// Allow transfers to restart.
|
|
ep_xfer[ep_number].valid = 0;
|
|
ep_xfer[ep_number].remain_size = 0;
|
|
CRITICAL_SECTION_END
|
|
}
|
|
}
|
|
|
|
// Interrupt handling.
|
|
|
|
void _ft90x_usbd_ISR(void)
|
|
{
|
|
tud_int_handler(BOARD_DEVICE_RHPORT_NUM); // 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(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
|
}
|
|
if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt
|
|
{
|
|
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_SUSPEND, true);
|
|
}
|
|
if (cmif & MASK_USBD_CMIF_RESIRQ) //Handle Resume interrupt
|
|
{
|
|
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_RESUME, true);
|
|
}
|
|
if (cmif & MASK_USBD_CMIF_SOFIRQ) //Handle SOF interrupt
|
|
{
|
|
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, 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(BOARD_DEVICE_RHPORT_NUM, _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(BOARD_DEVICE_RHPORT_NUM, (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(BOARD_DEVICE_RHPORT_NUM, 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(BOARD_DEVICE_RHPORT_NUM, 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(BOARD_DEVICE_RHPORT_NUM, 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(BOARD_DEVICE_RHPORT_NUM, 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(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_RESUME, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|