Przeglądaj źródła

add chipidea highspeed controller

add warning to transdimension for the rename
hathach 4 lat temu
rodzic
commit
0612433eef

+ 0 - 2
examples/host/cdc_msc_hid/Makefile

@@ -20,9 +20,7 @@ SRC_C += \
 	src/host/hub.c \
 	src/host/usbh.c \
 	src/host/usbh_control.c \
-	src/portable/ehci/ehci.c \
 	src/portable/ohci/ohci.c \
-	src/portable/nxp/transdimension/hcd_transdimension.c \
 	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
 
 include ../../rules.mk

+ 3 - 1
hw/bsp/lpc18/family.mk

@@ -18,7 +18,9 @@ CFLAGS += -Wno-error=unused-parameter -Wno-error=strict-prototypes -Wno-error=ca
 MCU_DIR = hw/mcu/nxp/lpcopen/lpc18xx/lpc_chip_18xx
 
 SRC_C += \
-	src/portable/nxp/transdimension/dcd_transdimension.c \
+	src/portable/chipidea/ci_hs/dcd_ci_hs.c \
+	src/portable/chipidea/ci_hs/hcd_ci_hs.c \
+	src/portable/ehci/ehci.c \
 	$(MCU_DIR)/../gcc/cr_startup_lpc18xx.c \
 	$(MCU_DIR)/src/chip_18xx_43xx.c \
 	$(MCU_DIR)/src/clock_18xx_43xx.c \

+ 2 - 0
src/device/dcd_attr.h

@@ -44,6 +44,7 @@
 
 #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
   // TODO USB0 has 6, USB1 has 4
+  #define DCD_ATTR_CONTROLLER_CHIPIDEA_HS
   #define DCD_ATTR_ENDPOINT_MAX   6
 
 #elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
@@ -58,6 +59,7 @@
   #define DCD_ATTR_ENDPOINT_MAX   6
 
 #elif TU_CHECK_MCU(OPT_MCU_MIMXRT10XX)
+  #define DCD_ATTR_CONTROLLER_CHIPIDEA_HS
   #define DCD_ATTR_ENDPOINT_MAX   8
 
 #elif TU_CHECK_MCU(OPT_MCU_MKL25ZXX, OPT_MCU_K32L2BXX)

+ 136 - 0
src/portable/chipidea/ci_hs/ci_hs_type.h

@@ -0,0 +1,136 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef CI_HS_TYPE_H_
+#define CI_HS_TYPE_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// USBCMD
+enum {
+  USBCMD_RUN_STOP         = TU_BIT(0),
+  USBCMD_RESET            = TU_BIT(1),
+  USBCMD_SETUP_TRIPWIRE   = TU_BIT(13),
+  USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14)  ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
+// Interrupt Threshold bit 23:16
+};
+
+// PORTSC1
+#define PORTSC1_PORT_SPEED_POS    26
+
+enum {
+  PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0),
+  PORTSC1_FORCE_PORT_RESUME      = TU_BIT(6),
+  PORTSC1_SUSPEND                = TU_BIT(7),
+  PORTSC1_FORCE_FULL_SPEED       = TU_BIT(24),
+  PORTSC1_PORT_SPEED             = TU_BIT(26) | TU_BIT(27)
+};
+
+// OTGSC
+enum {
+  OTGSC_VBUS_DISCHARGE          = TU_BIT(0),
+  OTGSC_VBUS_CHARGE             = TU_BIT(1),
+//  OTGSC_HWASSIST_AUTORESET    = TU_BIT(2),
+  OTGSC_OTG_TERMINATION         = TU_BIT(3), ///< Must set to 1 when OTG go to device mode
+  OTGSC_DATA_PULSING            = TU_BIT(4),
+  OTGSC_ID_PULLUP               = TU_BIT(5),
+//  OTGSC_HWASSIT_DATA_PULSE    = TU_BIT(6),
+//  OTGSC_HWASSIT_BDIS_ACONN    = TU_BIT(7),
+  OTGSC_ID                      = TU_BIT(8), ///< 0 = A device, 1 = B Device
+  OTGSC_A_VBUS_VALID            = TU_BIT(9),
+  OTGSC_A_SESSION_VALID         = TU_BIT(10),
+  OTGSC_B_SESSION_VALID         = TU_BIT(11),
+  OTGSC_B_SESSION_END           = TU_BIT(12),
+  OTGSC_1MS_TOGGLE              = TU_BIT(13),
+  OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14),
+};
+
+// USBMode
+enum {
+  USBMODE_CM_DEVICE = 2,
+  USBMODE_CM_HOST   = 3,
+
+  USBMODE_SLOM = TU_BIT(3),
+  USBMODE_SDIS = TU_BIT(4),
+
+  USBMODE_VBUS_POWER_SELECT = TU_BIT(5), // Need to be enabled for LPC18XX/43XX in host mode
+};
+
+// Device Registers
+typedef struct
+{
+  //------------- ID + HW Parameter Registers-------------//
+  __I  uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX
+
+  //------------- Capability Registers-------------//
+  __I  uint8_t  CAPLENGTH;       ///< Capability Registers Length
+  __I  uint8_t  TU_RESERVED[1];
+  __I  uint16_t HCIVERSION;      ///< Host Controller Interface Version
+
+  __I  uint32_t HCSPARAMS;       ///< Host Controller Structural Parameters
+  __I  uint32_t HCCPARAMS;       ///< Host Controller Capability Parameters
+  __I  uint32_t TU_RESERVED[5];
+
+  __I  uint16_t DCIVERSION;      ///< Device Controller Interface Version
+  __I  uint8_t  TU_RESERVED[2];
+
+  __I  uint32_t DCCPARAMS;       ///< Device Controller Capability Parameters
+  __I  uint32_t TU_RESERVED[6];
+
+  //------------- Operational Registers -------------//
+  __IO uint32_t USBCMD;          ///< USB Command Register
+  __IO uint32_t USBSTS;          ///< USB Status Register
+  __IO uint32_t USBINTR;         ///< Interrupt Enable Register
+  __IO uint32_t FRINDEX;         ///< USB Frame Index
+  __I  uint32_t TU_RESERVED;
+  __IO uint32_t DEVICEADDR;      ///< Device Address
+  __IO uint32_t ENDPTLISTADDR;   ///< Endpoint List Address
+  __I  uint32_t TU_RESERVED;
+  __IO uint32_t BURSTSIZE;       ///< Programmable Burst Size
+  __IO uint32_t TXFILLTUNING;    ///< TX FIFO Fill Tuning
+       uint32_t TU_RESERVED[4];
+  __IO uint32_t ENDPTNAK;        ///< Endpoint NAK
+  __IO uint32_t ENDPTNAKEN;      ///< Endpoint NAK Enable
+  __I  uint32_t TU_RESERVED;
+  __IO uint32_t PORTSC1;         ///< Port Status & Control
+  __I  uint32_t TU_RESERVED[7];
+  __IO uint32_t OTGSC;           ///< On-The-Go Status & control
+  __IO uint32_t USBMODE;         ///< USB Device Mode
+  __IO uint32_t ENDPTSETUPSTAT;  ///< Endpoint Setup Status
+  __IO uint32_t ENDPTPRIME;      ///< Endpoint Prime
+  __IO uint32_t ENDPTFLUSH;      ///< Endpoint Flush
+  __I  uint32_t ENDPTSTAT;       ///< Endpoint Status
+  __IO uint32_t ENDPTCOMPLETE;   ///< Endpoint Complete
+  __IO uint32_t ENDPTCTRL[8];    ///< Endpoint Control 0 - 7
+} dcd_registers_t, hcd_registers_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* CI_HS_TYPE_H_ */

+ 662 - 0
src/portable/chipidea/ci_hs/dcd_ci_hs.c

@@ -0,0 +1,662 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && \
+    (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+  #include "fsl_device_registers.h"
+  #define INCLUDE_FSL_DEVICE_REGISTERS
+#else
+  // LPCOpen for 18xx & 43xx
+  #include "chip.h"
+#endif
+
+#include "common/tusb_common.h"
+#include "device/dcd.h"
+#include "ci_hs_type.h"
+
+#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1
+  #define CleanInvalidateDCache_by_Addr   SCB_CleanInvalidateDCache_by_Addr
+#else
+  #define CleanInvalidateDCache_by_Addr(_addr, _dsize)
+#endif
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// ENDPTCTRL
+enum {
+  ENDPTCTRL_STALL          = TU_BIT(0),
+  ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only
+  ENDPTCTRL_TOGGLE_RESET   = TU_BIT(6),
+  ENDPTCTRL_ENABLE         = TU_BIT(7)
+};
+
+enum {
+  ENDPTCTRL_TYPE_POS  = 2, // Endpoint type is 2-bit field
+};
+
+// USBSTS, USBINTR
+enum {
+  INTR_USB         = TU_BIT(0),
+  INTR_ERROR       = TU_BIT(1),
+  INTR_PORT_CHANGE = TU_BIT(2),
+  INTR_RESET       = TU_BIT(6),
+  INTR_SOF         = TU_BIT(7),
+  INTR_SUSPEND     = TU_BIT(8),
+  INTR_NAK         = TU_BIT(16)
+};
+
+// Queue Transfer Descriptor
+typedef struct
+{
+  // Word 0: Next QTD Pointer
+  uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
+
+  // Word 1: qTQ Token
+  uint32_t                      : 3  ;
+  volatile uint32_t xact_err    : 1  ;
+  uint32_t                      : 1  ;
+  volatile uint32_t buffer_err  : 1  ;
+  volatile uint32_t halted      : 1  ;
+  volatile uint32_t active      : 1  ;
+  uint32_t                      : 2  ;
+  uint32_t iso_mult_override    : 2  ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
+  uint32_t                      : 3  ;
+  uint32_t int_on_complete      : 1  ;
+  volatile uint32_t total_bytes : 15 ;
+  uint32_t                      : 1  ;
+
+  // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
+  uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
+
+  //--------------------------------------------------------------------+
+  // TD is 32 bytes aligned but occupies only 28 bytes
+  // Therefore there are 4 bytes padding that we can use.
+  //--------------------------------------------------------------------+
+  uint16_t expected_bytes;
+  uint8_t reserved[2];
+} dcd_qtd_t;
+
+TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
+
+// Queue Head
+typedef struct
+{
+  // Word 0: Capabilities and Characteristics
+  uint32_t                         : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
+  uint32_t int_on_setup            : 1  ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
+  uint32_t max_packet_size         : 11 ; ///< Endpoint's wMaxPacketSize
+  uint32_t                         : 2  ;
+  uint32_t zero_length_termination : 1  ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
+  uint32_t iso_mult                : 2  ; ///<
+
+  // Word 1: Current qTD Pointer
+  volatile uint32_t qtd_addr;
+
+  // Word 2-9: Transfer Overlay
+  volatile dcd_qtd_t qtd_overlay;
+
+  // Word 10-11: Setup request (control OUT only)
+  volatile tusb_control_request_t setup_request;
+
+  //--------------------------------------------------------------------+
+  // QHD is 64 bytes aligned but occupies only 48 bytes
+  // Therefore there are 16 bytes padding that we can use.
+  //--------------------------------------------------------------------+
+  tu_fifo_t * ff;
+  uint8_t reserved[12];
+} dcd_qhd_t;
+
+TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Variables
+//--------------------------------------------------------------------+
+
+typedef struct
+{
+  dcd_registers_t* regs;  // registers
+  const IRQn_Type irqnum; // IRQ number
+  const uint8_t ep_count; // Max bi-directional Endpoints
+}dcd_controller_t;
+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+  static const dcd_controller_t _dcd_controller[] =
+  {
+    // RT1010 and RT1020 only has 1 USB controller
+    #if FSL_FEATURE_SOC_USBHS_COUNT == 1
+      { .regs = (dcd_registers_t*) USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 }
+    #else
+      { .regs = (dcd_registers_t*) USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 },
+      { .regs = (dcd_registers_t*) USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 }
+    #endif
+  };
+
+#else
+  static const dcd_controller_t _dcd_controller[] =
+  {
+    { .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
+    { .regs = (dcd_registers_t*) LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
+  };
+#endif
+
+#define QTD_NEXT_INVALID 0x01
+
+typedef struct {
+  // Must be at 2K alignment
+  // Each endpoint with direction (IN/OUT) occupies a queue head
+  // for portability, TinyUSB only queue 1 TD for each Qhd
+  dcd_qhd_t qhd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64);
+  dcd_qtd_t qtd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
+}dcd_data_t;
+
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
+static dcd_data_t _dcd_data;
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+/// follows LPC43xx User Manual 23.10.3
+static void bus_reset(uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  // The reset value for all endpoint types is the control endpoint. If one endpoint
+  // direction is enabled and the paired endpoint of opposite direction is disabled, then the
+  // endpoint type of the unused direction must be changed from the control type to any other
+  // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
+  // for the data PID tracking on the active endpoint.
+  for( uint8_t i=1; i < _dcd_controller[rhport].ep_count; i++)
+  {
+    dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
+  }
+
+  //------------- Clear All Registers -------------//
+  dcd_reg->ENDPTNAK       = dcd_reg->ENDPTNAK;
+  dcd_reg->ENDPTNAKEN     = 0;
+  dcd_reg->USBSTS         = dcd_reg->USBSTS;
+  dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
+  dcd_reg->ENDPTCOMPLETE  = dcd_reg->ENDPTCOMPLETE;
+
+  while (dcd_reg->ENDPTPRIME) {}
+  dcd_reg->ENDPTFLUSH = 0xFFFFFFFF;
+  while (dcd_reg->ENDPTFLUSH) {}
+
+  // read reset bit in portsc
+
+  //------------- Queue Head & Queue TD -------------//
+  tu_memclr(&_dcd_data, sizeof(dcd_data_t));
+
+  //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
+  _dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1;
+  _dcd_data.qhd[0][0].max_packet_size  = _dcd_data.qhd[0][1].max_packet_size  = CFG_TUD_ENDPOINT0_SIZE;
+  _dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
+
+  _dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
+}
+
+void dcd_init(uint8_t rhport)
+{
+  tu_memclr(&_dcd_data, sizeof(dcd_data_t));
+
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  // Reset controller
+  dcd_reg->USBCMD |= USBCMD_RESET;
+  while( dcd_reg->USBCMD & USBCMD_RESET ) {}
+
+  // Set mode to device, must be set immediately after reset
+  dcd_reg->USBMODE = USBMODE_CM_DEVICE;
+  dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
+
+#if !TUD_OPT_HIGH_SPEED
+  dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
+#endif
+
+  CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+  dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
+  dcd_reg->USBSTS  = dcd_reg->USBSTS;
+  dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
+
+  dcd_reg->USBCMD &= ~0x00FF0000;     // Interrupt Threshold Interval = 0
+  dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+  NVIC_EnableIRQ(_dcd_controller[rhport].irqnum);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+  NVIC_DisableIRQ(_dcd_controller[rhport].irqnum);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+  // Response with status first before changing device address
+  dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->USBCMD |= USBCMD_RUN_STOP;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
+}
+
+//--------------------------------------------------------------------+
+// HELPER
+//--------------------------------------------------------------------+
+
+static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
+{
+  // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
+  // address to 32-byte boundaries. Buffer must be word aligned
+  CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
+
+  tu_memclr(p_qtd, sizeof(dcd_qtd_t));
+
+  p_qtd->next            = QTD_NEXT_INVALID;
+  p_qtd->active          = 1;
+  p_qtd->total_bytes     = p_qtd->expected_bytes = total_bytes;
+  p_qtd->int_on_complete = true;
+
+  if (data_ptr != NULL)
+  {
+    p_qtd->buffer[0] = (uint32_t) data_ptr;
+
+    uint32_t const bufend = p_qtd->buffer[0] + total_bytes;
+    for(uint8_t i=1; i<5; i++)
+    {
+      uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096;
+      if ( bufend <= next_page ) break;
+
+      p_qtd->buffer[i] = next_page;
+
+      // TODO page[1] FRAME_N for ISO transfer
+    }
+  }
+}
+
+//--------------------------------------------------------------------+
+// DCD Endpoint Port
+//--------------------------------------------------------------------+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+  uint8_t const epnum  = tu_edpt_number(ep_addr);
+  uint8_t const dir    = tu_edpt_dir(ep_addr);
+
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
+
+  // flush to abort any primed buffer
+  dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+  uint8_t const epnum = tu_edpt_number(ep_addr);
+  uint8_t const dir   = tu_edpt_dir(ep_addr);
+
+  // data toggle also need to be reset
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 );
+  dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir  ? 16 : 0));
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+  uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+  uint8_t const dir   = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+
+  // Must not exceed max endpoint number
+  TU_ASSERT( epnum < _dcd_controller[rhport].ep_count );
+
+  //------------- Prepare Queue Head -------------//
+  dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
+  tu_memclr(p_qhd, sizeof(dcd_qhd_t));
+
+  p_qhd->zero_length_termination = 1;
+  p_qhd->max_packet_size         = tu_edpt_packet_size(p_endpoint_desc);
+  if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
+  {
+    p_qhd->iso_mult = 1;
+  }
+
+  p_qhd->qtd_overlay.next        = QTD_NEXT_INVALID;
+
+  CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+  // Enable EP Control
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
+
+  if ( dir == TUSB_DIR_OUT )
+  {
+    dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl;
+  }else
+  {
+    dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16);
+  }
+
+  return true;
+}
+
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  // Disable all non-control endpoints
+  for( uint8_t epnum=1; epnum < _dcd_controller[rhport].ep_count; epnum++)
+  {
+    _dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
+    _dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
+
+    dcd_reg->ENDPTFLUSH = TU_BIT(epnum) |  TU_BIT(epnum+16);
+    dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
+  }
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+  uint8_t const epnum  = tu_edpt_number(ep_addr);
+  uint8_t const dir    = tu_edpt_dir(ep_addr);
+
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
+
+  // Flush EP
+  uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
+  dcd_reg->ENDPTFLUSH = flush_mask;
+  while(dcd_reg->ENDPTFLUSH & flush_mask);
+
+  // Clear EP enable
+  dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0));
+}
+
+static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+  dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
+  dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
+
+  p_qhd->qtd_overlay.halted = false;            // clear any previous error
+  p_qhd->qtd_overlay.next   = (uint32_t) p_qtd; // link qtd to qhd
+
+  // flush cache
+  CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+  if ( epnum == 0 )
+  {
+    // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
+    // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
+    while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
+  }
+
+  // start transfer
+  dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0));
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+  uint8_t const epnum = tu_edpt_number(ep_addr);
+  uint8_t const dir   = tu_edpt_dir(ep_addr);
+
+  dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
+  dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
+
+  // Prepare qtd
+  qtd_init(p_qtd, buffer, total_bytes);
+
+  // Start qhd transfer
+  p_qhd->ff = NULL;
+  qhd_start_xfer(rhport, epnum, dir);
+
+  return true;
+}
+
+// fifo has to be aligned to 4k boundary
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+  uint8_t const epnum = tu_edpt_number(ep_addr);
+  uint8_t const dir   = tu_edpt_dir(ep_addr);
+
+  dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
+  dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
+
+  tu_fifo_buffer_info_t fifo_info;
+
+  if (dir)
+  {
+    tu_fifo_get_read_info(ff, &fifo_info);
+  } else
+  {
+    tu_fifo_get_write_info(ff, &fifo_info);
+  }
+
+  if ( fifo_info.len_lin >= total_bytes )
+  {
+    // Linear length is enough for this transfer
+    qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes);
+  }
+  else
+  {
+    // linear part is not enough
+
+    // prepare TD up to linear length
+    qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin);
+
+    if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) )
+    {
+      // If buffer is aligned to 4K & buffer size is multiple of 4K
+      // We can make use of buffer page array to also combine the linear + wrapped length
+      p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
+
+      for(uint8_t i = 1, page = 0; i < 5; i++)
+      {
+        // pick up buffer array where linear ends
+        if (p_qtd->buffer[i] == 0)
+        {
+          p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page;
+          page++;
+        }
+      }
+
+      CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
+    }
+    else
+    {
+      // TODO we may need to carry the wrapped length after the linear part complete
+      // for now only transfer up to linear part
+    }
+  }
+
+  // Start qhd transfer
+  p_qhd->ff = ff;
+  qhd_start_xfer(rhport, epnum, dir);
+
+  return true;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+
+static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir)
+{
+  dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
+  dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
+
+  uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
+      ( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
+
+  if ( result != XFER_RESULT_SUCCESS )
+  {
+    dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+    // flush to abort error buffer
+    dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
+  }
+
+  uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
+
+  if (p_qhd->ff)
+  {
+    if (dir == TUSB_DIR_IN)
+    {
+      tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes);
+    } else
+    {
+      tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
+    }
+  }
+  
+  // only number of bytes in the IOC qtd
+  dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+  dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+  uint32_t const int_enable = dcd_reg->USBINTR;
+  uint32_t const int_status = dcd_reg->USBSTS & int_enable;
+  dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
+
+  // disabled interrupt sources
+  if (int_status == 0) return;
+
+  // Set if the port controller enters the full or high-speed operational state.
+  // either from Bus Reset or Suspended state
+	if (int_status & INTR_PORT_CHANGE)
+	{
+	  // TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
+
+	  // Reset interrupt is not enabled, we manually check if Port Change is due
+	  // to connection / disconnection
+	  if ( dcd_reg->USBSTS & INTR_RESET )
+	  {
+	    dcd_reg->USBSTS = INTR_RESET;
+
+	    if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS)
+	    {
+	      uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
+	      bus_reset(rhport);
+	      dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
+	    }else
+	    {
+	      dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+	    }
+	  }
+	  else
+	  {
+	    // Triggered by resuming from suspended state
+	    if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) )
+	    {
+	      dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+	    }
+	  }
+	}
+
+  if (int_status & INTR_SUSPEND)
+  {
+    // TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1);
+
+    if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
+    {
+      // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
+      // Skip suspend event if we are not addressed
+      if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
+      {
+        dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+      }
+    }
+  }
+
+  if (int_status & INTR_USB)
+  {
+    // Make sure we read the latest version of _dcd_data.
+    CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+    uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
+    dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
+
+    if (dcd_reg->ENDPTSETUPSTAT)
+    {
+      //------------- Set up Received -------------//
+      // 23.10.10.2 Operational model for setup transfers
+      dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
+
+      dcd_event_setup_received(rhport, (uint8_t*)(uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
+    }
+
+    // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
+    // nothing to do, we will submit xfer as error to usbd
+    // if (int_status & INTR_ERROR) { }
+
+    if ( edpt_complete )
+    {
+      for(uint8_t epnum = 0; epnum < DCD_ATTR_ENDPOINT_MAX; epnum++)
+      {
+        if ( tu_bit_test(edpt_complete, epnum)    ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
+        if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
+      }
+    }
+  }
+
+  if (int_status & INTR_SOF)
+  {
+    dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+  }
+}
+
+#endif

+ 117 - 0
src/portable/chipidea/ci_hs/hcd_ci_hs.c

@@ -0,0 +1,117 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+// Chipidea Highspeed USB IP implement EHCI for host functionality
+
+#if TUSB_OPT_HOST_ENABLED && \
+    (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+  #include "fsl_device_registers.h"
+#else
+  // LPCOpen for 18xx & 43xx
+  #include "chip.h"
+#endif
+
+#include "common/tusb_common.h"
+#include "ci_hs_type.h"
+#include "portable/ehci/ehci_api.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// TODO can be merged with dcd_controller_t
+typedef struct
+{
+  uint32_t regs_base;     // registers base
+  const IRQn_Type irqnum; // IRQ number
+}hcd_controller_t;
+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+  static const hcd_controller_t _hcd_controller[] =
+  {
+    // RT1010 and RT1020 only has 1 USB controller
+    #if FSL_FEATURE_SOC_USBHS_COUNT == 1
+      { .regs_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
+    #else
+      { .regs_base = USB1_BASE, .irqnum = USB_OTG1_IRQn },
+      { .regs_base = USB2_BASE, .irqnum = USB_OTG2_IRQn }
+    #endif
+  };
+
+#else
+  static const hcd_controller_t _hcd_controller[] =
+  {
+    { .regs_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
+    { .regs_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
+  };
+#endif
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+bool hcd_init(uint8_t rhport)
+{
+  hcd_registers_t* hcd_reg = (hcd_registers_t*) _hcd_controller[rhport].regs_base;
+
+  // Reset controller
+  hcd_reg->USBCMD |= USBCMD_RESET;
+  while( hcd_reg->USBCMD & USBCMD_RESET ) {}
+
+  // Set mode to device, must be set immediately after reset
+#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
+  // LPC18XX/43XX need to set VBUS Power Select to HIGH
+  // RHPORT1 is fullspeed only (need external PHY for Highspeed)
+  hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
+  if (rhport == 1) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+#else
+  hcd_reg->USBMODE = USBMODE_CM_HOST;
+#endif
+
+  // FIXME force full speed, still have issue with Highspeed enumeration
+  hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+
+  return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+  NVIC_EnableIRQ(_hcd_controller[rhport].irqnum);
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+  NVIC_DisableIRQ(_hcd_controller[rhport].irqnum);
+}
+
+#endif

+ 2 - 0
src/portable/nxp/transdimension/dcd_transdimension.c

@@ -29,6 +29,8 @@
 #if TUSB_OPT_DEVICE_ENABLED && \
     (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
 
+#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
+
 //--------------------------------------------------------------------+
 // INCLUDE
 //--------------------------------------------------------------------+

+ 2 - 0
src/portable/nxp/transdimension/hcd_transdimension.c

@@ -31,6 +31,8 @@
 #if TUSB_OPT_HOST_ENABLED && \
     (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
 
+#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
+
 //--------------------------------------------------------------------+
 // INCLUDE
 //--------------------------------------------------------------------+