update to dcd ip3511 to add work-around for lpc54628 usb hs errata USB.1 and USB.2

msc is mounted, but device couldn't work reliably and got constant reset
due to other errata probably.
This commit is contained in:
hathach 2023-08-08 21:59:36 +07:00
parent 6d877c3170
commit b8b01c1075
No known key found for this signature in database
GPG Key ID: F5D50C6D51D17CBA
6 changed files with 169 additions and 119 deletions

4
.idea/cmake.xml generated
View File

@ -30,7 +30,8 @@
<configuration PROFILE_NAME="mcb1800" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mcb1800 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" /> <configuration PROFILE_NAME="mcb1800" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mcb1800 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="ea4088 quickstart" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ea4088_quickstart -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" /> <configuration PROFILE_NAME="ea4088 quickstart" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ea4088_quickstart -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="ea4357" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ea4357 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" /> <configuration PROFILE_NAME="ea4357" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ea4357 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="lpc55s69" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=lpcxpresso55s69" /> <configuration PROFILE_NAME="lpc54628" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=lpcxpresso54628 -DLOG=4 -DLOGGER=RTT" />
<configuration PROFILE_NAME="lpc55s69" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=lpcxpresso55s69 -DLOG=4 -DLOGGER=RTT" />
<configuration PROFILE_NAME="mcxn947" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mcxn947brk -DLOG=3 -DLOGGER=RTT" /> <configuration PROFILE_NAME="mcxn947" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mcxn947brk -DLOG=3 -DLOGGER=RTT" />
<configuration PROFILE_NAME="pca10056" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pca10056 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" /> <configuration PROFILE_NAME="pca10056" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pca10056 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="pca10095" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pca10095 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" /> <configuration PROFILE_NAME="pca10095" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pca10095 -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1" />
@ -51,6 +52,7 @@
<configuration PROFILE_NAME="ra6m5 PORT0" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ra6m5_ek -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1 -DPORT=0" /> <configuration PROFILE_NAME="ra6m5 PORT0" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ra6m5_ek -DLOG=3 -DLOGGER=RTT -DTRACE_ETM=1 -DPORT=0" />
<configuration PROFILE_NAME="uno_r4" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=uno_r4 -DLOG=4 -DLOGGER=RTT" /> <configuration PROFILE_NAME="uno_r4" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=uno_r4 -DLOG=4 -DLOGGER=RTT" />
<configuration PROFILE_NAME="portenta_c33" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=portenta_c33 -DLOG=3" /> <configuration PROFILE_NAME="portenta_c33" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=portenta_c33 -DLOG=3" />
<configuration PROFILE_NAME="lpc54628 PORT0" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=lpcxpresso54628 -DLOG=4 -DLOGGER=RTT -DPORT=0" />
</configurations> </configurations>
</component> </component>
</project> </project>

View File

@ -70,13 +70,11 @@
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// Forward USB interrupt events to TinyUSB IRQ Handler // Forward USB interrupt events to TinyUSB IRQ Handler
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
void USB0_IRQHandler(void) void USB0_IRQHandler(void) {
{
tud_int_handler(0); tud_int_handler(0);
} }
void USB1_IRQHandler(void) void USB1_IRQHandler(void) {
{
tud_int_handler(1); tud_int_handler(1);
} }

View File

@ -59,6 +59,7 @@ function(add_board_target BOARD_TARGET)
BOARD_TUD_MAX_SPEED=OPT_MODE_FULL_SPEED BOARD_TUD_MAX_SPEED=OPT_MODE_FULL_SPEED
BOARD_TUH_MAX_SPEED=OPT_MODE_HIGH_SPEED BOARD_TUH_MAX_SPEED=OPT_MODE_HIGH_SPEED
CFG_TUH_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\) CFG_TUH_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
#CFG_TUD_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
) )
endif () endif ()

View File

@ -159,11 +159,12 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) {
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
//------------- Align -------------// //------------- Align -------------//
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) {
{
return value & ((uint32_t) ~(alignment-1)); return value & ((uint32_t) ~(alignment-1));
} }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }

View File

@ -58,6 +58,7 @@
// NXP // NXP
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) #if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
#define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 5 #define TUP_DCD_ENDPOINT_MAX 5
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) #elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
@ -66,14 +67,17 @@
#define TUP_OHCI_RHPORTS 2 #define TUP_OHCI_RHPORTS 2
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX) #elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
#define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 5 #define TUP_DCD_ENDPOINT_MAX 5
#elif TU_CHECK_MCU(OPT_MCU_LPC54XXX) #elif TU_CHECK_MCU(OPT_MCU_LPC54)
// TODO USB0 has 5, USB1 has 6 // TODO USB0 has 5, USB1 has 6
#define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 6 #define TUP_DCD_ENDPOINT_MAX 6
#elif TU_CHECK_MCU(OPT_MCU_LPC55XX) #elif TU_CHECK_MCU(OPT_MCU_LPC55)
// TODO USB0 has 5, USB1 has 6 // TODO USB0 has 5, USB1 has 6
#define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 6 #define TUP_DCD_ENDPOINT_MAX 6
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)

View File

@ -34,18 +34,13 @@
* - LPC54114 * - LPC54114
* - LPC55s69 * - LPC55s69
*/ */
#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_LPC11UXX || \ #if CFG_TUD_ENABLED && defined(TUP_USBIP_IP3511)
CFG_TUSB_MCU == OPT_MCU_LPC13XX || \
CFG_TUSB_MCU == OPT_MCU_LPC15XX || \
CFG_TUSB_MCU == OPT_MCU_LPC51UXX || \
CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
CFG_TUSB_MCU == OPT_MCU_LPC55XX)
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// INCLUDE // INCLUDE
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
#if CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX || CFG_TUSB_MCU == OPT_MCU_LPC15XX #if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
// LPCOpen // LPCOpen
#include "chip.h" #include "chip.h"
#else #else
@ -80,7 +75,7 @@ typedef struct {
enum { enum {
NBYTES_ISO_FS_MAX = 1023, // FS ISO NBYTES_ISO_FS_MAX = 1023, // FS ISO
NBYTES_ISO_HS_MAX = 1024, // HS ISO NBYTES_ISO_HS_MAX = 1024, // HS ISO
NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt. TODO some FS can do burst with higher size e.g 1024. Need to test
NBYTES_CBI_HS_MAX = 32767 // can be up to all 15-bit, but only tested with 4096 NBYTES_CBI_HS_MAX = 32767 // can be up to all 15-bit, but only tested with 4096
}; };
@ -90,26 +85,36 @@ enum {
}; };
enum { enum {
CMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1, DEVCMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
CMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ), DEVCMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
CMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ), DEVCMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
CMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), // reflect the soft-connect only, does not reflect the actual attached state DEVCMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), // reflect the soft-connect only, does not reflect the actual attached state
CMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17), DEVCMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
// 23-22 is link speed (only available for HighSpeed port) // 23-22 is link speed (only available for HighSpeed port)
CMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24), DEVCMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
CMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25), DEVCMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
CMDSTAT_RESET_CHANGE_MASK = TU_BIT(26), DEVCMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
CMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28), DEVCMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
}; };
enum { enum {
CMDSTAT_SPEED_SHIFT = 22 DEVCMDSTAT_SPEED_SHIFT = 22
}; };
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// Endpoint Command/Status List // Endpoint Command/Status List
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// EP Command/Status field definition
enum {
EPCS_TYPE = TU_BIT(26),
EPCS_RF_TV = TU_BIT(27),
EPCS_TOGGLE_RESET = TU_BIT(28),
EPCS_STALL = TU_BIT(29),
EPCS_DISABLED = TU_BIT(30),
EPCS_ACTIVE = TU_BIT(31),
};
// Endpoint Command/Status // Endpoint Command/Status
typedef union TU_ATTR_PACKED typedef union TU_ATTR_PACKED
{ {
@ -133,8 +138,8 @@ typedef union TU_ATTR_PACKED
volatile struct { volatile struct {
uint32_t TU_RESERVED : 26; uint32_t TU_RESERVED : 26;
uint32_t is_iso : 1 ; uint32_t type : 1 ;
uint32_t toggle_mode : 1 ; uint32_t rf_tv : 1 ; // rate feedback or toggle value
uint32_t toggle_reset : 1 ; uint32_t toggle_reset : 1 ;
uint32_t stall : 1 ; uint32_t stall : 1 ;
uint32_t disable : 1 ; uint32_t disable : 1 ;
@ -180,6 +185,7 @@ typedef struct
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd; CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
// Dummy buffer to fix ZLPs overwriting the buffer (probably an USB/DMA controller bug) // Dummy buffer to fix ZLPs overwriting the buffer (probably an USB/DMA controller bug)
// TODO find way to save memory
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8]; CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
@ -188,59 +194,73 @@ CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
typedef struct typedef struct
{ {
dcd_registers_t* regs; // registers dcd_registers_t* regs; // registers
const tusb_speed_t max_speed; // max link speed const bool is_highspeed; // max link speed
const IRQn_Type irqnum; // IRQ number const IRQn_Type irqnum; // IRQ number
const uint8_t ep_pairs; // Max bi-directional Endpoints const uint8_t ep_pairs; // Max bi-directional Endpoints
}dcd_controller_t; }dcd_controller_t;
#ifdef INCLUDE_FSL_DEVICE_REGISTERS #ifdef INCLUDE_FSL_DEVICE_REGISTERS
static const dcd_controller_t _dcd_controller[] = static const dcd_controller_t _dcd_controller[] = {
{ { .regs = (dcd_registers_t*) USB0_BASE , .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = FSL_FEATURE_USB_EP_NUM },
{ .regs = (dcd_registers_t*) USB0_BASE , .max_speed = TUSB_SPEED_FULL, .irqnum = USB0_IRQn, .ep_pairs = FSL_FEATURE_USB_EP_NUM },
#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT #if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
{ .regs = (dcd_registers_t*) USBHSD_BASE, .max_speed = TUSB_SPEED_HIGH, .irqnum = USB1_IRQn, .ep_pairs = FSL_FEATURE_USBHSD_EP_NUM } { .regs = (dcd_registers_t*) USBHSD_BASE, .is_highspeed = true, .irqnum = USB1_IRQn, .ep_pairs = FSL_FEATURE_USBHSD_EP_NUM }
#endif #endif
}; };
#else #else
static const dcd_controller_t _dcd_controller[] = static const dcd_controller_t _dcd_controller[] = {
{ { .regs = (dcd_registers_t*) LPC_USB0_BASE, .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = 5 },
{ .regs = (dcd_registers_t*) LPC_USB0_BASE, .max_speed = TUSB_SPEED_FULL, .irqnum = USB0_IRQn, .ep_pairs = 5 },
}; };
#endif #endif
#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
#define IP3511_HAS_HIGHSPEED
#endif
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION // INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
static inline uint16_t get_buf_offset(void const * buffer) TU_ATTR_ALWAYS_INLINE static inline uint16_t get_buf_offset(void const * buffer) {
{
uint32_t addr = (uint32_t) buffer; uint32_t addr = (uint32_t) buffer;
TU_ASSERT( (addr & 0x3f) == 0, 0 ); TU_ASSERT( (addr & 0x3f) == 0, 0 );
return ( (addr >> 6) & 0xFFFFUL ) ; return ( (addr >> 6) & 0xFFFFUL ) ;
} }
static inline uint8_t ep_addr2id(uint8_t ep_addr) TU_ATTR_ALWAYS_INLINE static inline uint8_t ep_addr2id(uint8_t ep_addr) {
{
return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0); return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
} }
TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_highspeed) {
return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
}
TU_ATTR_ALWAYS_INLINE static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
}
TU_ATTR_ALWAYS_INLINE static inline ep_cmd_sts_t* get_ep_cs(uint8_t ep_id) {
return _dcd.ep[ep_id];
}
TU_ATTR_ALWAYS_INLINE static inline bool rhport_is_highspeed(uint8_t rhport) {
return _dcd_controller[rhport].is_highspeed;
}
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// CONTROLLER API // CONTROLLER API
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
static void prepare_setup_packet(uint8_t rhport) static void prepare_setup_packet(uint8_t rhport) {
{ uint16_t const buf_offset = get_buf_offset(_dcd.setup_packet);
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL ) if ( _dcd_controller[rhport].is_highspeed ) {
{ _dcd.ep[0][1].buffer_hs.offset = buf_offset;
_dcd.ep[0][1].buffer_fs.offset = get_buf_offset(_dcd.setup_packet); } else {
}else _dcd.ep[0][1].buffer_fs.offset = buf_offset;
{
_dcd.ep[0][1].buffer_hs.offset = get_buf_offset(_dcd.setup_packet);
} }
} }
@ -268,8 +288,8 @@ void dcd_init(uint8_t rhport)
dcd_reg->DATABUFSTART = tu_align((uint32_t) &_dcd, TU_BIT(22)); // 22-bit alignment dcd_reg->DATABUFSTART = tu_align((uint32_t) &_dcd, TU_BIT(22)); // 22-bit alignment
dcd_reg->INTSTAT |= dcd_reg->INTSTAT; // clear all pending interrupt dcd_reg->INTSTAT |= dcd_reg->INTSTAT; // clear all pending interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK; dcd_reg->INTEN = INT_DEVICE_STATUS_MASK;
dcd_reg->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK | dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_DEVICE_ENABLE_MASK | DEVCMDSTAT_DEVICE_CONNECT_MASK |
CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK; DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
NVIC_ClearPendingIRQ(_dcd_controller[rhport].irqnum); NVIC_ClearPendingIRQ(_dcd_controller[rhport].irqnum);
} }
@ -291,7 +311,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
// Response with status first before changing device address // Response with status first before changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK; dcd_reg->DEVCMDSTAT &= ~DEVCMDSTAT_DEVICE_ADDR_MASK;
dcd_reg->DEVCMDSTAT |= dev_addr; dcd_reg->DEVCMDSTAT |= dev_addr;
} }
@ -303,13 +323,13 @@ void dcd_remote_wakeup(uint8_t rhport)
void dcd_connect(uint8_t rhport) void dcd_connect(uint8_t rhport)
{ {
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->DEVCMDSTAT |= CMDSTAT_DEVICE_CONNECT_MASK; dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_DEVICE_CONNECT_MASK;
} }
void dcd_disconnect(uint8_t rhport) void dcd_disconnect(uint8_t rhport)
{ {
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_CONNECT_MASK; dcd_reg->DEVCMDSTAT &= ~DEVCMDSTAT_DEVICE_CONNECT_MASK;
} }
void dcd_sof_enable(uint8_t rhport, bool en) void dcd_sof_enable(uint8_t rhport, bool en)
@ -340,19 +360,39 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
_dcd.ep[ep_id][0].cmd_sts.stall = 0; _dcd.ep[ep_id][0].cmd_sts.stall = 0;
_dcd.ep[ep_id][0].cmd_sts.toggle_reset = 1; _dcd.ep[ep_id][0].cmd_sts.toggle_reset = 1;
_dcd.ep[ep_id][0].cmd_sts.toggle_mode = 0; _dcd.ep[ep_id][0].cmd_sts.rf_tv = 0;
} }
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{ {
//------------- Prepare Queue Head -------------// //------------- Prepare Queue Head -------------//
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress); uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
// Check if endpoint is available // Check if endpoint is available
TU_ASSERT( _dcd.ep[ep_id][0].cmd_sts.disable && _dcd.ep[ep_id][1].cmd_sts.disable ); TU_ASSERT( ep_cs[0].cmd_sts.disable && ep_cs[1].cmd_sts.disable );
edpt_reset(rhport, ep_id); edpt_reset(rhport, ep_id);
_dcd.ep[ep_id][0].cmd_sts.is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
switch (p_endpoint_desc->bmAttributes.xfer) {
case TUSB_XFER_ISOCHRONOUS:
ep_cs[0].cmd_sts.type = 1;
break;
case TUSB_XFER_INTERRUPT:
// What is interrupt endpoint in rate feedback mode ?
if ( rhport_is_highspeed(rhport) ) {
ep_cs[0].cmd_sts.type = 1;
ep_cs[0].cmd_sts.rf_tv = 1;
}
break;
case TUSB_XFER_BULK:
// nothing to do both type and rf_tv are 0
break;
default: break;
}
// Enable EP interrupt // Enable EP interrupt
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@ -379,42 +419,50 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
_dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1; _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
} }
static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) {
{
uint16_t nbytes; uint16_t nbytes;
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL ) const bool is_iso = ep_is_iso(ep_cs, _dcd_controller[rhport].is_highspeed);
{
nbytes = tu_min16(total_bytes, _dcd.ep[ep_id][0].cmd_sts.is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX); if ( rhport_is_highspeed(rhport) ) {
_dcd.ep[ep_id][0].buffer_fs.offset = buf_offset; nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_HS_MAX : NBYTES_CBI_HS_MAX);
_dcd.ep[ep_id][0].buffer_fs.nbytes = nbytes; #if TU_CHECK_MCU(OPT_MCU_LPC54)
}else // LPC54 Errata USB.1: In USB high-speed device mode, the NBytes field does not decrement after BULK OUT transfer.
{ // Suggested Work-around: Program the NByte to the max packet size (512)
nbytes = tu_min16(total_bytes, NBYTES_CBI_HS_MAX); // Actual Work-around: round up NByte to multiple of 4.
_dcd.ep[ep_id][0].buffer_hs.offset = buf_offset; // Note: this can cause buffer overflowed and corrupt data if host send more data than total_bytes
_dcd.ep[ep_id][0].buffer_hs.nbytes = nbytes; if ( (ep_id > 1) && (ep_id & 0x01) == 0 && ep_is_bulk(ep_cs) ) {
if ( nbytes & 0x03 ) {
nbytes = tu_align4(nbytes) + 4;
}
}
#endif
ep_cs[0].buffer_hs.offset = buf_offset;
ep_cs[0].buffer_hs.nbytes = nbytes;
}else {
nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX);
ep_cs[0].buffer_fs.offset = buf_offset;
ep_cs[0].buffer_fs.nbytes = nbytes;
} }
_dcd.dma[ep_id].nbytes = nbytes; _dcd.dma[ep_id].nbytes = nbytes;
ep_cs[0].cmd_sts.active = 1;
_dcd.ep[ep_id][0].cmd_sts.active = 1;
} }
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
{
uint8_t const ep_id = ep_addr2id(ep_addr); uint8_t const ep_id = ep_addr2id(ep_addr);
if (!buffer || total_bytes == 0) {
// Although having no data, ZLPs can cause buffer overwritten to zeroes. Probably due to USB/DMA controller side
// effect/bug. Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
buffer = (uint8_t *) (uint32_t) dummy;
}
tu_memclr(&_dcd.dma[ep_id], sizeof(xfer_dma_t)); tu_memclr(&_dcd.dma[ep_id], sizeof(xfer_dma_t));
_dcd.dma[ep_id].total_bytes = total_bytes; _dcd.dma[ep_id].total_bytes = total_bytes;
if (!buffer)
{
// Although having no data, ZLPs can cause buffer overwritten to zeroes.
// Probably due to USB/DMA controller side effect/bug.
// Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
buffer = (uint8_t*)(uint32_t)dummy;
}
prepare_ep_xfer(rhport, ep_id, get_buf_offset(buffer), total_bytes); prepare_ep_xfer(rhport, ep_id, get_buf_offset(buffer), total_bytes);
return true; return true;
@ -441,23 +489,19 @@ static void bus_reset(uint8_t rhport)
dcd_reg->EPSKIP = 0xFFFFFFFF; dcd_reg->EPSKIP = 0xFFFFFFFF;
dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt
dcd_reg->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints dcd_reg->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints
} }
static void process_xfer_isr(uint8_t rhport, uint32_t int_status) static void process_xfer_isr(uint8_t rhport, uint32_t int_status) {
{
uint8_t const max_ep = 2*_dcd_controller[rhport].ep_pairs; uint8_t const max_ep = 2*_dcd_controller[rhport].ep_pairs;
for(uint8_t ep_id = 0; ep_id < max_ep; ep_id++ ) for(uint8_t ep_id = 0; ep_id < max_ep; ep_id++ ) {
{ if ( tu_bit_test(int_status, ep_id) ) {
if ( tu_bit_test(int_status, ep_id) )
{
ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0]; ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0];
xfer_dma_t* xfer_dma = &_dcd.dma[ep_id]; xfer_dma_t* xfer_dma = &_dcd.dma[ep_id];
if ( ep_id == 0 || ep_id == 1) if ( ep_id <= 1 ) {
{
// For control endpoint, we need to manually clear Active bit // For control endpoint, we need to manually clear Active bit
ep_cs->cmd_sts.active = 0; ep_cs->cmd_sts.active = 0;
} }
@ -465,26 +509,29 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status)
uint16_t buf_offset; uint16_t buf_offset;
uint16_t buf_nbytes; uint16_t buf_nbytes;
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL) if ( rhport_is_highspeed(rhport) ) {
{
buf_offset = ep_cs->buffer_fs.offset;
buf_nbytes = ep_cs->buffer_fs.nbytes;
}else
{
buf_offset = ep_cs->buffer_hs.offset; buf_offset = ep_cs->buffer_hs.offset;
buf_nbytes = ep_cs->buffer_hs.nbytes; buf_nbytes = ep_cs->buffer_hs.nbytes;
#if TU_CHECK_MCU(OPT_MCU_LPC54)
// LPC54 Errata USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer
// There is no work-around. For EP in transfer, the NByte value can be ignored after a packet is transmitted.
if ( (ep_id > 1) && (ep_id & 0x01) == 1 && ep_is_bulk(ep_cs) ) {
buf_nbytes = 0;
}
#endif
} else {
buf_offset = ep_cs->buffer_fs.offset;
buf_nbytes = ep_cs->buffer_fs.nbytes;
} }
xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes; xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes;
if ( (buf_nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) ) if ( (buf_nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) ) {
{
// There is more data to transfer // There is more data to transfer
// buff_offset has been already increased by hw to correct value for next transfer // buff_offset has been already increased by hw to correct value for next transfer
prepare_ep_xfer(rhport, ep_id, buf_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes); prepare_ep_xfer(rhport, ep_id, buf_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes);
} } else {
else
{
// for detecting ZLP // for detecting ZLP
xfer_dma->total_bytes = xfer_dma->xferred_bytes; xfer_dma->total_bytes = xfer_dma->xferred_bytes;
@ -511,19 +558,16 @@ void dcd_int_handler(uint8_t rhport)
//------------- Device Status -------------// //------------- Device Status -------------//
if ( int_status & INT_DEVICE_STATUS_MASK ) if ( int_status & INT_DEVICE_STATUS_MASK )
{ {
dcd_reg->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK; dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
if ( cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset if ( cmd_stat & DEVCMDSTAT_RESET_CHANGE_MASK) // bus reset
{ {
bus_reset(rhport); bus_reset(rhport);
tusb_speed_t speed = TUSB_SPEED_FULL; tusb_speed_t speed = TUSB_SPEED_FULL;
if ( _dcd_controller[rhport].is_highspeed ) {
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_HIGH)
{
// 0 : reserved, 1 : full, 2 : high, 3: super // 0 : reserved, 1 : full, 2 : high, 3: super
if ( 2 == ((cmd_stat >> CMDSTAT_SPEED_SHIFT) & 0x3UL) ) if ( 2 == ((cmd_stat >> DEVCMDSTAT_SPEED_SHIFT) & 0x3UL) ) {
{
speed= TUSB_SPEED_HIGH; speed= TUSB_SPEED_HIGH;
} }
} }
@ -531,35 +575,35 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_reset(rhport, speed, true); dcd_event_bus_reset(rhport, speed, true);
} }
if (cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK) if (cmd_stat & DEVCMDSTAT_CONNECT_CHANGE_MASK)
{ {
// device disconnect // device disconnect
if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK) if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{ {
// debouncing as this can be set when device is powering // debouncing as this can be set when device is powering
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
} }
} }
if (cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK) if (cmd_stat & DEVCMDSTAT_SUSPEND_CHANGE_MASK)
{ {
// suspend signal, bus idle for more than 3ms // suspend signal, bus idle for more than 3ms
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK) if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{ {
dcd_event_bus_signal(rhport, (cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true); dcd_event_bus_signal(rhport, (cmd_stat & DEVCMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true);
} }
} }
} }
// Setup Receive // Setup Receive
if ( tu_bit_test(int_status, 0) && (cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) ) if ( tu_bit_test(int_status, 0) && (cmd_stat & DEVCMDSTAT_SETUP_RECEIVED_MASK) )
{ {
// Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints // Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints
_dcd.ep[0][0].cmd_sts.active = _dcd.ep[1][0].cmd_sts.active = 0; _dcd.ep[0][0].cmd_sts.active = _dcd.ep[1][0].cmd_sts.active = 0;
_dcd.ep[0][0].cmd_sts.stall = _dcd.ep[1][0].cmd_sts.stall = 0; _dcd.ep[0][0].cmd_sts.stall = _dcd.ep[1][0].cmd_sts.stall = 0;
dcd_reg->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_SETUP_RECEIVED_MASK;
dcd_event_setup_received(rhport, _dcd.setup_packet, true); dcd_event_setup_received(rhport, _dcd.setup_packet, true);