fix ehci issue with portsc when enable port power and port reset

fix attached device not regconized if attached before power on
This commit is contained in:
hathach 2023-05-09 17:32:14 +07:00
parent 04c759028a
commit 77f0726361
5 changed files with 173 additions and 83 deletions

View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="rt1060 redlink" type="com.jetbrains.cidr.embedded.customgdbserver.type" factoryName="com.jetbrains.cidr.embedded.customgdbserver.factory" PROGRAM_PARAMS="gdbserver --gdb-port 55503 --semihost-port -1 MIMXRT1062xxxxA:EVK-MIMXRT1060" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="cdc_msc_hid" TARGET_NAME="cdc_msc_hid" CONFIG_NAME="rt1060 evk" version="1" RUN_TARGET_PROJECT_NAME="cdc_msc_hid" RUN_TARGET_NAME="cdc_msc_hid">
<custom-gdb-server version="1" gdb-connect="tcp::55503" executable="/usr/local/LinkServer/LinkServer" warmup-ms="0" download-type="ALWAYS" reset-cmd="monitor reset" reset-type="AFTER_DOWNLOAD">
<debugger kind="GDB" isBundled="true" />
</custom-gdb-server>
<method v="2">
<option name="CLION.COMPOUND.BUILD" enabled="true" />
</method>
</configuration>
</component>

View File

@ -2,6 +2,7 @@ set(MCU_VARIANT MIMXRT1062)
set(JLINK_DEVICE MIMXRT1062xxx6A)
set(PYOCD_TARGET mimxrt1060)
set(NXPLS_DEVICE MIMXRT1062xxxxA:EVK-MIMXRT1060)
function(update_board TARGET)
target_sources(${TARGET} PUBLIC

View File

@ -121,15 +121,26 @@ function(family_configure_target TARGET)
# Link dependencies
target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
# Flash Target
# group target (not yet supported by clion)
set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
PROPERTIES FOLDER ${TARGET}_sub
)
#---------- Flash ----------
# Flash using pyocd
add_custom_target(${TARGET}-pyocd
COMMAND pyocd flash -t ${PYOCD_TARGET} $<TARGET_FILE:${TARGET}>
)
# group target
set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
PROPERTIES FOLDER ${TARGET}_sub
# Flash using NXP LinkServer (redlink)
# https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER
# LinkServer has a bug that can only execute with full path otherwise it throws:
# realpath error: No such file or directory
execute_process(COMMAND which LinkServer OUTPUT_VARIABLE LINKSERVER_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_target(${TARGET}-redlink
COMMAND ${LINKSERVER_PATH} flash ${NXPLS_DEVICE} load $<TARGET_FILE:${TARGET}>
)
endfunction()

View File

@ -79,7 +79,8 @@ typedef struct
ehci_qhd_t qhd_pool[QHD_MAX];
ehci_qtd_t qtd_pool[QTD_MAX] TU_ATTR_ALIGNED(32);
ehci_registers_t* regs;
ehci_registers_t* regs; // operational register
ehci_cap_registers_t* cap_regs; // capability register
volatile uint32_t uframe_number;
}ehci_data_t;
@ -87,6 +88,26 @@ typedef struct
// Periodic frame list must be 4K alignment
CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= EHCI_DBG
static inline void print_portsc(ehci_registers_t* regs)
{
TU_LOG_HEX(EHCI_DBG, regs->portsc);
TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status);
TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change);
TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled);
TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset);
TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power);
}
#else
#define print_portsc(_reg)
#endif
//--------------------------------------------------------------------+
// PROTOTYPE
//--------------------------------------------------------------------+
@ -152,11 +173,11 @@ void hcd_port_reset(uint8_t rhport)
ehci_registers_t* regs = ehci_data.regs;
// regs->portsc_bm.port_enabled = 0; // disable port before reset
// regs->portsc_bm.port_reset = 1;
uint32_t portsc = regs->portsc;
// mask out all change bits since they are Write 1 to clear
uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_CHANGE_ALL;
// EHCI Table 2-16 PortSC
// when software writes Port Reset bit to a one, it must also write a zero to the Port Enable bit.
portsc &= ~(EHCI_PORTSC_MASK_PORT_EANBLED);
portsc |= EHCI_PORTSC_MASK_PORT_RESET;
@ -167,9 +188,14 @@ void hcd_port_reset_end(uint8_t rhport)
{
(void) rhport;
#if 0
#if 0 // TODO check if this is necessary
ehci_registers_t* regs = ehci_data.regs;
regs->portsc_bm.port_reset = 0;
// mask out all change bits since they are Write 1 to clear
uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_CHANGE_ALL;
portsc &= ~(EHCI_PORTSC_MASK_PORT_RESET);
regs->portsc = portsc;
#endif
}
@ -240,19 +266,26 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
{
(void) capability_reg; // not used yet
tu_memclr(&ehci_data, sizeof(ehci_data_t));
ehci_data.regs = (ehci_registers_t* ) operatial_reg;
ehci_data.regs = (ehci_registers_t*) operatial_reg;
ehci_data.cap_regs = (ehci_cap_registers_t*) capability_reg;
ehci_registers_t* regs = ehci_data.regs;
//------------- CTRLDSSEGMENT Register (skip) -------------//
//------------- USB INT Register -------------//
regs->inten = 0; // 1. disable all the interrupt
regs->status = EHCI_INT_MASK_ALL; // 2. clear all status
// EHCI 4.1 Host Controller Initialization
//------------- CTRLDSSEGMENT Register (skip) -------------//
//------------- USB INT Register -------------//
// disable all the interrupt
regs->inten = 0;
// clear all status except port change since device maybe connected before this driver is initialized
regs->status = (EHCI_INT_MASK_ALL & ~EHCI_INT_MASK_PORT_CHANGE);
// Enable interrupts
regs->inten = EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE |
EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_FRAMELIST_ROLLOVER;
@ -316,7 +349,16 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
FRAMELIST_SIZE_USBCMD_VALUE;
//------------- ConfigFlag Register (skip) -------------//
regs->portsc_bm.port_power = 1; // enable port power
// enable port power bit in portsc. The function of this bit depends on the value of the Port
// Power Control (PPC) field in the HCSPARAMS register.
if (ehci_data.cap_regs->hcsparams_bm.port_power_control) {
// mask out all change bits since they are Write 1 to clear
uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_CHANGE_ALL);
portsc |= ECHI_PORTSC_MASK_PORT_POWER;
regs->portsc = portsc;
}
return true;
}
@ -656,26 +698,6 @@ static void xfer_error_isr(uint8_t hostid)
}
}
#if CFG_TUSB_DEBUG >= EHCI_DBG
static inline void print_portsc(ehci_registers_t* regs)
{
TU_LOG_HEX(EHCI_DBG, regs->portsc);
TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status);
TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change);
TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled);
TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset);
TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power);
}
#else
#define print_portsc(_reg)
#endif
//------------- Host Controller Driver's Interrupt Handler -------------//
void hcd_int_handler(uint8_t rhport)
{
@ -695,7 +717,7 @@ void hcd_int_handler(uint8_t rhport)
if (int_status & EHCI_INT_MASK_PORT_CHANGE)
{
uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_CHANGE_ALL;
print_portsc(regs);
if (regs->portsc_bm.connect_status_change)

View File

@ -267,16 +267,15 @@ TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
//--------------------------------------------------------------------+
// EHCI Operational Register
//--------------------------------------------------------------------+
enum ehci_interrupt_mask_{
enum {
EHCI_INT_MASK_USB = TU_BIT(0),
EHCI_INT_MASK_ERROR = TU_BIT(1),
EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2),
EHCI_INT_MASK_FRAMELIST_ROLLOVER = TU_BIT(3),
EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = TU_BIT(4),
EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5),
EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18),
EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19),
@ -287,7 +286,7 @@ enum ehci_interrupt_mask_{
EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC
};
enum ehci_usbcmd_pos_ {
enum {
EHCI_USBCMD_POS_RUN_STOP = 0,
EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
@ -296,24 +295,27 @@ enum ehci_usbcmd_pos_ {
EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
};
enum ehci_portsc_change_mask_{
enum {
EHCI_PORTSC_MASK_CURRENT_CONNECT_STATUS = TU_BIT(0),
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
EHCI_PORTSC_MASK_PORT_EANBLED = TU_BIT(2),
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = TU_BIT(3),
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE = TU_BIT(3),
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
ECHI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
EHCI_PORTSC_MASK_ALL =
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE |
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
EHCI_PORTSC_MASK_CHANGE_ALL =
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE |
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
};
typedef volatile struct
{
union {
uint32_t command;
uint32_t command; // 0x00
struct {
uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
@ -333,7 +335,7 @@ typedef volatile struct
};
union {
uint32_t status;
uint32_t status; // 0x04
struct {
uint32_t usb : 1 ; ///< qTD with IOC is retired
@ -357,7 +359,7 @@ typedef volatile struct
};
union{
uint32_t inten;
uint32_t inten; // 0x08
struct {
uint32_t usb : 1 ;
@ -375,43 +377,87 @@ typedef volatile struct
}inten_bm;
};
uint32_t frame_index ; ///< Micro frame counter
uint32_t ctrl_ds_seg ; ///< Control Data Structure Segment
uint32_t periodic_list_base ; ///< Beginning address of perodic frame list
uint32_t async_list_addr ; ///< Address of next async QHD to be executed
uint32_t frame_index ; ///< 0x0C Micro frame counter
uint32_t ctrl_ds_seg ; ///< 0x10 Control Data Structure Segment
uint32_t periodic_list_base ; ///< 0x14 Beginning address of perodic frame list
uint32_t async_list_addr ; ///< 0x18 Address of next async QHD to be executed
uint32_t nxp_tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
uint32_t reserved[8] ;
uint32_t config_flag ; ///< not used by NXP
uint32_t config_flag ; ///< 0x40 not used by NXP
union {
uint32_t portsc ; ///< port status and control
struct {
uint32_t current_connect_status : 1; ///< 0: No device, 1: Device is present on port
uint32_t connect_status_change : 1; ///< Change in Current Connect Status
uint32_t port_enabled : 1; ///< Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
uint32_t port_enable_change : 1; ///< Port Enabled has changed
uint32_t over_current_active : 1; ///< Port has an over-current condition
uint32_t over_current_change : 1; ///< Change to Over-current Active
uint32_t force_port_resume : 1; ///< Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
uint32_t suspend : 1; ///< Port in suspend state
uint32_t port_reset : 1; ///< 1=Port is in Reset. 0=Port is not in Reset
uint32_t nxp_highspeed_status : 1; ///< NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
uint32_t line_status : 2; ///< D+/D- state: 00: SE0, 10: J-state, 01: K-state
uint32_t port_power : 1; ///< 0= power off, 1= power on
uint32_t port_owner : 1; ///< not used by NXP
uint32_t port_indicator_control : 2; ///< 00b: off, 01b: Amber, 10b: green, 11b: undefined
uint32_t port_test_control : 4; ///< Port test mode, not used by tinyusb
uint32_t wake_on_connect_enable : 1; ///< Enables device connects as wake-up events
uint32_t wake_on_disconnect_enable : 1; ///< Enables device disconnects as wake-up events
uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events
uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
uint32_t TU_RESERVED : 1;
uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
// mixed with RW and R/WC bits, care should be taken when writing to this register
uint32_t portsc ; ///< 0x44 port status and control
const struct {
uint32_t current_connect_status : 1; ///< 00: 0: No device, 1: Device is present on port
uint32_t connect_status_change : 1; ///< 01: [R/WC] Change in Current Connect Status
uint32_t port_enabled : 1; ///< 02: Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
uint32_t port_enable_change : 1; ///< 03: [R/WC] Port Enabled has changed
uint32_t over_current_active : 1; ///< 04: Port has an over-current condition
uint32_t over_current_change : 1; ///< 05: [R/WC] Change to Over-current Active
uint32_t force_port_resume : 1; ///< 06: Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
uint32_t suspend : 1; ///< 07: Port in suspend state
uint32_t port_reset : 1; ///< 08: 1=Port is in Reset. 0=Port is not in Reset
uint32_t nxp_highspeed_status : 1; ///< 09: NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
uint32_t line_status : 2; ///< 10-11: D+/D- state: 00: SE0, 10: J-state, 01: K-state
uint32_t port_power : 1; ///< 12: 0= power off, 1= power on
uint32_t port_owner : 1; ///< 13: not used by NXP
uint32_t port_indicator_control : 2; ///< 14-15: 00b: off, 01b: Amber, 10b: green, 11b: undefined
uint32_t port_test_control : 4; ///< 16-19: Port test mode, not used by tinyusb
uint32_t wake_on_connect_enable : 1; ///< 20: Enables device connects as wake-up events
uint32_t wake_on_disconnect_enable : 1; ///< 21: Enables device disconnects as wake-up events
uint32_t wake_on_over_current_enable : 1; ///< 22: Enables over-current conditions as wake-up events
uint32_t nxp_phy_clock_disable : 1; ///< 23: NXP customized: the PHY can be put into Low Power Suspend Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
uint32_t nxp_port_force_fullspeed : 1; ///< 24: NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
uint32_t TU_RESERVED : 1; ///< 25
uint32_t nxp_port_speed : 2; ///< 26-27: NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
uint32_t TU_RESERVED : 4;
}portsc_bm;
};
}ehci_registers_t;
} ehci_registers_t;
//--------------------------------------------------------------------+
// Capability Registers
//--------------------------------------------------------------------+
typedef volatile struct {
uint8_t caplength; // 0x00
uint8_t TU_RESERVED; // 0x01
uint16_t hciversion; // 0x02
union {
uint32_t hcsparams; // 0x04
struct {
uint32_t num_ports : 4; // [00:03]
uint32_t port_power_control : 1; // [04]
uint32_t TU_RESERVED : 2; // [05:06]
uint32_t port_route_rule : 1; // [07]
uint32_t n_pcc : 4; // [08:11] Number of Ports per Companion Controller
uint32_t n_cc : 4; // [12:15] Number of Companion Controllers
uint32_t port_ind : 1; // [16] Port Indicators
uint32_t TU_RESERVED : 3; // [17:19]
uint32_t n_ptt : 4; // [20:23] ChipIdea: Number of Ports per Transaction Translator
uint32_t n_tt : 4; // [24:27] ChipIdea: Number of Transaction Translators
uint32_t TU_RESERVED : 4; // [28:31]
} hcsparams_bm;
};
union {
uint32_t hccparams; // 0x08
struct {
uint32_t addr_64bit : 1; // [00] 64-bit Addressing Capability
uint32_t programmable_frame_list_flag : 1; // [01] Programmable Frame List Flag
uint32_t async_park_cap : 1; // [02] Asynchronous Schedule Park Capability
uint32_t TU_RESERVED : 1; // [03]
uint32_t iso_schedule_threshold : 4; // [4:7] Isochronous Scheduling Threshold
uint32_t eecp : 8; // [8:15] EHCI Extended Capabilities Pointer
uint32_t TU_RESERVED : 16;// [16:31]
} hccparams_bm;
};
uint32_t hcsp_portroute; // 0x0C HCSP Port Route Register
} ehci_cap_registers_t;
TU_VERIFY_STATIC(sizeof(ehci_cap_registers_t) == 16, "size is not correct");
#ifdef __cplusplus
}