You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2020/08/05 11:56:04 UTC

[incubator-nuttx] 01/02: Kinetis K28 USBHS USB-Host.

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit c760db7fd0f509f52e1210bebc64a1f35aa2af8f
Author: Johannes Schock <jo...@nivus.com>
AuthorDate: Sun Aug 2 17:39:47 2020 +0200

    Kinetis K28 USBHS USB-Host.
---
 arch/arm/include/kinetis/kinetis_sim.h             |    4 +-
 arch/arm/src/kinetis/Kconfig                       |    8 +
 arch/arm/src/kinetis/Make.defs                     |    8 +
 arch/arm/src/kinetis/hardware/kinetis_usbhs.h      |   48 +-
 arch/arm/src/kinetis/kinetis_usbhshost.c           | 5469 ++++++++++++++++++++
 arch/arm/src/kinetis/kinetis_usbhshost.h           |  149 +
 boards/arm/kinetis/freedom-k28f/src/Makefile       |    6 +
 boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h |   23 +
 boards/arm/kinetis/freedom-k28f/src/k28_bringup.c  |    5 +
 .../arm/kinetis/freedom-k28f/src/k28_usbhshost.c   |  409 ++
 10 files changed, 6113 insertions(+), 16 deletions(-)

diff --git a/arch/arm/include/kinetis/kinetis_sim.h b/arch/arm/include/kinetis/kinetis_sim.h
index 3ed64e8..addf44f 100644
--- a/arch/arm/include/kinetis/kinetis_sim.h
+++ b/arch/arm/include/kinetis/kinetis_sim.h
@@ -530,8 +530,8 @@
 
 #  define KINETIS_SIM_HAS_SCGC3                       1   /* SoC has SCGC3 Register */
 #  define KINETIS_SIM_HAS_SCGC3_RNGA                  1   /* SoC has SCGC3[TRNG/RNGA] */
-#  undef  KINETIS_SIM_HAS_SCGC3_USBHS                     /* SoC does not have SCGC3[USBHS] */
-#  undef  KINETIS_SIM_HAS_SCGC3_USBHSPHY                  /* SoC does not have SCGC3[USBHSPHY] */
+#  define KINETIS_SIM_HAS_SCGC3_USBHS                 1   /* SoC has SCGC3[USBHS] */
+#  define KINETIS_SIM_HAS_SCGC3_USBHSPHY              1   /* SoC has SCGC3[USBHSPHY] */
 #  undef  KINETIS_SIM_HAS_SCGC3_USBHSDCD                  /* SoC does not have SCGC3[USBHSDCD] */
 #  undef  KINETIS_SIM_HAS_SCGC3_FLEXCAN1                  /* SoC does not have SCGC3[FLEXCAN1] */
 #  undef  KINETIS_SIM_HAS_SCGC3_NFC                       /* SoC does not have SCGC3[NFC] */
diff --git a/arch/arm/src/kinetis/Kconfig b/arch/arm/src/kinetis/Kconfig
index ad64c49..c5a51c4 100644
--- a/arch/arm/src/kinetis/Kconfig
+++ b/arch/arm/src/kinetis/Kconfig
@@ -713,6 +713,14 @@ config KINETIS_CMT
 	---help---
 		Support Carrier Modulator Transmitter
 
+config KINETIS_USBHS
+	bool "USB HS"
+	default n
+	select USBHOST_HAVE_ASYNCH if USBHOST
+	select USBHOST_ASYNCH
+	---help---
+		Support USB HIGH SPEED (see also USBHOST and USBDEV)
+
 config KINETIS_USBOTG
 	bool "USB OTG"
 	default n
diff --git a/arch/arm/src/kinetis/Make.defs b/arch/arm/src/kinetis/Make.defs
index aba8fe3..f5df84f 100644
--- a/arch/arm/src/kinetis/Make.defs
+++ b/arch/arm/src/kinetis/Make.defs
@@ -149,8 +149,16 @@ CHIP_CSRCS += kinetis_usbdev.c
 endif
 
 ifeq ($(CONFIG_USBHOST),y)
+ifneq ($(CONFIG_KINETIS_USBHS),y)
 CHIP_CSRCS += kinetis_usbhost.c
 endif
+endif
+
+ifeq ($(CONFIG_USBHOST),y)
+ifeq ($(CONFIG_KINETIS_USBHS),y)
+CHIP_CSRCS += kinetis_usbhshost.c
+endif
+endif
 
 ifeq ($(CONFIG_KINETIS_DMA),y)
 CHIP_CSRCS += kinetis_dma.c kinetis_pindma.c
diff --git a/arch/arm/src/kinetis/hardware/kinetis_usbhs.h b/arch/arm/src/kinetis/hardware/kinetis_usbhs.h
index 1fc1191..407f904 100644
--- a/arch/arm/src/kinetis/hardware/kinetis_usbhs.h
+++ b/arch/arm/src/kinetis/hardware/kinetis_usbhs.h
@@ -48,6 +48,11 @@
  * Pre-processor Definitions
  ********************************************************************************************************************/
 
+#define KINETIS_EHCI_NRHPORT                 1     /* There is only a single root hub port */
+
+#define KINETIS_USBHS_HCCR_OFFSET            0x100 /* Offset to EHCI Host Controller Capabiliy registers */
+#define KINETIS_USBHS_HCOR_OFFSET            0x140 /* Offset to EHCI Host Controller Operational Registers */
+
 /* Register Offsets *************************************************************************************************/
 
 #define KINETIS_USBHS_ID_OFFSET                           0x0000  /* Identification Register */
@@ -160,6 +165,9 @@
 
 /* Register Addresses ***********************************************************************************************/
 
+#define KINETIS_USBHS_HCCR_BASE                           (KINETIS_USBHS_BASE + KINETIS_USBHS_HCCR_OFFSET)
+#define KINETIS_USBHS_HCOR_BASE                           (KINETIS_USBHS_BASE + KINETIS_USBHS_HCOR_OFFSET)
+
 #define KINETIS_USBHS_ID                                  (KINETIS_USBHS_BASE + KINETIS_USBHS_ID_OFFSET)
 #define KINETIS_USBHS_HWGENERAL                           (KINETIS_USBHS_BASE + KINETIS_USBHS_HWGENERAL_OFFSET)
 #define KINETIS_USBHS_HWHOST                              (KINETIS_USBHS_BASE + KINETIS_USBHS_HWHOST_OFFSET)
@@ -369,7 +377,7 @@
                                                                     /* Bits 28-31: Reserved */
 #define USBHS_HCSPARAMS_N_TT_SHIFT                        (24)      /* Bits 24-27: Number of Transaction Translators */
 #define USBHS_HCSPARAMS_N_TT_MASK                         (0xf << USBHS_HCSPARAMS_N_TT_SHIFT)
-#define USBHS_HCSPARAMS_N_PTT_SHIFT                       (20)      /* Bits 22-30: Ports per Transaction Translator */
+#define USBHS_HCSPARAMS_N_PTT_SHIFT                       (20)      /* Bits 20-23: Ports per Transaction Translator */
 #define USBHS_HCSPARAMS_N_PTT_MASK                        (0xf << USBHS_HCSPARAMS_N_PTT_SHIFT)
                                                                     /* Bits 17-19: Reserved */
 #define USBHS_HCSPARAMS_PI                                (1 << 16) /* Bit 16: Port Indicators */
@@ -556,12 +564,16 @@
                                                                     /* Bit 28-29: Reserved */
 #define USBHS_PORTSC1_PSPD_SHIFT                          (26)      /* Bits 26-27: Port Speed */
 #define USBHS_PORTSC1_PSPD_MASK                           (0x3 << USBHS_PORTSC1_PSPD_SHIFT)
-#  define USBHS_PORTSC1_PTS2                              (1 << 25) /* Bit 25: Port Transceiver Select [2] */
-#  define USBHS_PORTSC1_PFSC                              (1 << 24) /* Bit 24: Port force Full-Speed Connect */
-#  define USBHS_PORTSC1_PHCD                              (1 << 23) /* Bit 23: PHY low power suspend */
-#  define USBHS_PORTSC1_WKOC                              (1 << 22) /* Bit 22: Wake on Over-Current enable */
-#  define USBHS_PORTSC1_WKDS                              (1 << 21) /* Bit 21: Wake on Disconnect enable */
-#  define USBHS_PORTSC1_WKCN                              (1 << 20) /* Bit 20: Wake on Connect enable */
+#  define USBHS_PORTSC1_PSPD_FS                           (0 << USBHS_PORTSC1_PSPD_SHIFT) /* Full-speed */
+#  define USBHS_PORTSC1_PSPD_LS                           (1 << USBHS_PORTSC1_PSPD_SHIFT) /* Low-speed */
+#  define USBHS_PORTSC1_PSPD_HS                           (2 << USBHS_PORTSC1_PSPD_SHIFT) /* High-speed */
+
+#define USBHS_PORTSC1_PTS2                                (1 << 25) /* Bit 25: Port Transceiver Select [2] */
+#define USBHS_PORTSC1_PFSC                                (1 << 24) /* Bit 24: Port force Full-Speed Connect */
+#define USBHS_PORTSC1_PHCD                                (1 << 23) /* Bit 23: PHY low power suspend */
+#define USBHS_PORTSC1_WKOC                                (1 << 22) /* Bit 22: Wake on Over-Current enable */
+#define USBHS_PORTSC1_WKDS                                (1 << 21) /* Bit 21: Wake on Disconnect enable */
+#define USBHS_PORTSC1_WKCN                                (1 << 20) /* Bit 20: Wake on Connect enable */
 #define USBHS_PORTSC1_PTC_SHIFT                           (16)      /* Bits 16-19: Port Test Control */
 #define USBHS_PORTSC1_PTC_MASK                            (0xf << USBHS_PORTSC1_PTC_SHIFT)
 #define USBHS_PORTSC1_PIC_SHIFT                           (14)      /* Bits 14-15: Port Indicator Control */
@@ -627,6 +639,9 @@
 #define USBHS_USBMODE_ES                                  (1 << 2)  /* Bit 2:  Endian Select */
 #define USBHS_USBMODE_CM_SHIFT                            (0)       /* Bits 0-1: Controller Mode */
 #define USBHS_USBMODE_CM_MASK                             (0x3 << USBHS_USBMODE_CM_SHIFT)
+#  define USBHS_USBMODE_CM_IDLE                           (0 << USBHS_USBMODE_CM_SHIFT) /* Idle */
+#  define USBHS_USBMODE_CM_DEVICE                         (2 << USBHS_USBMODE_CM_SHIFT) /* Device controller */
+#  define USBHS_USBMODE_CM_HOST                           (3 << USBHS_USBMODE_CM_SHIFT) /* Host controller */
 
 /* Endpoint Setup Status Register */
 
@@ -730,24 +745,24 @@
 
 /* USB PHY Transmitter Control Register */
 
-#define USBPHY_TXn_USBPHY_TX_EDGECTRL_SHIFT               (29)      /* Bits 28-26: Edge-rate of the current sensing in HS transmit */
+#define USBPHY_TXn_USBPHY_TX_EDGECTRL_SHIFT               (26)      /* Bits 26-28: Edge-rate of the current sensing in HS transmit */
 #define USBPHY_TXn_USBPHY_TX_EDGECTRL_MASK                (0x7 << USBPHY_TXn_USBPHY_TX_EDGECTRL_SHIFT)
                                                                     /* Bit 20-25: Reserved */
-#define USBPHY_TXn_TXCAL45DP_SHIFT                        (19)      /* Bits 16-19: Trim termination resistance to the USB_DP output */
+#define USBPHY_TXn_TXCAL45DP_SHIFT                        (16)      /* Bits 16-19: Trim termination resistance to the USB_DP output */
 #define USBPHY_TXn_TXCAL45DP_MASK                         (0xf << USBPHY_TXn_TXCAL45DP_SHIFT)
                                                                     /* Bits 12-15: Reserved */
-#define USBPHY_TXn_TXCAL45DM_SHIFT                        (11)      /* Bits 8-11: Trim termination resistance to the USB_DM output */
+#define USBPHY_TXn_TXCAL45DM_SHIFT                        (8)       /* Bits 8-11: Trim termination resistance to the USB_DM output */
 #define USBPHY_TXn_TXCAL45DM_MASK                         (0xf << USBPHY_TXn_TXCAL45DM_SHIFT)
                                                                     /* Bits 4-7: Reserved */
 #define USBPHY_TXn_D_CAL_SHIFT                            (0)       /* Bits 7-0: Trim current source for the High Speed TX drivers */
-#define USBPHY_TXn_D_CAL_MASK                             (0x7f << USBPHY_TXn_D_CAL_SHIFT)
+#define USBPHY_TXn_D_CAL_MASK                             (0xf << USBPHY_TXn_D_CAL_SHIFT)
 
 /* USB PHY Receiver Control Register */
 
                                                                     /* Bits 23-31: Reserved */
 #define USBPHY_RXn_RXDBYPASS                              (1 << 22) /* Bit 22: Test mode, replace FS differential receiver with DP single ended receiver */
                                                                     /* Bits 7-21: Reserved */
-#define USBPHY_RXn_DISCONADJ_SHIFT                        (4)       /* Bits 6-4: Adjusts the trip point for the disconnect detector */
+#define USBPHY_RXn_DISCONADJ_SHIFT                        (4)       /* Bits 4-6: Adjusts the trip point for the disconnect detector */
 #define USBPHY_RXn_DISCONADJ_MASK                         (0x7 << USBPHY_RXn_DISCONADJ_SHIFT)
                                                                     /* Bit 3: Reserved */
 #define USBPHY_RXn_ENVADJ_SHIFT                           (0)       /* Bits 0-3:  Adjusts the trip point for the envelope detector */
@@ -847,11 +862,16 @@
 #define USBPHY_PLL_SICn_PLL_ENABLE                        (1 << 13) /* Bit 13: Enable the clock output from the USB PLL */
 #define USBPHY_PLL_SICn_PLL_POWER                         (1 << 12) /* Bit 12: Power up the USB PLL */
 #define USBPHY_PLL_SICn_PLL_HOLD_RING_OFF                 (1 << 11) /* Bit 11: Analog debug bit */
-                                                                    /* Bit 10: Reserved */
-#define USBPHY_PLL_SICn_PLL_EN_USB_CLKS                   (1 << 9)  /* Bit 6:  Enable the USB clock output from the USB PHY PLL */
+                                                                    /* Bits 7-10: Reserved */
+#define USBPHY_PLL_SICn_PLL_EN_USB_CLKS                   (1 << 6)  /* Bit 6:  Enable the USB clock output from the USB PHY PLL */
                                                                     /* Bits 2-5: Reserved */
 #define USBPHY_PLL_SICn_PLL_DIV_SEL_SHIFT                 (0)       /* Bits 0-4: Controls the USB PLL feedback loop divider */
 #define USBPHY_PLL_SICn_PLL_DIV_SEL_MASK                  (0x1f << USBPHY_PLL_SICn_PLL_DIV_SEL_SHIFT)
+#  define USBPHY_PLL_SICn_PLL_DIV_SEL_24MHZ               (0 << USBPHY_PLL_SICn_PLL_DIV_SEL_SHIFT) /* 24Mhz XTAL */
+#  define USBPHY_PLL_SICn_PLL_DIV_SEL_16MHZ               (1 << USBPHY_PLL_SICn_PLL_DIV_SEL_SHIFT) /* 16Mhz XTAL */
+#  define USBPHY_PLL_SICn_PLL_DIV_SEL_12MHZ               (2 << USBPHY_PLL_SICn_PLL_DIV_SEL_SHIFT) /* 12Mhz XTAL */
+
+
 
 /* USB PHY VBUS Detect Control Register */
 
diff --git a/arch/arm/src/kinetis/kinetis_usbhshost.c b/arch/arm/src/kinetis/kinetis_usbhshost.c
new file mode 100644
index 0000000..7b419bc
--- /dev/null
+++ b/arch/arm/src/kinetis/kinetis_usbhshost.c
@@ -0,0 +1,5469 @@
+/****************************************************************************
+ * arch/arm/src/kinetis/kinetis_usbhshost.c
+ *
+ *   Copyright (C) 2013-2017, 2020 Gregory Nutt. All rights reserved.
+ *   Authors: Gregory Nutt <gn...@nuttx.org>
+ *            Dave Marples <da...@marples.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+#include <nuttx/usb/ehci.h>
+#include <nuttx/usb/usbhost_devaddr.h>
+#include <nuttx/usb/usbhost_trace.h>
+
+#include <arch/irq.h>
+
+#include "arm_arch.h"
+#include "chip.h"
+#include "hardware/kinetis_sim.h"
+#include "hardware/kinetis_usbhs.h"
+
+#include "kinetis_usbhshost.h"
+
+#include <arch/board/board.h>
+
+
+#if defined(CONFIG_KINETIS_USBHS) && defined(CONFIG_USBHOST)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Pre-requisites */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
+#elif !defined(CONFIG_SCHED_HPWORK)
+#  error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK)
+#endif
+
+/* Configurable number of Queue Head (QH) structures.  The default is one per
+ * Root hub port plus one for EP0.
+ */
+
+#ifndef CONFIG_KINETIS_EHCI_NQHS
+#  define CONFIG_KINETIS_EHCI_NQHS (KINETIS_EHCI_NRHPORT + 1)
+#endif
+
+/* Configurable number of Queue Element Transfer Descriptor (qTDs).  The
+ * default is one per root hub plus three from EP0.
+ */
+
+#ifndef CONFIG_KINETIS_EHCI_NQTDS
+#  define CONFIG_KINETIS_EHCI_NQTDS (KINETIS_EHCI_NRHPORT + 3)
+#endif
+
+/* Buffers must be aligned to the cache line size */
+
+#ifndef ARMV7M_DCACHE_LINESIZE
+#  define ARMV7M_DCACHE_LINESIZE 32
+#endif
+
+#define DCACHE_LINEMASK (ARMV7M_DCACHE_LINESIZE -1)
+
+/* Configurable size of a request/descriptor buffers */
+
+#ifndef CONFIG_KINETIS_EHCI_BUFSIZE
+#  define CONFIG_KINETIS_EHCI_BUFSIZE 128
+#endif
+
+#define KINETIS_EHCI_BUFSIZE \
+  ((CONFIG_KINETIS_EHCI_BUFSIZE + DCACHE_LINEMASK) & ~DCACHE_LINEMASK)
+
+/* Debug options */
+
+#ifndef CONFIG_DEBUG_USB_INFO
+#  undef CONFIG_KINETIS_EHCI_REGDEBUG
+#endif
+
+/* Isochronous transfers are not currently supported */
+
+#undef CONFIG_USBHOST_ISOC_DISABLE
+#define CONFIG_USBHOST_ISOC_DISABLE 1
+
+/* Registers ****************************************************************
+ * Traditionally, NuttX specifies register locations using individual
+ * register offsets from a base address.  That tradition is broken here and,
+ * instead, register blocks are represented as structures.  This is done here
+ * because, in principle, EHCI operational register address may not be known
+ * at compile time; the operational registers lie at an offset specified in
+ * the 'caplength' byte of the Host Controller Capability Registers.
+ *
+ * However, for the case of the KINETIS EHCI, we know apriori that locations
+ * of these register blocks.
+ */
+
+/* Host Controller Capability Registers */
+
+#define HCCR ((struct ehci_hccr_s *)KINETIS_USBHS_HCCR_BASE)
+
+/* Host Controller Operational Registers */
+
+#define HCOR ((volatile struct ehci_hcor_s *)KINETIS_USBHS_HCOR_BASE)
+
+/* Interrupts ***************************************************************
+ * This is the set of interrupts handled by this driver.
+ */
+
+#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \
+                           EHCI_INT_PORTSC |  EHCI_INT_SYSERROR | \
+                           EHCI_INT_AAINT)
+
+/* The periodic frame list is a 4K-page aligned array of Frame List Link
+ * pointers. The length of the frame list may be programmable.  The
+ * programmability of the periodic frame list is exported to system software
+ * via the HCCPARAMS register. If non-programmable, the length is 1024
+ * elements. If programmable, the length can be selected by system software
+ * as one of 256, 512, or 1024 elements.
+ */
+
+#define FRAME_LIST_SIZE 1024
+
+/* DMA **********************************************************************/
+
+/* For now, we are assuming an identity mapping between physical and virtual
+ * address spaces.
+ */
+
+#define kinetis_physramaddr(a) (a)
+#define kinetis_virtramaddr(a) (a)
+
+/* USB trace ****************************************************************/
+
+#ifdef HAVE_USBHOST_TRACE
+#  define TR_FMT1 false
+#  define TR_FMT2 true
+
+#  define TRENTRY(id,fmt1,string) {string}
+
+#  define TRACE1_FIRST     ((int)__TRACE1_BASEVALUE + 1)
+#  define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST)
+#  define TRACE1_NSTRINGS  TRACE1_INDEX(__TRACE1_NSTRINGS)
+
+#  define TRACE2_FIRST     ((int)__TRACE1_NSTRINGS + 1)
+#  define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST)
+#  define TRACE2_NSTRINGS  TRACE2_INDEX(__TRACE2_NSTRINGS)
+#endif
+
+/* Port numbers */
+
+#define RHPNDX(rh)            ((rh)->hport.hport.port)
+#define RHPORT(rh)            (RHPNDX(rh)+1)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Internal representation of the EHCI Queue Head (QH) */
+
+struct kinetis_epinfo_s;
+struct kinetis_qh_s
+{
+  /* Fields visible to hardware */
+
+  struct ehci_qh_s hw;           /* Hardware representation of the queue head */
+
+  /* Internal fields used by the EHCI driver */
+
+  struct kinetis_epinfo_s *epinfo; /* Endpoint used for the transfer */
+  uint32_t fqp;                  /* First qTD in the list (physical address) */
+  uint8_t pad[8];                /* Padding to assure 32-byte alignment */
+};
+
+/* Internal representation of the EHCI Queue Element Transfer Descriptor (qTD) */
+
+struct kinetis_qtd_s
+{
+  /* Fields visible to hardware */
+
+  struct ehci_qtd_s hw;          /* Hardware representation of the queue head */
+
+  /* Internal fields used by the EHCI driver */
+};
+
+/* The following is used to manage lists of free QHs and qTDs */
+
+struct kinetis_list_s
+{
+  struct kinetis_list_s *flink;    /* Link to next entry in the list
+                                  * Variable length entry data follows
+                                  */
+};
+
+/* List traversal call-out functions */
+
+typedef int (*foreach_qh_t)(struct kinetis_qh_s *qh, uint32_t **bp,
+                            void *arg);
+typedef int (*foreach_qtd_t)(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                             void *arg);
+
+/* This structure describes one endpoint. */
+
+struct kinetis_epinfo_s
+{
+  uint8_t epno:7;                /* Endpoint number */
+  uint8_t dirin:1;               /* 1:IN endpoint 0:OUT endpoint */
+  uint8_t devaddr:7;             /* Device address */
+  uint8_t toggle:1;              /* Next data toggle */
+#ifndef CONFIG_USBHOST_INT_DISABLE
+  uint8_t interval;              /* Polling interval */
+#endif
+  uint8_t status;                /* Retained token status bits (for debug purposes) */
+  volatile bool iocwait;         /* TRUE: Thread is waiting for transfer completion */
+  uint16_t maxpacket:11;         /* Maximum packet size */
+  uint16_t xfrtype:2;            /* See USB_EP_ATTR_XFER_* definitions in usb.h */
+  uint16_t speed:2;              /* See USB_*_SPEED definitions in ehci.h */
+  int result;                    /* The result of the transfer */
+  uint32_t xfrd;                 /* On completion, will hold the number of bytes transferred */
+  sem_t iocsem;                  /* Semaphore used to wait for transfer completion */
+#ifdef CONFIG_USBHOST_ASYNCH
+  usbhost_asynch_t callback;     /* Transfer complete callback */
+  void *arg;                     /* Argument that accompanies the callback */
+#endif
+};
+
+/* This structure retains the state of one root hub port */
+
+struct kinetis_rhport_s
+{
+  /* Common device fields.  This must be the first thing defined in the
+   * structure so that it is possible to simply cast from struct usbhost_s
+   * to struct kinetis_rhport_s.
+   */
+
+  struct usbhost_driver_s drvr;
+
+  /* Root hub port status */
+
+  volatile bool connected;     /* Connected to device */
+  volatile bool lowspeed;      /* Low speed device attached */
+  struct kinetis_epinfo_s ep0;   /* EP0 endpoint info */
+
+  /* This is the hub port description understood by class drivers */
+
+  struct usbhost_roothubport_s hport;
+};
+
+/* This structure retains the overall state of the USB host controller */
+
+struct kinetis_ehci_s
+{
+  volatile bool pscwait;        /* TRUE: Thread is waiting for port status change event */
+
+  sem_t exclsem;                /* Support mutually exclusive access */
+  sem_t pscsem;                 /* Semaphore to wait for port status change events */
+
+  struct kinetis_epinfo_s ep0;    /* Endpoint 0 */
+  struct kinetis_list_s *qhfree;  /* List of free Queue Head (QH) structures */
+  struct kinetis_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */
+  struct work_s work;           /* Supports interrupt bottom half */
+
+#ifdef CONFIG_USBHOST_HUB
+  /* Used to pass external hub port events */
+
+  volatile struct usbhost_hubport_s *hport;
+#endif
+
+  /* Root hub ports */
+
+  struct kinetis_rhport_s rhport[KINETIS_EHCI_NRHPORT];
+};
+
+#ifdef HAVE_USBHOST_TRACE
+/* USB trace codes */
+
+enum usbhost_trace1codes_e
+{
+  __TRACE1_BASEVALUE = 0,           /* This will force the first value to be 1 */
+
+  EHCI_TRACE1_SYSTEMERROR,          /* EHCI ERROR: System error */
+  EHCI_TRACE1_QTDFOREACH_FAILED,    /* EHCI ERROR: kinetis_qtd_foreach failed */
+  EHCI_TRACE1_QHALLOC_FAILED,       /* EHCI ERROR: Failed to allocate a QH */
+  EHCI_TRACE1_BUFTOOBIG,            /* EHCI ERROR: Buffer too big */
+  EHCI_TRACE1_REQQTDALLOC_FAILED,   /* EHCI ERROR: Failed to allocate request qTD */
+  EHCI_TRACE1_ADDBPL_FAILED,        /* EHCI ERROR: kinetis_qtd_addbpl failed */
+  EHCI_TRACE1_DATAQTDALLOC_FAILED,  /* EHCI ERROR: Failed to allocate data buffer qTD */
+  EHCI_TRACE1_DEVDISCONNECTED,      /* EHCI ERROR: Device disconnected */
+  EHCI_TRACE1_QHCREATE_FAILED,      /* EHCI ERROR: kinetis_qh_create failed */
+  EHCI_TRACE1_QTDSETUP_FAILED,      /* EHCI ERROR: kinetis_qtd_setupphase failed */
+
+  EHCI_TRACE1_QTDDATA_FAILED,       /* EHCI ERROR: kinetis_qtd_dataphase failed */
+  EHCI_TRACE1_QTDSTATUS_FAILED,     /* EHCI ERROR: kinetis_qtd_statusphase failed */
+  EHCI_TRACE1_TRANSFER_FAILED,      /* EHCI ERROR: Transfer failed */
+  EHCI_TRACE1_QHFOREACH_FAILED,     /* EHCI ERROR: kinetis_qh_foreach failed: */
+  EHCI_TRACE1_SYSERR_INTR,          /* EHCI: Host System Error Interrupt */
+  EHCI_TRACE1_USBERR_INTR,          /* EHCI: USB Error Interrupt (USBERRINT) Interrupt */
+  EHCI_TRACE1_EPALLOC_FAILED,       /* EHCI ERROR: Failed to allocate EP info structure */
+  EHCI_TRACE1_BADXFRTYPE,           /* EHCI ERROR: Support for transfer type not implemented */
+  EHCI_TRACE1_HCHALTED_TIMEOUT,     /* EHCI ERROR: Timed out waiting for HCHalted */
+  EHCI_TRACE1_QHPOOLALLOC_FAILED,   /* EHCI ERROR: Failed to allocate the QH pool */
+
+  EHCI_TRACE1_QTDPOOLALLOC_FAILED,  /* EHCI ERROR: Failed to allocate the qTD pool */
+  EHCI_TRACE1_PERFLALLOC_FAILED,    /* EHCI ERROR: Failed to allocate the periodic frame list */
+  EHCI_TRACE1_RESET_FAILED,         /* EHCI ERROR: kinetis_reset failed */
+  EHCI_TRACE1_RUN_FAILED,           /* EHCI ERROR: EHCI Failed to run */
+  EHCI_TRACE1_IRQATTACH_FAILED,     /* EHCI ERROR: Failed to attach IRQ */
+
+#ifdef HAVE_USBHOST_TRACE_VERBOSE
+  EHCI_VTRACE1_PORTSC_CSC,          /* EHCI Connect Status Change */
+  EHCI_VTRACE1_PORTSC_CONNALREADY,  /* EHCI Already connected */
+  EHCI_VTRACE1_PORTSC_DISCALREADY,  /* EHCI Already disconnected */
+  EHCI_VTRACE1_TOPHALF,             /* EHCI Interrupt top half */
+  EHCI_VTRACE1_AAINTR,              /* EHCI Async Advance Interrupt */
+
+  EHCI_VTRACE1_CLASSENUM,           /* EHCI Hub port CLASS enumeration */
+  EHCI_VTRACE1_USBINTR,             /* EHCI USB Interrupt (USBINT) Interrupt */
+  EHCI_VTRACE1_ENUM_DISCONN,        /* EHCI Enumeration not connected */
+  EHCI_VTRACE1_INITIALIZING,        /* EHCI Initializing EHCI Stack */
+  EHCI_VTRACE1_HCCPARAMS,           /* EHCI HCCPARAMS */
+  EHCI_VTRACE1_INIITIALIZED,        /* EHCI USB EHCI Initialized */
+#endif
+
+  __TRACE1_NSTRINGS,                /* Separates the format 1 from the format 2 strings */
+
+  EHCI_TRACE2_EPSTALLED,            /* EHCI EP Stalled */
+  EHCI_TRACE2_EPIOERROR,            /* EHCI ERROR: EP TOKEN */
+  EHCI_TRACE2_CLASSENUM_FAILED,     /* EHCI usbhost_enumerate() failed */
+
+#ifdef HAVE_USBHOST_TRACE_VERBOSE
+  EHCI_VTRACE2_ASYNCXFR,            /* EHCI Async transfer */
+  EHCI_VTRACE2_INTRXFR,             /* EHCI Interrupt Transfer */
+  EHCI_VTRACE2_IOCCHECK,            /* EHCI IOC */
+  EHCI_VTRACE2_PORTSC,              /* EHCI PORTSC */
+  EHCI_VTRACE2_PORTSC_CONNECTED,    /* EHCI RHPort connected */
+  EHCI_VTRACE2_PORTSC_DISCONND,     /* EHCI RHport disconnected */
+  EHCI_VTRACE2_MONWAKEUP,           /* EHCI RHPort connected wakeup */
+
+  EHCI_VTRACE2_EPALLOC,             /* EHCI EPALLOC */
+  EHCI_VTRACE2_CTRLINOUT,           /* EHCI CTRLIN/OUT */
+  EHCI_VTRACE2_HCIVERSION,          /* EHCI HCIVERSION */
+  EHCI_VTRACE2_HCSPARAMS,           /* EHCI HCSPARAMS */
+#endif
+
+  __TRACE2_NSTRINGS                 /* Total number of enumeration values */
+};
+
+/* USB trace data structure */
+
+struct kinetis_ehci_trace_s
+{
+#if 0
+  uint16_t id;
+  bool fmt2;
+#endif
+  FAR const char *string;
+};
+
+#endif /* HAVE_USBHOST_TRACE */
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t kinetis_read16(const uint8_t *addr);
+static uint32_t kinetis_read32(const uint8_t *addr);
+#if 0 /* Not used */
+static void kinetis_write16(uint16_t memval, uint8_t *addr);
+static void kinetis_write32(uint32_t memval, uint8_t *addr);
+#endif
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t kinetis_swap16(uint16_t value);
+static uint32_t kinetis_swap32(uint32_t value);
+#else
+#  define kinetis_swap16(value) (value)
+#  define kinetis_swap32(value) (value)
+#endif
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_printreg(volatile uint32_t *regaddr, uint32_t regval,
+         bool iswrite);
+static void kinetis_checkreg(volatile uint32_t *regaddr, uint32_t regval,
+         bool iswrite);
+static uint32_t kinetis_getreg(volatile uint32_t *regaddr);
+static void kinetis_putreg(uint32_t regval, volatile uint32_t *regaddr);
+#else
+static inline uint32_t kinetis_getreg(volatile uint32_t *regaddr);
+static inline void kinetis_putreg(uint32_t regval, volatile uint32_t *regaddr);
+#endif
+static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits,
+         unsigned int delay);
+
+/* Semaphores ***************************************************************/
+
+static int kinetis_takesem(sem_t *sem);
+static int kinetis_takesem_noncancelable(sem_t *sem);
+#define kinetis_givesem(s) nxsem_post(s);
+
+/* Allocators ***************************************************************/
+
+static struct kinetis_qh_s *kinetis_qh_alloc(void);
+static void kinetis_qh_free(struct kinetis_qh_s *qh);
+static struct kinetis_qtd_s *kinetis_qtd_alloc(void);
+static void kinetis_qtd_free(struct kinetis_qtd_s *qtd);
+
+/* List Management **********************************************************/
+
+static int kinetis_qh_foreach(struct kinetis_qh_s *qh, uint32_t **bp,
+         foreach_qh_t handler, void *arg);
+static int kinetis_qtd_foreach(struct kinetis_qh_s *qh, foreach_qtd_t handler,
+         void *arg);
+static int kinetis_qtd_discard(struct kinetis_qtd_s *qtd, uint32_t **bp,
+         void *arg);
+static int kinetis_qh_discard(struct kinetis_qh_s *qh);
+
+/* Cache Operations *********************************************************/
+
+#if 0 /* Not used */
+static int kinetis_qtd_invalidate(struct kinetis_qtd_s *qtd, uint32_t **bp,
+          void *arg);
+static int kinetis_qh_invalidate(struct kinetis_qh_s *qh);
+#endif
+static int kinetis_qtd_flush(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                           void *arg);
+static int kinetis_qh_flush(struct kinetis_qh_s *qh);
+
+/* Endpoint Transfer Handling ***********************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_qtd_print(struct kinetis_qtd_s *qtd);
+static void kinetis_qh_print(struct kinetis_qh_s *qh);
+static int kinetis_qtd_dump(struct kinetis_qtd_s *qtd, uint32_t **bp, void *arg);
+static int kinetis_qh_dump(struct kinetis_qh_s *qh, uint32_t **bp, void *arg);
+#else
+#  define kinetis_qtd_print(qtd)
+#  define kinetis_qh_print(qh)
+#  define kinetis_qtd_dump(qtd, bp, arg) OK
+#  define kinetis_qh_dump(qh, bp, arg)   OK
+#endif
+
+static inline uint8_t kinetis_ehci_speed(uint8_t usbspeed);
+static int kinetis_ioc_setup(struct kinetis_rhport_s *rhport,
+         struct kinetis_epinfo_s *epinfo);
+static int kinetis_ioc_wait(struct kinetis_epinfo_s *epinfo);
+static void kinetis_qh_enqueue(struct kinetis_qh_s *qhead,
+         struct kinetis_qh_s *qh);
+static struct kinetis_qh_s *kinetis_qh_create(struct kinetis_rhport_s *rhport,
+         struct kinetis_epinfo_s *epinfo);
+static int kinetis_qtd_addbpl(struct kinetis_qtd_s *qtd, const void *buffer,
+         size_t buflen);
+static struct kinetis_qtd_s *kinetis_qtd_setupphase(
+         struct kinetis_epinfo_s *epinfo, const struct usb_ctrlreq_s *req);
+static struct kinetis_qtd_s *kinetis_qtd_dataphase(struct kinetis_epinfo_s *epinfo,
+         void *buffer, int buflen, uint32_t tokenbits);
+static struct kinetis_qtd_s *kinetis_qtd_statusphase(uint32_t tokenbits);
+static ssize_t kinetiskinetis_virtramaddr_async_setup(
+         struct kinetis_rhport_s *rhport, struct kinetis_epinfo_s *epinfo,
+         const struct usb_ctrlreq_s *req, uint8_t *buffer, size_t buflen);
+#ifndef CONFIG_USBHOST_INT_DISABLE
+static int kinetis_intr_setup(struct kinetis_rhport_s *rhport,
+         struct kinetis_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
+#endif
+static ssize_t kinetis_transfer_wait(struct kinetis_epinfo_s *epinfo);
+#ifdef CONFIG_USBHOST_ASYNCH
+static inline int kinetis_ioc_async_setup(struct kinetis_rhport_s *rhport,
+         struct kinetis_epinfo_s *epinfo, usbhost_asynch_t callback,
+         FAR void *arg);
+static void kinetis_asynch_completion(struct kinetis_epinfo_s *epinfo);
+#endif
+
+/* Interrupt Handling *******************************************************/
+
+static int kinetis_qtd_ioccheck(struct kinetis_qtd_s *qtd, uint32_t **bp,
+         void *arg);
+static int kinetis_qh_ioccheck(struct kinetis_qh_s *qh, uint32_t **bp,
+         void *arg);
+#ifdef CONFIG_USBHOST_ASYNCH
+static int kinetis_qtd_cancel(struct kinetis_qtd_s *qtd, uint32_t **bp,
+         void *arg);
+static int kinetis_qh_cancel(struct kinetis_qh_s *qh, uint32_t **bp, void *arg);
+#endif
+static inline void kinetis_ioc_bottomhalf(void);
+static inline void kinetis_portsc_bottomhalf(void);
+static inline void kinetis_syserr_bottomhalf(void);
+static inline void kinetis_async_advance_bottomhalf(void);
+static void kinetis_ehci_bottomhalf(FAR void *arg);
+static int kinetis_ehci_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* USB Host Controller Operations *******************************************/
+
+static int kinetis_wait(FAR struct usbhost_connection_s *conn,
+         FAR struct usbhost_hubport_s **hport);
+static int kinetis_rh_enumerate(FAR struct usbhost_connection_s *conn,
+         FAR struct usbhost_hubport_s *hport);
+static int kinetis_enumerate(FAR struct usbhost_connection_s *conn,
+         FAR struct usbhost_hubport_s *hport);
+
+static int kinetis_ep0configure(FAR struct usbhost_driver_s *drvr,
+         usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed,
+         uint16_t maxpacketsize);
+static int kinetis_epalloc(FAR struct usbhost_driver_s *drvr,
+         const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
+static int kinetis_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
+static int kinetis_alloc(FAR struct usbhost_driver_s *drvr,
+         FAR uint8_t **buffer, FAR size_t *maxlen);
+static int kinetis_free(FAR struct usbhost_driver_s *drvr,
+         FAR uint8_t *buffer);
+static int kinetis_ioalloc(FAR struct usbhost_driver_s *drvr,
+         FAR uint8_t **buffer, size_t buflen);
+static int kinetis_iofree(FAR struct usbhost_driver_s *drvr,
+         FAR uint8_t *buffer);
+static int kinetis_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
+         FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer);
+static int kinetis_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
+         FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer);
+static ssize_t kinetis_transfer(FAR struct usbhost_driver_s *drvr,
+         usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen);
+#ifdef CONFIG_USBHOST_ASYNCH
+static int kinetis_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
+         FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback,
+         FAR void *arg);
+#endif
+static int kinetis_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
+#ifdef CONFIG_USBHOST_HUB
+static int kinetis_connect(FAR struct usbhost_driver_s *drvr,
+         FAR struct usbhost_hubport_s *hport, bool connected);
+#endif
+static void kinetis_disconnect(FAR struct usbhost_driver_s *drvr,
+                             FAR struct usbhost_hubport_s *hport);
+
+/* Initialization ***********************************************************/
+
+static int kinetis_reset(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* In this driver implementation, support is provided for only a single a
+ * single USB device.  All status information can be simply retained in a
+ * single global instance.
+ */
+
+static struct kinetis_ehci_s g_ehci;
+
+/* This is the connection/enumeration interface */
+
+static struct usbhost_connection_s g_ehciconn;
+
+/* Maps USB chapter 9 speed to EHCI speed */
+
+static const uint8_t g_ehci_speed[4] =
+{
+  0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED
+};
+
+/* The head of the asynchronous queue */
+
+static struct kinetis_qh_s g_asynchead __attribute__ ((aligned(32)));
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+/* The head of the periodic queue */
+
+static struct kinetis_qh_s g_intrhead   __attribute__ ((aligned(32)));
+
+/* The frame list */
+
+#ifdef CONFIG_KINETIS_EHCI_PREALLOCATE
+static uint32_t g_framelist[FRAME_LIST_SIZE] __attribute__ ((aligned(4096)));
+#else
+static uint32_t *g_framelist;
+#endif
+#endif /* CONFIG_USBHOST_INT_DISABLE */
+
+#ifdef CONFIG_KINETIS_EHCI_PREALLOCATE
+/* Pools of pre-allocated data structures.  These will all be linked into the
+ * free lists within g_ehci.  These must all be aligned to 32-byte boundaries
+ */
+
+/* Queue Head (QH) pool */
+
+static struct kinetis_qh_s g_qhpool[CONFIG_KINETIS_EHCI_NQHS]
+                       __attribute__ ((aligned(32)));
+
+/* Queue Element Transfer Descriptor (qTD) pool */
+
+static struct kinetis_qtd_s g_qtdpool[CONFIG_KINETIS_EHCI_NQTDS]
+                        __attribute__ ((aligned(32)));
+
+#else
+/* Pools of dynamically data structures.  These will all be linked into the
+ * free lists within g_ehci.  These must all be aligned to 32-byte boundaries
+ */
+
+/* Queue Head (QH) pool */
+
+static struct kinetis_qh_s *g_qhpool;
+
+/* Queue Element Transfer Descriptor (qTD) pool */
+
+static struct kinetis_qtd_s *g_qtdpool;
+
+#endif
+
+#ifdef HAVE_USBHOST_TRACE
+/* USB trace strings */
+
+static const struct kinetis_ehci_trace_s g_trace1[TRACE1_NSTRINGS] =
+{
+  TRENTRY(EHCI_TRACE1_SYSTEMERROR, TR_FMT1,
+          "EHCI ERROR: System error: %06x\n"),
+  TRENTRY(EHCI_TRACE1_QTDFOREACH_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qtd_foreach failed: %d\n"),
+  TRENTRY(EHCI_TRACE1_QHALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate a QH\n"),
+  TRENTRY(EHCI_TRACE1_BUFTOOBIG, TR_FMT1,
+          "EHCI ERROR: Buffer too big. Remaining %d\n"),
+  TRENTRY(EHCI_TRACE1_REQQTDALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate request qTD"),
+  TRENTRY(EHCI_TRACE1_ADDBPL_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qtd_addbpl failed: %d\n"),
+  TRENTRY(EHCI_TRACE1_DATAQTDALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate data buffer qTD, 0"),
+  TRENTRY(EHCI_TRACE1_DEVDISCONNECTED, TR_FMT1,
+          "EHCI ERROR: Device disconnected %d\n"),
+  TRENTRY(EHCI_TRACE1_QHCREATE_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qh_create failed\n"),
+  TRENTRY(EHCI_TRACE1_QTDSETUP_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qtd_setupphase failed\n"),
+
+  TRENTRY(EHCI_TRACE1_QTDDATA_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qtd_dataphase failed\n"),
+  TRENTRY(EHCI_TRACE1_QTDSTATUS_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qtd_statusphase failed\n"),
+  TRENTRY(EHCI_TRACE1_TRANSFER_FAILED, TR_FMT1,
+          "EHCI ERROR: Transfer failed %d\n"),
+  TRENTRY(EHCI_TRACE1_QHFOREACH_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_qh_foreach failed: %d\n"),
+  TRENTRY(EHCI_TRACE1_SYSERR_INTR, TR_FMT1,
+          "EHCI: Host System Error Interrupt\n"),
+  TRENTRY(EHCI_TRACE1_USBERR_INTR, TR_FMT1,
+          "EHCI: USB Error Interrupt (USBERRINT) Interrupt: %06x\n"),
+  TRENTRY(EHCI_TRACE1_EPALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate EP info structure\n"),
+  TRENTRY(EHCI_TRACE1_BADXFRTYPE, TR_FMT1,
+          "EHCI ERROR: Support for transfer type %d not implemented\n"),
+  TRENTRY(EHCI_TRACE1_HCHALTED_TIMEOUT, TR_FMT1,
+          "EHCI ERROR: Timed out waiting for HCHalted. USBSTS: %06x\n"),
+  TRENTRY(EHCI_TRACE1_QHPOOLALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate the QH pool\n"),
+
+  TRENTRY(EHCI_TRACE1_QTDPOOLALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate the qTD pool\n"),
+  TRENTRY(EHCI_TRACE1_PERFLALLOC_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to allocate the periodic frame list\n"),
+  TRENTRY(EHCI_TRACE1_RESET_FAILED, TR_FMT1,
+          "EHCI ERROR: kinetis_reset failed: %d\n"),
+  TRENTRY(EHCI_TRACE1_RUN_FAILED, TR_FMT1,
+          "EHCI ERROR: EHCI Failed to run: USBSTS=%06x\n"),
+  TRENTRY(EHCI_TRACE1_IRQATTACH_FAILED, TR_FMT1,
+          "EHCI ERROR: Failed to attach IRQ%d\n"),
+
+#ifdef HAVE_USBHOST_TRACE_VERBOSE
+  TRENTRY(EHCI_VTRACE1_PORTSC_CSC, TR_FMT1,
+          "EHCI Connect Status Change: %06x\n"),
+  TRENTRY(EHCI_VTRACE1_PORTSC_CONNALREADY, TR_FMT1,
+          "EHCI Already connected: %06x\n"),
+  TRENTRY(EHCI_VTRACE1_PORTSC_DISCALREADY, TR_FMT1,
+          "EHCI Already disconnected: %06x\n"),
+  TRENTRY(EHCI_VTRACE1_TOPHALF, TR_FMT1,
+          "EHCI Interrupt: %06x\n"),
+  TRENTRY(EHCI_VTRACE1_AAINTR, TR_FMT1,
+          "EHCI Async Advance Interrupt\n"),
+
+  TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_FMT1,
+          "EHCI Hub port %d: Enumerate the device\n"),
+  TRENTRY(EHCI_VTRACE1_USBINTR, TR_FMT1,
+          "EHCI USB Interrupt (USBINT) Interrupt: %06x\n"),
+  TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_FMT1,
+          "EHCI Enumeration not connected\n"),
+  TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_FMT1,
+          "EHCI Initializing EHCI Stack\n"),
+  TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_FMT1,
+          "EHCI HCCPARAMS=%06x\n"),
+  TRENTRY(EHCI_VTRACE1_INIITIALIZED, TR_FMT1,
+          "EHCI USB EHCI Initialized\n"),
+#endif
+};
+
+static const struct kinetis_ehci_trace_s g_trace2[TRACE2_NSTRINGS] =
+{
+  TRENTRY(EHCI_TRACE2_EPSTALLED, TR_FMT2,
+          "EHCI EP%d Stalled: TOKEN=%04x\n"),
+  TRENTRY(EHCI_TRACE2_EPIOERROR, TR_FMT2,
+          "EHCI ERROR: EP%d TOKEN=%04x\n"),
+  TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_FMT2,
+          "EHCI Hub port %d usbhost_enumerate() failed: %d\n"),
+
+#ifdef HAVE_USBHOST_TRACE_VERBOSE
+  TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_FMT2,
+          "EHCI Async transfer EP%d buflen=%d\n"),
+  TRENTRY(EHCI_VTRACE2_INTRXFR, TR_FMT2,
+          "EHCI Intr Transfer EP%d buflen=%d\n"),
+  TRENTRY(EHCI_VTRACE2_IOCCHECK, TR_FMT2,
+          "EHCI IOC EP%d TOKEN=%04x\n"),
+  TRENTRY(EHCI_VTRACE2_PORTSC, TR_FMT2,
+          "EHCI PORTSC%d: %04x\n"),
+  TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_FMT2,
+          "EHCI RHPort%d connected, pscwait: %d\n"),
+  TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_FMT2,
+          "EHCI RHport%d disconnected, pscwait: %d\n"),
+  TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_FMT2,
+          "EHCI RHPort%d connected: %d\n"),
+
+  TRENTRY(EHCI_VTRACE2_EPALLOC, TR_FMT2,
+          "EHCI EPALLOC: EP%d TYPE=%d\n"),
+  TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_FMT2,
+          "EHCI CTRLIN/OUT: RHPort%d req: %02x\n"),
+  TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_FMT2,
+          "EHCI HCIVERSION %x.%02x\n"),
+  TRENTRY(EHCI_VTRACE2_HCSPARAMS, TR_FMT2,
+          "EHCI nports=%d, HCSPARAMS=%04x\n"),
+#endif
+};
+#endif /* HAVE_USBHOST_TRACE */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kinetis_read16
+ *
+ * Description:
+ *   Read 16-bit little endian data
+ *
+ ****************************************************************************/
+
+static uint16_t kinetis_read16(const uint8_t *addr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+  return (uint16_t)addr[0] << 8 | (uint16_t)addr[1];
+#else
+  return (uint16_t)addr[1] << 8 | (uint16_t)addr[0];
+#endif
+}
+
+/****************************************************************************
+ * Name: kinetis_read32
+ *
+ * Description:
+ *   Read 32-bit little endian data
+ *
+ ****************************************************************************/
+
+static inline uint32_t kinetis_read32(const uint8_t *addr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+  return (uint32_t)kinetis_read16(&addr[0]) << 16 |
+         (uint32_t)kinetis_read16(&addr[2]);
+#else
+  return (uint32_t)kinetis_read16(&addr[2]) << 16 |
+         (uint32_t)kinetis_read16(&addr[0]);
+#endif
+}
+
+/****************************************************************************
+ * Name: kinetis_write16
+ *
+ * Description:
+ *   Write 16-bit little endian data
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static void kinetis_write16(uint16_t memval, uint8_t *addr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+  addr[0] = memval & 0xff;
+  addr[1] = memval >> 8;
+#else
+  addr[0] = memval >> 8;
+  addr[1] = memval & 0xff;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_write32
+ *
+ * Description:
+ *   Write 32-bit little endian data
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static void kinetis_write32(uint32_t memval, uint8_t *addr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+  kinetis_write16(memval >> 16, &addr[0]);
+  kinetis_write16(memval & 0xffff, &addr[2]);
+#else
+  kinetis_write16(memval & 0xffff, &addr[0]);
+  kinetis_write16(memval >> 16, &addr[2]);
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_swap16
+ *
+ * Description:
+ *   Swap bytes on a 16-bit value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t kinetis_swap16(uint16_t value)
+{
+  return ((value >> 8) & 0xff) | ((value & 0xff) << 8);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_swap32
+ *
+ * Description:
+ *   Swap bytes on a 32-bit value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint32_t kinetis_swap32(uint32_t value)
+{
+  return (uint32_t)kinetis_swap16((uint16_t)((value >> 16) & 0xffff)) |
+         (uint32_t)kinetis_swap16((uint16_t)(value & 0xffff)) << 16;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_printreg
+ *
+ * Description:
+ *   Print the contents of a KINETIS EHCI register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_printreg(volatile uint32_t *regaddr, uint32_t regval,
+                           bool iswrite)
+{
+  uinfo("%08x%s%08x\n", (uintptr_t)regaddr, iswrite ? "<-" : "->", regval);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_checkreg
+ *
+ * Description:
+ *   Check if it is time to output debug information for accesses to a KINETIS
+ *   EHCI register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_checkreg(volatile uint32_t *regaddr, uint32_t regval,
+                           bool iswrite)
+{
+  static uint32_t *prevaddr = NULL;
+  static uint32_t preval = 0;
+  static uint32_t count = 0;
+  static bool     prevwrite = false;
+
+  /* Is this the same value that we read from/wrote to the same register last
+   * time?  Are we polling the register?  If so, suppress the output.
+   */
+
+  if (regaddr == prevaddr && regval == preval && prevwrite == iswrite)
+    {
+      /* Yes.. Just increment the count */
+
+      count++;
+    }
+  else
+    {
+      /* No this is a new address or value or operation. Were there any
+       * duplicate accesses before this one?
+       */
+
+      if (count > 0)
+        {
+          /* Yes.. Just one? */
+
+          if (count == 1)
+            {
+              /* Yes.. Just one */
+
+              kinetis_printreg(prevaddr, preval, prevwrite);
+            }
+          else
+            {
+              /* No.. More than one. */
+
+              uinfo("[repeats %d more times]\n", count);
+            }
+        }
+
+      /* Save the new address, value, count, and operation for next time */
+
+      prevaddr  = (uint32_t *)regaddr;
+      preval    = regval;
+      count     = 0;
+      prevwrite = iswrite;
+
+      /* Show the new register access */
+
+      kinetis_printreg(regaddr, regval, iswrite);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_getreg
+ *
+ * Description:
+ *   Get the contents of an KINETIS register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static uint32_t kinetis_getreg(volatile uint32_t *regaddr)
+{
+  /* Read the value from the register */
+
+  uint32_t regval = *regaddr;
+
+  /* Check if we need to print this value */
+
+  kinetis_checkreg(regaddr, regval, false);
+  return regval;
+}
+#else
+static inline uint32_t kinetis_getreg(volatile uint32_t *regaddr)
+{
+  return *regaddr;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_putreg
+ *
+ * Description:
+ *   Set the contents of an KINETIS register to a value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_putreg(uint32_t regval, volatile uint32_t *regaddr)
+{
+  /* Check if we need to print this value */
+
+  kinetis_checkreg(regaddr, regval, true);
+
+  /* Write the value */
+
+  *regaddr = regval;
+}
+#else
+static inline void kinetis_putreg(uint32_t regval, volatile uint32_t *regaddr)
+{
+  *regaddr = regval;
+}
+#endif
+
+/****************************************************************************
+ * Name: ehci_wait_usbsts
+ *
+ * Description:
+ *   Wait for either (1) a field in the USBSTS register to take a specific
+ *   value, (2) for a timeout to occur, or (3) a error to occur.  Return
+ *   a value to indicate which terminated the wait.
+ *
+ ****************************************************************************/
+
+static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits,
+                            unsigned int delay)
+{
+  uint32_t regval;
+  unsigned int timeout;
+
+  timeout = 0;
+  do
+    {
+      /* Wait 5usec before trying again */
+
+      up_udelay(5);
+      timeout += 5;
+
+      /* Read the USBSTS register and check for a system error */
+
+      regval = kinetis_getreg(&HCOR->usbsts);
+      if ((regval & EHCI_INT_SYSERROR) != 0)
+        {
+          usbhost_trace1(EHCI_TRACE1_SYSTEMERROR, regval);
+          return -EIO;
+        }
+
+      /* Mask out the bits of interest */
+
+      regval &= maskbits;
+
+      /* Loop until the masked bits take the specified value or until a
+       * timeout occurs.
+       */
+    }
+  while (regval != donebits && timeout < delay);
+
+  /* We got here because either the waited for condition or a timeout
+   * occurred.  Return a value to indicate which.
+   */
+
+  return (regval == donebits) ? OK : -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: kinetis_takesem
+ *
+ * Description:
+ *   This is just a wrapper to handle the annoying behavior of semaphore
+ *   waits that return due to the receipt of a signal.
+ *
+ ****************************************************************************/
+
+static int kinetis_takesem(sem_t *sem)
+{
+  return nxsem_wait_uninterruptible(sem);
+}
+
+/****************************************************************************
+ * Name: kinetis_takesem_noncancelable
+ *
+ * Description:
+ *   This is just a wrapper to handle the annoying behavior of semaphore
+ *   waits that return due to the receipt of a signal.  This version also
+ *   ignores attempts to cancel the thread.
+ *
+ ****************************************************************************/
+
+static int kinetis_takesem_noncancelable(sem_t *sem)
+{
+  int result;
+  int ret = OK;
+
+  do
+    {
+      result = nxsem_wait_uninterruptible(sem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(result == OK || result == -ECANCELED);
+      if (ret == OK && result < 0)
+        {
+          ret = result;
+        }
+    }
+  while (result < 0);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_alloc
+ *
+ * Description:
+ *   Allocate a Queue Head (QH) structure by removing it from the free list
+ *
+ * Assumption:  Caller holds the exclsem
+ *
+ ****************************************************************************/
+
+static struct kinetis_qh_s *kinetis_qh_alloc(void)
+{
+  struct kinetis_qh_s *qh;
+
+  /* Remove the QH structure from the freelist */
+
+  qh = (struct kinetis_qh_s *)g_ehci.qhfree;
+  if (qh)
+    {
+      g_ehci.qhfree = ((struct kinetis_list_s *)qh)->flink;
+      memset(qh, 0, sizeof(struct kinetis_qh_s));
+    }
+
+  return qh;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_free
+ *
+ * Description:
+ *   Free a Queue Head (QH) structure by returning it to the free list
+ *
+ * Assumption:  Caller holds the exclsem
+ *
+ ****************************************************************************/
+
+static void kinetis_qh_free(struct kinetis_qh_s *qh)
+{
+  struct kinetis_list_s *entry = (struct kinetis_list_s *)qh;
+
+  /* Put the QH structure back into the free list */
+
+  entry->flink  = g_ehci.qhfree;
+  g_ehci.qhfree = entry;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_alloc
+ *
+ * Description:
+ *   Allocate a Queue Element Transfer Descriptor (qTD) by removing it from
+ *   the free list
+ *
+ * Assumption:  Caller holds the exclsem
+ *
+ ****************************************************************************/
+
+static struct kinetis_qtd_s *kinetis_qtd_alloc(void)
+{
+  struct kinetis_qtd_s *qtd;
+
+  /* Remove the qTD from the freelist */
+
+  qtd = (struct kinetis_qtd_s *)g_ehci.qtdfree;
+  if (qtd)
+    {
+      g_ehci.qtdfree = ((struct kinetis_list_s *)qtd)->flink;
+      memset(qtd, 0, sizeof(struct kinetis_qtd_s));
+    }
+
+  return qtd;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_free
+ *
+ * Description:
+ *   Free a Queue Element Transfer Descriptor (qTD) by returning it to the
+ *   free list
+ *
+ * Assumption:
+ *   Caller holds the exclsem
+ *
+ ****************************************************************************/
+
+static void kinetis_qtd_free(struct kinetis_qtd_s *qtd)
+{
+  struct kinetis_list_s *entry = (struct kinetis_list_s *)qtd;
+
+  /* Put the qTD back into the free list */
+
+  entry->flink   = g_ehci.qtdfree;
+  g_ehci.qtdfree = entry;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_foreach
+ *
+ * Description:
+ *   Give the first entry in a list of Queue Head (QH) structures, call the
+ *   handler for each QH structure in the list (including the one at the head
+ *   of the list).
+ *
+ ****************************************************************************/
+
+static int kinetis_qh_foreach(struct kinetis_qh_s *qh, uint32_t **bp,
+                            foreach_qh_t handler, void *arg)
+{
+  struct kinetis_qh_s *next;
+  uintptr_t physaddr;
+  int ret;
+
+  DEBUGASSERT(qh && handler);
+  while (qh)
+    {
+      /* Is this the end of the list?  Check the horizontal link pointer
+       * (HLP) terminate (T) bit.  If T==1, then the HLP address is not
+       * valid.
+       */
+
+      physaddr = kinetis_swap32(qh->hw.hlp);
+      if ((physaddr & QH_HLP_T) != 0)
+        {
+          /* Set the next pointer to NULL.  This will terminate the loop. */
+
+          next = NULL;
+        }
+
+      /* Is the next QH the asynchronous list head which will always be at
+       * the end of the asynchronous queue?
+       */
+
+      else if (kinetis_virtramaddr(physaddr & QH_HLP_MASK) ==
+               (uintptr_t)&g_asynchead)
+        {
+          /* That will also terminate the loop */
+
+          next = NULL;
+        }
+
+      /* Otherwise, there is a QH structure after this one that describes
+       * another transaction.
+       */
+
+      else
+        {
+          physaddr = kinetis_swap32(qh->hw.hlp) & QH_HLP_MASK;
+          next     = (struct kinetis_qh_s *)kinetis_virtramaddr(physaddr);
+        }
+
+      /* Perform the user action on this entry.  The action might result in
+       * unlinking the entry!  But that is okay because we already have the
+       * next QH pointer.
+       *
+       * Notice that we do not manage the back pointer (bp).  If the call-
+       * out uses it, it must update it as necessary.
+       */
+
+      ret = handler(qh, bp, arg);
+
+      /* If the handler returns any non-zero value, then terminate the
+       * traversal early.
+       */
+
+      if (ret != 0)
+        {
+          return ret;
+        }
+
+      /* Set up to visit the next entry */
+
+      qh = next;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_foreach
+ *
+ * Description:
+ *   Give a Queue Head (QH) instance, call the handler for each qTD structure
+ *   in the queue.
+ *
+ ****************************************************************************/
+
+static int kinetis_qtd_foreach(struct kinetis_qh_s *qh, foreach_qtd_t handler,
+                             void *arg)
+{
+  struct kinetis_qtd_s *qtd;
+  struct kinetis_qtd_s *next;
+  uintptr_t physaddr;
+  uint32_t *bp;
+  int ret;
+
+  DEBUGASSERT(qh && handler);
+
+  /* Handle the special case where the queue is empty */
+
+  bp       = &qh->fqp;          /* Start of qTDs in original list */
+  physaddr = kinetis_swap32(*bp); /* Physical address of first qTD in CPU order */
+
+  if ((physaddr & QTD_NQP_T) != 0)
+    {
+      return 0;
+    }
+
+  /* Start with the first qTD in the list */
+
+  qtd  = (struct kinetis_qtd_s *)kinetis_virtramaddr(physaddr);
+  next = NULL;
+
+  /* And loop until we encounter the end of the qTD list */
+
+  while (qtd)
+    {
+      /* Is this the end of the list?  Check the next qTD pointer (NQP)
+       * terminate (T) bit.  If T==1, then the NQP address is not valid.
+       */
+
+      if ((kinetis_swap32(qtd->hw.nqp) & QTD_NQP_T) != 0)
+        {
+          /* Set the next pointer to NULL.  This will terminate the loop. */
+
+          next = NULL;
+        }
+      else
+        {
+          physaddr = kinetis_swap32(qtd->hw.nqp) & QTD_NQP_NTEP_MASK;
+          next     = (struct kinetis_qtd_s *)kinetis_virtramaddr(physaddr);
+        }
+
+      /* Perform the user action on this entry.  The action might result in
+       * unlinking the entry!  But that is okay because we already have the
+       * next qTD pointer.
+       *
+       * Notice that we do not manage the back pointer (bp).  If the call-out
+       * uses it, it must update it as necessary.
+       */
+
+      ret = handler(qtd, &bp, arg);
+
+      /* If the handler returns any non-zero value, then terminate the
+       * traversal early.
+       */
+
+      if (ret != 0)
+        {
+          return ret;
+        }
+
+      /* Set up to visit the next entry */
+
+      qtd = next;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_discard
+ *
+ * Description:
+ *   This is a kinetis_qtd_foreach callback.  It simply unlinks the QTD,
+ *   updates the back pointer, and frees the QTD structure.
+ *
+ ****************************************************************************/
+
+static int kinetis_qtd_discard(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                             void *arg)
+{
+  DEBUGASSERT(qtd && bp && *bp);
+
+  /* Remove the qTD from the list by updating the forward pointer to skip
+   * around this qTD.  We do not change that pointer because are repeatedly
+   * removing the aTD at the head of the QH list.
+   */
+
+  **bp = qtd->hw.nqp;
+
+  /* Then free the qTD */
+
+  kinetis_qtd_free(qtd);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_discard
+ *
+ * Description:
+ *   Free the Queue Head (QH) and all qTD's attached to the QH.
+ *
+ * Assumptions:
+ *   The QH structure itself has already been unlinked from whatever list it
+ *   may have been in.
+ *
+ ****************************************************************************/
+
+static int kinetis_qh_discard(struct kinetis_qh_s *qh)
+{
+  int ret;
+
+  DEBUGASSERT(qh);
+
+  /* Free all of the qTD's attached to the QH */
+
+  ret = kinetis_qtd_foreach(qh, kinetis_qtd_discard, NULL);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret);
+    }
+
+  /* Then free the QH itself */
+
+  kinetis_qh_free(qh);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_invalidate
+ *
+ * Description:
+ *   This is a callback from kinetis_qtd_foreach.  It simply invalidates D-
+ *   cache for address range of the qTD entry.
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static int kinetis_qtd_invalidate(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                                void *arg)
+{
+  /* Invalidate the D-Cache, i.e., force reloading of the D-Cache from memory
+   * memory over the specified address range.
+   */
+
+  up_invalidate_dcache((uintptr_t)&qtd->hw,
+                       (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qh_invalidate
+ *
+ * Description:
+ *   Invalidate the Queue Head and all qTD entries in the queue.
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static int kinetis_qh_invalidate(struct kinetis_qh_s *qh)
+{
+  /* Invalidate the QH first so that we reload the qTD list head */
+
+  up_invalidate_dcache((uintptr_t)&qh->hw,
+                       (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
+
+  /* Then invalidate all of the qTD entries in the queue */
+
+  return kinetis_qtd_foreach(qh, kinetis_qtd_invalidate, NULL);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qtd_flush
+ *
+ * Description:
+ *   This is a callback from kinetis_qtd_foreach.  It simply flushes D-cache
+ *   for address range of the qTD entry.
+ *
+ ****************************************************************************/
+
+static int kinetis_qtd_flush(struct kinetis_qtd_s *qtd, uint32_t **bp, void *arg)
+{
+  /* Flush the D-Cache, i.e., make the contents of the memory match the
+   * contents of the D-Cache in the specified address range and invalidate
+   * the D-Cache to force re-loading of the data from memory when next
+   * accessed.
+   */
+
+  up_flush_dcache((uintptr_t)&qtd->hw,
+                  (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_flush
+ *
+ * Description:
+ *   Invalidate the Queue Head and all qTD entries in the queue.
+ *
+ ****************************************************************************/
+
+static int kinetis_qh_flush(struct kinetis_qh_s *qh)
+{
+  /* Flush the QH first.  This will write the contents of the D-cache to RAM
+   * and invalidate the contents of the D-cache so that the next access will
+   * be reloaded from D-Cache.
+   */
+
+  up_flush_dcache((uintptr_t)&qh->hw,
+                  (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
+
+  /* Then flush all of the qTD entries in the queue */
+
+  return kinetis_qtd_foreach(qh, kinetis_qtd_flush, NULL);
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_print
+ *
+ * Description:
+ *   Print the context of one qTD
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_qtd_print(struct kinetis_qtd_s *qtd)
+{
+  uinfo("  QTD[%p]:\n", qtd);
+  uinfo("    hw:\n");
+  uinfo("      nqp: %08x alt: %08x token: %08x\n",
+        qtd->hw.nqp, qtd->hw.alt, qtd->hw.token);
+  uinfo("      bpl: %08x %08x %08x %08x %08x\n",
+        qtd->hw.bpl[0], qtd->hw.bpl[1], qtd->hw.bpl[2],
+        qtd->hw.bpl[3], qtd->hw.bpl[4]);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qh_print
+ *
+ * Description:
+ *   Print the context of one QH
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static void kinetis_qh_print(struct kinetis_qh_s *qh)
+{
+  struct kinetis_epinfo_s *epinfo;
+  struct ehci_overlay_s *overlay;
+
+  uinfo("QH[%p]:\n", qh);
+  uinfo("  hw:\n");
+  uinfo("    hlp: %08x epchar: %08x epcaps: %08x cqp: %08x\n",
+        qh->hw.hlp, qh->hw.epchar, qh->hw.epcaps, qh->hw.cqp);
+
+  overlay = &qh->hw.overlay;
+  uinfo("  overlay:\n");
+  uinfo("    nqp: %08x alt: %08x token: %08x\n",
+        overlay->nqp, overlay->alt, overlay->token);
+  uinfo("    bpl: %08x %08x %08x %08x %08x\n",
+        overlay->bpl[0], overlay->bpl[1], overlay->bpl[2],
+        overlay->bpl[3], overlay->bpl[4]);
+
+  uinfo("  fqp:\n", qh->fqp);
+
+  epinfo = qh->epinfo;
+  uinfo("  epinfo[%p]:\n", epinfo);
+  if (epinfo)
+    {
+      uinfo("    EP%d DIR=%s FA=%08x TYPE=%d MaxPacket=%d\n",
+            epinfo->epno, epinfo->dirin ? "IN" : "OUT", epinfo->devaddr,
+            epinfo->xfrtype, epinfo->maxpacket);
+      uinfo("    Toggle=%d iocwait=%d speed=%d result=%d\n",
+            epinfo->toggle, epinfo->iocwait, epinfo->speed, epinfo->result);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qtd_dump
+ *
+ * Description:
+ *   This is a kinetis_qtd_foreach callout function.  It dumps the context of
+ *   one qTD
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static int kinetis_qtd_dump(struct kinetis_qtd_s *qtd, uint32_t **bp, void *arg)
+{
+  kinetis_qtd_print(qtd);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qh_dump
+ *
+ * Description:
+ *   This is a kinetis_qh_foreach call-out function.  It dumps a QH structure
+ *   and all of the qTD structures linked to the QH.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KINETIS_EHCI_REGDEBUG
+static int kinetis_qh_dump(struct kinetis_qh_s *qh, uint32_t **bp, void *arg)
+{
+  kinetis_qh_print(qh);
+  return kinetis_qtd_foreach(qh, kinetis_qtd_dump, NULL);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_ehci_speed
+ *
+ * Description:
+ *  Map a speed enumeration value per Chapter 9 of the USB specification to
+ *  the speed enumeration required in the EHCI queue head.
+ *
+ ****************************************************************************/
+
+static inline uint8_t kinetis_ehci_speed(uint8_t usbspeed)
+{
+  DEBUGASSERT(usbspeed >= USB_SPEED_LOW && usbspeed <= USB_SPEED_HIGH);
+  return g_ehci_speed[usbspeed];
+}
+
+/****************************************************************************
+ * Name: kinetis_ioc_setup
+ *
+ * Description:
+ *   Set the request for the IOC event well BEFORE enabling the transfer (as
+ *   soon as we are absolutely committed to the to avoid transfer).  We do
+ *   this to minimize race conditions.  This logic would have to be expanded
+ *   if we want to have more than one packet in flight at a time!
+ *
+ * Assumption:  The caller holds the EHCI exclsem
+ *
+ ****************************************************************************/
+
+static int kinetis_ioc_setup(struct kinetis_rhport_s *rhport,
+                           struct kinetis_epinfo_s *epinfo)
+{
+  irqstate_t flags;
+  int ret = -ENODEV;
+
+  DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
+#ifdef CONFIG_USBHOST_ASYNCH
+  DEBUGASSERT(epinfo->callback == NULL);
+#endif
+
+  /* Is the device still connected? */
+
+  flags = enter_critical_section();
+  if (rhport->connected)
+    {
+      /* Then set iocwait to indicate that we expect to be informed when
+       * either (1) the device is disconnected, or (2) the transfer
+       * completed.
+       */
+
+      epinfo->iocwait  = true;   /* We want to be awakened by IOC interrupt */
+      epinfo->status   = 0;      /* No status yet */
+      epinfo->xfrd     = 0;      /* Nothing transferred yet */
+      epinfo->result   = -EBUSY; /* Transfer in progress */
+#ifdef CONFIG_USBHOST_ASYNCH
+      epinfo->callback = NULL;   /* No asynchronous callback */
+      epinfo->arg      = NULL;
+#endif
+      ret              = OK;     /* We are good to go */
+    }
+
+  leave_critical_section(flags);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_ioc_wait
+ *
+ * Description:
+ *   Wait for the IOC event.
+ *
+ * Assumption:  The caller does *NOT* hold the EHCI exclsem.  That would
+ * cause a deadlock when the bottom-half, worker thread needs to take the
+ * semaphore.
+ *
+ ****************************************************************************/
+
+static int kinetis_ioc_wait(struct kinetis_epinfo_s *epinfo)
+{
+  int ret = OK;
+
+  /* Wait for the IOC event.  Loop to handle any false alarm semaphore
+   * counts.  Return an error if the task is canceled.
+   */
+
+  while (epinfo->iocwait)
+    {
+      ret = kinetis_takesem(&epinfo->iocsem);
+      if (ret < 0)
+        {
+          break;
+        }
+    }
+
+  return ret < 0 ? ret : epinfo->result;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_enqueue
+ *
+ * Description:
+ *   Add a new, ready-to-go QH w/attached qTDs to the asynchronous queue.
+ *
+ * Assumptions:  The caller holds the EHCI exclsem
+ *
+ ****************************************************************************/
+
+static void kinetis_qh_enqueue(struct kinetis_qh_s *qhead, struct kinetis_qh_s *qh)
+{
+  uintptr_t physaddr;
+
+  /* Set the internal fqp field.  When we transverse the QH list later,
+   * we need to know the correct place to start because the overlay may no
+   * longer point to the first qTD entry.
+   */
+
+  qh->fqp = qh->hw.overlay.nqp;
+  kinetis_qh_dump(qh, NULL, NULL);
+
+  /* Add the new QH to the head of the asynchronous queue list.
+   *
+   * First, attach the old head as the new QH HLP and flush the new QH and
+   * its attached qTDs to RAM.
+   */
+
+  qh->hw.hlp = qhead->hw.hlp;
+  kinetis_qh_flush(qh);
+
+  /* Then set the new QH as the first QH in the asynchronous queue and flush
+   * the modified head to RAM.
+   */
+
+  physaddr = (uintptr_t)kinetis_physramaddr((uintptr_t)qh);
+  qhead->hw.hlp = kinetis_swap32(physaddr | QH_HLP_TYP_QH);
+
+  up_flush_dcache((uintptr_t)&qhead->hw,
+                  (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s));
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_create
+ *
+ * Description:
+ *   Create a new Queue Head (QH)
+ *
+ ****************************************************************************/
+
+static struct kinetis_qh_s *kinetis_qh_create(struct kinetis_rhport_s *rhport,
+                                          struct kinetis_epinfo_s *epinfo)
+{
+  struct kinetis_qh_s *qh;
+  uint32_t rhpndx;
+  uint32_t regval;
+  uint8_t hubaddr;
+  uint8_t hubport;
+
+  /* Allocate a new queue head structure */
+
+  qh = kinetis_qh_alloc();
+  if (qh == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_QHALLOC_FAILED, 0);
+      return NULL;
+    }
+
+  /* Save the endpoint information with the QH itself */
+
+  qh->epinfo = epinfo;
+
+  /* Write QH endpoint characteristics:
+   *
+   * FIELD    DESCRIPTION                     VALUE/SOURCE
+   * -------- ------------------------------- --------------------
+   * DEVADDR  Device address                  Endpoint structure
+   * I        Inactivate on Next Transaction  0
+   * ENDPT    Endpoint number                 Endpoint structure
+   * EPS      Endpoint speed                  Endpoint structure
+   * DTC      Data toggle control             1
+   * MAXPKT   Max packet size                 Endpoint structure
+   * C        Control endpoint                Calculated
+   * RL       NAK count reloaded              8
+   */
+
+  regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) |
+           ((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) |
+           ((uint32_t)kinetis_ehci_speed(epinfo->speed) <<
+            QH_EPCHAR_EPS_SHIFT) |
+           QH_EPCHAR_DTC |
+           ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) |
+           ((uint32_t)8 << QH_EPCHAR_RL_SHIFT);
+
+  /* Paragraph 3.6.3: "Control Endpoint Flag (C). If the QH.EPS field
+   * indicates the endpoint is not a high-speed device, and the endpoint
+   * is an control endpoint, then software must set this bit to a one.
+   * Otherwise it should always set this bit to a zero."
+   */
+
+  if (epinfo->speed   != USB_SPEED_HIGH &&
+      epinfo->xfrtype == USB_EP_ATTR_XFER_CONTROL)
+    {
+      regval |= QH_EPCHAR_C;
+    }
+
+  /* Save the endpoint characteristics word with the correct byte order */
+
+  qh->hw.epchar = kinetis_swap32(regval);
+
+  /* Write QH endpoint capabilities
+   *
+   * FIELD    DESCRIPTION                     VALUE/SOURCE
+   * -------- ------------------------------- --------------------
+   * SSMASK   Interrupt Schedule Mask         Depends on epinfo->xfrtype
+   * SCMASK   Split Completion Mask           0
+   * HUBADDR  Hub Address                     Always 0 for now
+   * PORT     Port number                     RH port index + 1
+   * MULT     High band width multiplier      1
+   */
+
+  rhpndx  = RHPNDX(rhport);
+
+#ifdef CONFIG_USBHOST_HUB
+  /* REVISIT:  Future HUB support will require the HUB port number
+   * and HUB device address to be included here:
+   *
+   * - The HUB device address is the USB device address of the USB 2.0 Hub
+   *   below which a full- or low-speed device is attached.
+   * - The HUB port number is the port number on the above USB 2.0 Hub
+   *
+   * These fields are used in the split-transaction protocol.  The kludge
+   * below should work for hubs connected directly to a root hub port,
+   * but would not work for devices connected to downstream hubs.
+   */
+
+#warning Missing logic
+  hubaddr = rhport->ep0.devaddr;
+  hubport = rhpndx + 1;
+#else
+  hubaddr = rhport->ep0.devaddr;
+  hubport = rhpndx + 1;
+#endif
+
+  regval  = ((uint32_t)hubaddr << QH_EPCAPS_HUBADDR_SHIFT) |
+            ((uint32_t)hubport << QH_EPCAPS_PORT_SHIFT) |
+            ((uint32_t)1       << QH_EPCAPS_MULT_SHIFT);
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+  if (epinfo->xfrtype == USB_EP_ATTR_XFER_INT)
+    {
+      /* Here, the S-Mask field in the queue head is set to 1, indicating
+       * that the transaction for the endpoint should be executed on the bus
+       * during micro-frame 0 of the frame.
+       *
+       * REVISIT: The polling interval should be controlled by the which
+       * entry is the framelist holds the QH pointer for a given micro-frame
+       * and the QH pointer should be replicated for different polling rates.
+       * This implementation currently just sets all frame_list entry to
+       * all the same interrupt queue.  That should work but will not give
+       * any control over polling rates.
+       */
+#warning REVISIT
+
+      regval |= ((uint32_t)1               << QH_EPCAPS_SSMASK_SHIFT);
+    }
+#endif
+
+  qh->hw.epcaps = kinetis_swap32(regval);
+
+  /* Mark this as the end of this list.  This will be overwritten if/when the
+   * next qTD is added to the queue.
+   */
+
+  qh->hw.hlp         = kinetis_swap32(QH_HLP_T);
+  qh->hw.overlay.nqp = kinetis_swap32(QH_NQP_T);
+  qh->hw.overlay.alt = kinetis_swap32(QH_AQP_T);
+  return qh;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_addbpl
+ *
+ * Description:
+ *   Add a buffer pointer list to a qTD.
+ *
+ ****************************************************************************/
+
+static int kinetis_qtd_addbpl(struct kinetis_qtd_s *qtd, const void *buffer,
+                            size_t buflen)
+{
+  uint32_t physaddr;
+  uint32_t nbytes;
+  uint32_t next;
+  int ndx;
+
+  /* Flush the contents of the data buffer to RAM so that the correct
+   * contents will be accessed for an OUT DMA.
+   */
+
+  up_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+
+  /* Loop, adding the aligned physical addresses of the buffer to the buffer
+   * page list.  Only the first entry need not be aligned (because only the
+   * first entry has the offset field). The subsequent entries must begin on
+   * 4KB address boundaries.
+   */
+
+  physaddr = (uint32_t)kinetis_physramaddr((uintptr_t)buffer);
+
+  for (ndx = 0; ndx < 5; ndx++)
+    {
+      /* Write the physical address of the buffer into the qTD buffer pointer
+       * list.
+       */
+
+      qtd->hw.bpl[ndx] = kinetis_swap32(physaddr);
+
+      /* Get the next buffer pointer (in the case where we will have to
+       * transfer more then one chunk).  This buffer must be aligned to a
+       * 4KB address boundary.
+       */
+
+      next = (physaddr + 4096) & ~4095;
+
+      /* How many bytes were included in the last buffer?  Was it the whole
+       * thing?
+       */
+
+      nbytes = next - physaddr;
+      if (nbytes >= buflen)
+        {
+          /* Yes... it was the whole thing.  Break out of the loop early. */
+
+          break;
+        }
+
+      /* Adjust the buffer length and physical address for the next time
+       * through the loop.
+       */
+
+      buflen  -= nbytes;
+      physaddr = next;
+    }
+
+  /* Handle the case of a huge buffer > 4*4KB = 16KB */
+
+  if (ndx >= 5)
+    {
+      usbhost_trace1(EHCI_TRACE1_BUFTOOBIG, buflen);
+      return -EFBIG;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_setupphase
+ *
+ * Description:
+ *   Create a SETUP phase request qTD.
+ *
+ ****************************************************************************/
+
+static struct kinetis_qtd_s *
+  kinetis_qtd_setupphase(struct kinetis_epinfo_s *epinfo,
+                       const struct usb_ctrlreq_s *req)
+{
+  struct kinetis_qtd_s *qtd;
+  uint32_t regval;
+  int ret;
+
+  /* Allocate a new Queue Element Transfer Descriptor (qTD) */
+
+  qtd = kinetis_qtd_alloc();
+  if (qtd == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0);
+      return NULL;
+    }
+
+  /* Mark this as the end of the list (this will be overwritten if another
+   * qTD is added after this one).
+   */
+
+  qtd->hw.nqp = kinetis_swap32(QTD_NQP_T);
+  qtd->hw.alt = kinetis_swap32(QTD_AQP_T);
+
+  /* Write qTD token:
+   *
+   * FIELD    DESCRIPTION                     VALUE/SOURCE
+   * -------- ------------------------------- --------------------
+   * STATUS   Status                          QTD_TOKEN_ACTIVE
+   * PID      PID Code                        QTD_TOKEN_PID_SETUP
+   * CERR     Error Counter                   3
+   * CPAGE    Current Page                    0
+   * IOC      Interrupt on complete           0
+   * NBYTES   Total Bytes to Transfer         USB_SIZEOF_CTRLREQ
+   * TOGGLE   Data Toggle                     0
+   */
+
+  regval = QTD_TOKEN_ACTIVE | QTD_TOKEN_PID_SETUP |
+           ((uint32_t)3                  << QTD_TOKEN_CERR_SHIFT) |
+           ((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT);
+
+  qtd->hw.token = kinetis_swap32(regval);
+
+  /* Add the buffer data */
+
+  ret = kinetis_qtd_addbpl(qtd, req, USB_SIZEOF_CTRLREQ);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret);
+      kinetis_qtd_free(qtd);
+      return NULL;
+    }
+
+  /* Add the data transfer size to the count in the epinfo structure */
+
+  epinfo->xfrd += USB_SIZEOF_CTRLREQ;
+
+  return qtd;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_dataphase
+ *
+ * Description:
+ *   Create a data transfer or SET data phase qTD.
+ *
+ ****************************************************************************/
+
+static struct kinetis_qtd_s *kinetis_qtd_dataphase(struct kinetis_epinfo_s *epinfo,
+                                               void *buffer, int buflen,
+                                               uint32_t tokenbits)
+{
+  struct kinetis_qtd_s *qtd;
+  uint32_t regval;
+  int ret;
+
+  /* Allocate a new Queue Element Transfer Descriptor (qTD) */
+
+  qtd = kinetis_qtd_alloc();
+  if (qtd == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_DATAQTDALLOC_FAILED, 0);
+      return NULL;
+    }
+
+  /* Mark this as the end of the list (this will be overwritten if another
+   * qTD is added after this one).
+   */
+
+  qtd->hw.nqp = kinetis_swap32(QTD_NQP_T);
+  qtd->hw.alt = kinetis_swap32(QTD_AQP_T);
+
+  /* Write qTD token:
+   *
+   * FIELD    DESCRIPTION                     VALUE/SOURCE
+   * -------- ------------------------------- --------------------
+   * STATUS   Status                          QTD_TOKEN_ACTIVE
+   * PID      PID Code                        Contained in tokenbits
+   * CERR     Error Counter                   3
+   * CPAGE    Current Page                    0
+   * IOC      Interrupt on complete           Contained in tokenbits
+   * NBYTES   Total Bytes to Transfer         buflen
+   * TOGGLE   Data Toggle                     Contained in tokenbits
+   */
+
+  regval = tokenbits | QTD_TOKEN_ACTIVE |
+           ((uint32_t)3       << QTD_TOKEN_CERR_SHIFT) |
+           ((uint32_t)buflen  << QTD_TOKEN_NBYTES_SHIFT);
+
+  qtd->hw.token = kinetis_swap32(regval);
+
+  /* Add the buffer information to the buffer pointer list */
+
+  ret = kinetis_qtd_addbpl(qtd, buffer, buflen);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret);
+      kinetis_qtd_free(qtd);
+      return NULL;
+    }
+
+  /* Add the data transfer size to the count in the epinfo structure */
+
+  epinfo->xfrd += buflen;
+
+  return qtd;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_statusphase
+ *
+ * Description:
+ *   Create a STATUS phase request qTD.
+ *
+ ****************************************************************************/
+
+static struct kinetis_qtd_s *kinetis_qtd_statusphase(uint32_t tokenbits)
+{
+  struct kinetis_qtd_s *qtd;
+  uint32_t regval;
+
+  /* Allocate a new Queue Element Transfer Descriptor (qTD) */
+
+  qtd = kinetis_qtd_alloc();
+  if (qtd == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0);
+      return NULL;
+    }
+
+  /* Mark this as the end of the list (this will be overwritten if another
+   * qTD is added after this one).
+   */
+
+  qtd->hw.nqp = kinetis_swap32(QTD_NQP_T);
+  qtd->hw.alt = kinetis_swap32(QTD_AQP_T);
+
+  /* Write qTD token:
+   *
+   * FIELD    DESCRIPTION                     VALUE/SOURCE
+   * -------- ------------------------------- --------------------
+   * STATUS   Status                          QTD_TOKEN_ACTIVE
+   * PID      PID Code                        Contained in tokenbits
+   * CERR     Error Counter                   3
+   * CPAGE    Current Page                    0
+   * IOC      Interrupt on complete           QTD_TOKEN_IOC
+   * NBYTES   Total Bytes to Transfer         0
+   * TOGGLE   Data Toggle                     Contained in tokenbits
+   */
+
+  regval = tokenbits | QTD_TOKEN_ACTIVE | QTD_TOKEN_IOC |
+           ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT);
+
+  qtd->hw.token = kinetis_swap32(regval);
+  return qtd;
+}
+
+/****************************************************************************
+ * Name: kinetis_async_setup
+ *
+ * Description:
+ *   Process a IN or OUT request on any asynchronous endpoint (bulk or
+ *   control).  This function will enqueue the request and wait for it to
+ *   complete.  Bulk data transfers differ in that req == NULL and there are
+ *   not SETUP or STATUS phases.
+ *
+ *   This is a blocking function; it will not return until the control
+ *   transfer has completed.
+ *
+ * Assumption:  The caller holds the EHCI exclsem.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+static int kinetis_async_setup(struct kinetis_rhport_s *rhport,
+                             struct kinetis_epinfo_s *epinfo,
+                             const struct usb_ctrlreq_s *req,
+                             uint8_t *buffer, size_t buflen)
+{
+  struct kinetis_qh_s *qh;
+  struct kinetis_qtd_s *qtd;
+  uintptr_t physaddr;
+  uint32_t *flink;
+  uint32_t *alt;
+  uint32_t toggle;
+  bool dirin = false;
+  int ret;
+
+  /* Terse output only if we are tracing */
+
+#ifdef CONFIG_USBHOST_TRACE
+  usbhost_vtrace2(EHCI_VTRACE2_ASYNCXFR, epinfo->epno, buflen);
+#else
+  uinfo("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n",
+        RHPORT(rhport), epinfo->epno, buffer, buflen, req);
+#endif
+
+  DEBUGASSERT(rhport && epinfo);
+
+  /* A buffer may or may be supplied with an EP0 SETUP transfer.  A buffer
+   * will always be present for normal endpoint data transfers.
+   */
+
+  DEBUGASSERT(req || (buffer && buflen > 0));
+
+  /* Create and initialize a Queue Head (QH) structure for this transfer */
+
+  qh = kinetis_qh_create(rhport, epinfo);
+  if (qh == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
+      return -ENOMEM;
+    }
+
+  /* Initialize the QH link and get the next data toggle (not used for SETUP
+   * transfers)
+   */
+
+  flink  = &qh->hw.overlay.nqp;
+  toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
+  ret    = -EIO;
+
+  /* Is there an EP0 SETUP request?  If so, req will be non-NULL and we will
+   * queue two or three qTDs:
+   *
+   *   1) One for the SETUP phase,
+   *   2) One for the DATA phase (if there is data), and
+   *   3) One for the STATUS phase.
+   *
+   * If this is not an EP0 SETUP request, then only a data transfer will be
+   * enqueued.
+   */
+
+  if (req != NULL)
+    {
+      /* Allocate a new Queue Element Transfer Descriptor (qTD) for the SETUP
+       * phase of the request sequence.
+       */
+
+      qtd = kinetis_qtd_setupphase(epinfo, req);
+      if (qtd == NULL)
+        {
+          usbhost_trace1(EHCI_TRACE1_QTDSETUP_FAILED, 0);
+          goto errout_with_qh;
+        }
+
+      /* Link the new qTD to the QH head. */
+
+      physaddr = kinetis_physramaddr((uintptr_t)qtd);
+      *flink = kinetis_swap32(physaddr);
+
+      /* Get the new forward link pointer and data toggle */
+
+      flink  = &qtd->hw.nqp;
+      toggle = QTD_TOKEN_TOGGLE;
+    }
+
+  /* A buffer may or may be supplied with an EP0 SETUP transfer.  A buffer
+   * will always be present for normal endpoint data transfers.
+   */
+
+  alt = NULL;
+  if (buffer != NULL && buflen > 0)
+    {
+      uint32_t tokenbits;
+
+      /* Extra TOKEN bits include the data toggle, the data PID, and if
+       * there is no request, an indication to interrupt at the end of this
+       * transfer.
+       */
+
+      tokenbits = toggle;
+
+      /* Get the data token direction.
+       *
+       * If this is a SETUP request, use the direction contained in the
+       * request.  The IOC bit is not set.
+       */
+
+      if (req)
+        {
+          if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN)
+            {
+              tokenbits |= QTD_TOKEN_PID_IN;
+              dirin      = true;
+            }
+          else
+            {
+              tokenbits |= QTD_TOKEN_PID_OUT;
+              dirin      = false;
+            }
+        }
+
+      /* Otherwise, the endpoint is uni-directional.  Get the direction from
+       * the epinfo structure.  Since this is not an EP0 SETUP request,
+       * nothing follows the data and we want the IOC interrupt when the
+       * data transfer completes.
+       */
+
+      else if (epinfo->dirin)
+        {
+          tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC);
+          dirin      = true;
+        }
+      else
+        {
+          tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC);
+          dirin      = false;
+        }
+
+      /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data
+       * buffer.
+       */
+
+      qtd = kinetis_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
+      if (qtd == NULL)
+        {
+          usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0);
+          goto errout_with_qh;
+        }
+
+      /* Link the new qTD to either QH head of the SETUP qTD. */
+
+      physaddr = kinetis_physramaddr((uintptr_t)qtd);
+      *flink = kinetis_swap32(physaddr);
+
+      /* Set the forward link pointer to this new qTD */
+
+      flink = &qtd->hw.nqp;
+
+      /* If this was an IN transfer, then setup a pointer alternate link.
+       * The EHCI hardware will use this link if a short packet is received.
+       */
+
+      if (dirin)
+        {
+          alt = &qtd->hw.alt;
+        }
+    }
+
+  /* If this is an EP0 SETUP request, then enqueue one more qTD for the
+   * STATUS phase transfer.
+   */
+
+  if (req != NULL)
+    {
+      /* Extra TOKEN bits include the data toggle and the correct data PID. */
+
+      uint32_t tokenbits = toggle;
+
+      /* The status phase direction is the opposite of the data phase.  If
+       * this is an IN request, then we received the buffer and we will send
+       * the zero length packet handshake.
+       */
+
+      if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN)
+        {
+          tokenbits |= QTD_TOKEN_PID_OUT;
+        }
+
+      /* Otherwise, this in an OUT request.  We send the buffer and we expect
+       * to receive the NULL packet handshake.
+       */
+
+      else
+        {
+          tokenbits |= QTD_TOKEN_PID_IN;
+        }
+
+      /* Allocate a new Queue Element Transfer Descriptor (qTD) for the status */
+
+      qtd = kinetis_qtd_statusphase(tokenbits);
+      if (qtd == NULL)
+        {
+          usbhost_trace1(EHCI_TRACE1_QTDSTATUS_FAILED, 0);
+          goto errout_with_qh;
+        }
+
+      /* Link the new qTD to either the SETUP or data qTD. */
+
+      physaddr = kinetis_physramaddr((uintptr_t)qtd);
+      *flink = kinetis_swap32(physaddr);
+
+      /* In an IN data qTD was also enqueued, then linked the data qTD's
+       * alternate pointer to this STATUS phase qTD in order to handle short
+       * transfers.
+       */
+
+      if (alt)
+        {
+          *alt = kinetis_swap32(physaddr);
+        }
+    }
+
+  /* Add the new QH to the head of the asynchronous queue list */
+
+  kinetis_qh_enqueue(&g_asynchead, qh);
+  return OK;
+
+  /* Clean-up after an error */
+
+errout_with_qh:
+  kinetis_qh_discard(qh);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_intr_setup
+ *
+ * Description:
+ *   Process a IN or OUT request on any interrupt endpoint by inserting a qTD
+ *   into the periodic frame list.
+ *
+ *  Paragraph 4.10.7 "Adding Interrupt Queue Heads to the Periodic Schedule"
+ *    "The link path(s) from the periodic frame list to a queue head
+ *     establishes in which frames a transaction can be executed for the
+ *     queue head. Queue heads are linked into the periodic schedule so they
+ *     are polled at the appropriate rate. System software sets a bit in a
+ *     queue head's S-Mask to indicate which micro-frame with-in a 1
+ *     millisecond period a transaction should be executed for the queue
+ *     head. Software must ensure that all queue heads in the periodic
+ *     schedule have S-Mask set to a non-zero value. An S-mask with a zero
+ *     value in the context of the periodic schedule yields undefined
+ *     results.
+ *
+ *    "If the desired poll rate is greater than one frame, system software
+ *     can use a combination of queue head linking and S-Mask values to
+ *     spread interrupts of equal poll rates through the schedule so that the
+ *     periodic bandwidth is allocated and managed in the most efficient
+ *     manner possible."
+ *
+ *  Paragraph 4.6 "Periodic Schedule"
+ *
+ *    "The periodic schedule is used to manage all isochronous and interrupt
+ *     transfer streams. The base of the periodic schedule is the periodic
+ *     frame list. Software links schedule data structures to the periodic
+ *     frame list to produce a graph of scheduled data structures. The graph
+ *     represents an appropriate sequence of transactions on the USB. ...
+ *     isochronous transfers (using iTDs and siTDs) with a period of one are
+ *     linked directly to the periodic frame list. Interrupt transfers (are
+ *     managed with queue heads) and isochronous streams with periods other
+ *     than one are linked following the period-one iTD/siTDs. Interrupt
+ *     queue heads are linked into the frame list ordered by poll rate.
+ *     Longer poll rates are linked first (e.g. closest to the periodic
+ *     frame list), followed by shorter poll rates, with queue heads with a
+ *     poll rate of one, on the very end."
+ *
+ * Assumption:  The caller holds the EHCI exclsem.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+static int kinetis_intr_setup(struct kinetis_rhport_s *rhport,
+                            struct kinetis_epinfo_s *epinfo,
+                            uint8_t *buffer, size_t buflen)
+{
+  struct kinetis_qh_s *qh;
+  struct kinetis_qtd_s *qtd;
+  uintptr_t physaddr;
+  uint32_t tokenbits;
+  uint32_t regval;
+  int ret;
+
+  /* Terse output only if we are tracing */
+
+#ifdef CONFIG_USBHOST_TRACE
+  usbhost_vtrace2(EHCI_VTRACE2_INTRXFR, epinfo->epno, buflen);
+#else
+  uinfo("RHport%d EP%d: buffer=%p, buflen=%d\n",
+        RHPORT(rhport), epinfo->epno, buffer, buflen);
+#endif
+
+  DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
+
+  /* Create and initialize a Queue Head (QH) structure for this transfer */
+
+  qh = kinetis_qh_create(rhport, epinfo);
+  if (qh == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
+      return -ENOMEM;
+    }
+
+  /* Extra TOKEN bits include the data toggle, the data PID, and an
+   * indication to interrupt at the end of this transfer.
+   */
+
+  tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
+
+  /* Get the data token direction. */
+
+  if (epinfo->dirin)
+    {
+      tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC);
+    }
+  else
+    {
+      tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC);
+    }
+
+  /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data
+   * buffer.
+   */
+
+  qtd = kinetis_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
+  if (qtd == NULL)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0);
+      ret = -ENOMEM;
+      goto errout_with_qh;
+    }
+
+  /* Link the new qTD to the QH. */
+
+  physaddr = kinetis_physramaddr((uintptr_t)qtd);
+  qh->hw.overlay.nqp = kinetis_swap32(physaddr);
+
+  /* Disable the periodic schedule */
+
+  regval  = kinetis_getreg(&HCOR->usbcmd);
+  regval &= ~EHCI_USBCMD_PSEN;
+  kinetis_putreg(regval, &HCOR->usbcmd);
+
+  /* Add the new QH to the head of the interrupt transfer list */
+
+  kinetis_qh_enqueue(&g_intrhead, qh);
+
+  /* Re-enable the periodic schedule */
+
+  regval |= EHCI_USBCMD_PSEN;
+  kinetis_putreg(regval, &HCOR->usbcmd);
+  return OK;
+
+  /* Clean-up after an error */
+
+errout_with_qh:
+  kinetis_qh_discard(qh);
+  return ret;
+}
+#endif /* CONFIG_USBHOST_INT_DISABLE */
+
+/****************************************************************************
+ * Name: kinetis_transfer_wait
+ *
+ * Description:
+ *   Wait for an IN or OUT transfer to complete.
+ *
+ * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
+ *   that the EHCI exclsem will released while waiting for the transfer to
+ *   complete, but will be re-acquired when before returning.  The state of
+ *   EHCI resources could be very different upon return.
+ *
+ * Returned Value:
+ *   On success, this function returns the number of bytes actually
+ *   transferred.  For control transfers, this size includes the size of the
+ *   control request plus the size of the data (which could be short); for
+ *   bulk transfers, this will be the number of data bytes transfers (which
+ *   could be short).
+ *
+ ****************************************************************************/
+
+static ssize_t kinetis_transfer_wait(struct kinetis_epinfo_s *epinfo)
+{
+  int ret;
+  int ret2;
+
+  /* Release the EHCI semaphore while we wait.  Other threads need the
+   * opportunity to access the EHCI resources while we wait.
+   *
+   * REVISIT:  Is this safe?  NO.  This is a bug and needs rethinking.
+   * We need to lock all of the port-resources (not EHCI common) until
+   * the transfer is complete.  But we can't use the common EHCI exclsem
+   * or we will deadlock while waiting (because the working thread that
+   * wakes this thread up needs the exclsem).
+   */
+
+  /* REVISIT */
+
+  kinetis_givesem(&g_ehci.exclsem);
+
+  /* Wait for the IOC completion event */
+
+  ret = kinetis_ioc_wait(epinfo);
+
+  /* Re-acquire the EHCI semaphore.  The caller expects to be holding
+   * this upon return.
+   */
+
+  ret2 = kinetis_takesem_noncancelable(&g_ehci.exclsem);
+  if (ret >= 0 && ret2 < 0)
+    {
+      ret = ret2;
+    }
+
+#if 0 /* Does not seem to be needed */
+  /* Was there a data buffer?  Was this an OUT transfer? */
+
+  if (buffer != NULL && buflen > 0 && !dirin)
+    {
+      /* We have received data from the host -- unless there was an error.
+       * in any event, we will invalidate the data buffer so that we will
+       * reload any new data freshly DMAed into the user buffer.
+       *
+       * NOTE: This might be un-necessary.  We cleaned and invalidated the
+       * D-Cache prior to starting the DMA so the D-Cache should still be
+       * invalid in this memory region.
+       */
+
+      up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+    }
+#endif
+
+  /* Did kinetis_ioc_wait() or kinetis_takesem_noncancelable() report an
+   * error?
+   */
+
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret);
+      epinfo->iocwait = false;
+      return (ssize_t)ret;
+    }
+
+  /* Transfer completed successfully.  Return the number of bytes
+   * transferred.
+   */
+
+  return epinfo->xfrd;
+}
+
+/****************************************************************************
+ * Name: kinetis_ioc_async_setup
+ *
+ * Description:
+ *   Setup to receive an asynchronous notification when a transfer completes.
+ *
+ * Input Parameters:
+ *   epinfo - The IN or OUT endpoint descriptor for the device endpoint on
+ *      which the transfer will be performed.
+ *   callback - The function to be called when the completes
+ *   arg - An arbitrary argument that will be provided with the callback.
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   - Called from the interrupt level
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static inline int kinetis_ioc_async_setup(struct kinetis_rhport_s *rhport,
+                                        struct kinetis_epinfo_s *epinfo,
+                                        usbhost_asynch_t callback,
+                                        FAR void *arg)
+{
+  irqstate_t flags;
+  int ret = -ENODEV;
+
+  DEBUGASSERT(rhport && epinfo && !epinfo->iocwait &&
+              callback != NULL && epinfo->callback == NULL);
+
+  /* Is the device still connected? */
+
+  flags = enter_critical_section();
+  if (rhport->connected)
+    {
+      /* Then save callback information to used when either (1) the
+       * device is disconnected, or (2) the transfer completes.
+       */
+
+      epinfo->iocwait  = false;    /* No synchronous wakeup */
+      epinfo->status   = 0;        /* No status yet */
+      epinfo->xfrd     = 0;        /* Nothing transferred yet */
+      epinfo->result   = -EBUSY;   /* Transfer in progress */
+      epinfo->callback = callback; /* Asynchronous callback */
+      epinfo->arg      = arg;      /* Argument that accompanies the callback */
+      ret              = OK;       /* We are good to go */
+    }
+
+  leave_critical_section(flags);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_asynch_completion
+ *
+ * Description:
+ *   This function is called at the interrupt level when an asynchronous
+ *   transfer completes.  It performs the pending callback.
+ *
+ * Input Parameters:
+ *   epinfo - The IN or OUT endpoint descriptor for the device endpoint on
+ *      which the transfer was performed.
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   - Called from the interrupt level
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static void kinetis_asynch_completion(struct kinetis_epinfo_s *epinfo)
+{
+  usbhost_asynch_t callback;
+  ssize_t nbytes;
+  void *arg;
+  int result;
+
+  DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false &&
+              epinfo->callback != NULL);
+
+  /* Extract and reset the callback info */
+
+  callback         = epinfo->callback;
+  arg              = epinfo->arg;
+  result           = epinfo->result;
+  nbytes           = epinfo->xfrd;
+
+  epinfo->callback = NULL;
+  epinfo->arg      = NULL;
+  epinfo->result   = OK;
+  epinfo->iocwait  = false;
+
+  /* Then perform the callback.  Provide the number of bytes successfully
+   * transferred or the negated errno value in the event of a failure.
+   */
+
+  if (result < 0)
+    {
+      nbytes = (ssize_t)result;
+    }
+
+  callback(arg, nbytes);
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_qtd_ioccheck
+ *
+ * Description:
+ *   This function is a kinetis_qtd_foreach() callback function.  It services
+ *   one qTD in the asynchronous queue.  It removes all of the qTD
+ *   structures that are no longer active.
+ *
+ ****************************************************************************/
+
+static int kinetis_qtd_ioccheck(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                              void *arg)
+{
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)arg;
+  DEBUGASSERT(qtd && epinfo);
+
+  /* Make sure we reload the QH from memory */
+
+  up_invalidate_dcache((uintptr_t)&qtd->hw,
+                       (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
+  kinetis_qtd_print(qtd);
+
+  /* Remove the qTD from the list
+   *
+   * NOTE that we don't check if the qTD is active nor do we check if there
+   * are any errors reported in the qTD.  If the transfer halted due to
+   * an error, then qTDs in the list after the error qTD will still appear
+   * to be active.
+   */
+
+  **bp = qtd->hw.nqp;
+
+  /* Subtract the number of bytes left un-transferred.  The epinfo->xfrd
+   * field is initialized to the total number of bytes to be transferred
+   * (all qTDs in the list).  We subtract out the number of un-transferred
+   * bytes on each transfer and the final result will be the number of bytes
+   * actually transferred.
+   */
+
+  epinfo->xfrd -= (kinetis_swap32(qtd->hw.token) & QTD_TOKEN_NBYTES_MASK) >>
+    QTD_TOKEN_NBYTES_SHIFT;
+
+  /* Release this QH by returning it to the free list */
+
+  kinetis_qtd_free(qtd);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qh_ioccheck
+ *
+ * Description:
+ *   This function is a kinetis_qh_foreach() callback function.  It services
+ *   one QH in the asynchronous queue.  It check all attached qTD structures
+ *   and remove all of the structures that are no longer active.  if all of
+ *   the qTD structures are removed, then QH itself will also be removed.
+ *
+ ****************************************************************************/
+
+static int kinetis_qh_ioccheck(struct kinetis_qh_s *qh, uint32_t **bp, void *arg)
+{
+  struct kinetis_epinfo_s *epinfo;
+  uint32_t token;
+  int ret;
+
+  DEBUGASSERT(qh && bp);
+
+  /* Make sure we reload the QH from memory */
+
+  up_invalidate_dcache((uintptr_t)&qh->hw,
+                       (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
+  kinetis_qh_print(qh);
+
+  /* Get the endpoint info pointer from the extended QH data.  Only the
+   * g_asynchead QH can have a NULL epinfo field.
+   */
+
+  epinfo = qh->epinfo;
+  DEBUGASSERT(epinfo);
+
+  /* Paragraph 3.6.3:  "The nine DWords in [the Transfer Overlay] area
+   * represent a transaction working space for the host controller.  The
+   * general operational model is that the host controller can detect
+   * whether the overlay area contains a description of an active transfer.
+   * If it does not contain an active transfer, then it follows the Queue
+   * Head Horizontal Link Pointer to the next queue head.  The host
+   * controller will never follow the Next Transfer Queue Element or
+   * Alternate Queue Element pointers unless it is actively attempting to
+   * advance the queue ..."
+   */
+
+  /* Is the qTD still active? */
+
+  token = kinetis_swap32(qh->hw.overlay.token);
+  usbhost_vtrace2(EHCI_VTRACE2_IOCCHECK, epinfo->epno, token);
+
+  if ((token & QH_TOKEN_ACTIVE) != 0)
+    {
+      /* Yes... we cannot process the QH while it is still active.  Return
+       * zero to visit the next QH in the list.
+       */
+
+      *bp = &qh->hw.hlp;
+      return OK;
+    }
+
+  /* Remove all active, attached qTD structures from the inactive QH */
+
+  ret = kinetis_qtd_foreach(qh, kinetis_qtd_ioccheck, (void *)qh->epinfo);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret);
+    }
+
+  /* If there is no longer anything attached to the QH, then remove it from
+   * the asynchronous queue.
+   */
+
+  if ((kinetis_swap32(qh->fqp) & QTD_NQP_T) != 0)
+    {
+      /* Set the forward link of the previous QH to point to the next
+       * QH in the list.
+       */
+
+      **bp = qh->hw.hlp;
+      up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t));
+
+      /* Check for errors, update the data toggle */
+
+      if ((token & QH_TOKEN_ERRORS) == 0)
+        {
+          /* No errors.. Save the last data toggle value */
+
+          epinfo->toggle = (token >> QTD_TOKEN_TOGGLE_SHIFT) & 1;
+
+          /* Report success */
+
+          epinfo->status  = 0;
+          epinfo->result  = OK;
+        }
+      else
+        {
+          /* An error occurred */
+
+          epinfo->status = (token & QH_TOKEN_STATUS_MASK) >>
+                           QH_TOKEN_STATUS_SHIFT;
+
+          /* The HALT condition is set on a variety of conditions:  babble,
+           * error counter countdown to zero, or a STALL.  If we can rule
+           * out babble (babble bit not set) and if the error counter is
+           * non-zero, then we can assume a STALL. In this case, we return
+           * -PERM to inform the class driver of the stall condition.
+           */
+
+          if ((token & (QH_TOKEN_BABBLE | QH_TOKEN_HALTED)) ==
+               QH_TOKEN_HALTED &&
+              (token & QH_TOKEN_CERR_MASK) != 0)
+            {
+              /* It is a stall,  Note that the data toggle is reset
+               * after the stall.
+               */
+
+              usbhost_trace2(EHCI_TRACE2_EPSTALLED, epinfo->epno, token);
+              epinfo->result = -EPERM;
+              epinfo->toggle = 0;
+            }
+          else
+            {
+              /* Otherwise, it is some kind of data transfer error */
+
+              usbhost_trace2(EHCI_TRACE2_EPIOERROR, epinfo->epno, token);
+              epinfo->result = -EIO;
+            }
+        }
+
+      /* Is there a thread waiting for this transfer to complete? */
+
+      if (epinfo->iocwait)
+        {
+          /* Yes... wake it up */
+
+          epinfo->iocwait = false;
+          kinetis_givesem(&epinfo->iocsem);
+        }
+
+#ifdef CONFIG_USBHOST_ASYNCH
+      /* No.. Is there a pending asynchronous transfer? */
+
+      else if (epinfo->callback != NULL)
+        {
+          /* Yes.. perform the callback */
+
+          kinetis_asynch_completion(epinfo);
+        }
+#endif
+
+      /* Then release this QH by returning it to the free list */
+
+      kinetis_qh_free(qh);
+    }
+  else
+    {
+      /* Otherwise, the horizontal link pointer of this QH will become the
+       * next back pointer.
+       */
+
+      *bp = &qh->hw.hlp;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_qtd_cancel
+ *
+ * Description:
+ *   This function is a kinetis_qtd_foreach() callback function.  It removes
+ *   each qTD attached to a QH.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static int kinetis_qtd_cancel(struct kinetis_qtd_s *qtd, uint32_t **bp,
+                            void *arg)
+{
+  DEBUGASSERT(qtd != NULL && bp != NULL);
+
+  /* Make sure we reload the QH from memory */
+
+  up_invalidate_dcache((uintptr_t)&qtd->hw,
+                       (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
+  kinetis_qtd_print(qtd);
+
+  /* Remove the qTD from the list
+   *
+   * NOTE that we don't check if the qTD is active nor do we check if there
+   * are any errors reported in the qTD.  If the transfer halted due to
+   * an error, then qTDs in the list after the error qTD will still appear
+   * to be active.
+   *
+   * REVISIT: There is a race condition here that needs to be resolved.
+   */
+
+  **bp = qtd->hw.nqp;
+
+  /* Release this QH by returning it to the free list */
+
+  kinetis_qtd_free(qtd);
+  return OK;
+}
+#endif /* CONFIG_USBHOST_ASYNCH */
+
+/****************************************************************************
+ * Name: kinetis_qh_cancel
+ *
+ * Description:
+ *   This function is a kinetis_qh_foreach() callback function.  It cancels
+ *   one QH in the asynchronous queue.  It will remove all attached qTD
+ *   structures and remove all of the structures that are no longer active.
+ *   Then QH itself will also be removed.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static int kinetis_qh_cancel(struct kinetis_qh_s *qh, uint32_t **bp, void *arg)
+{
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)arg;
+  uint32_t regval;
+  int ret;
+
+  DEBUGASSERT(qh != NULL && bp != NULL && epinfo != NULL);
+
+  /* Make sure we reload the QH from memory */
+
+  up_invalidate_dcache((uintptr_t)&qh->hw,
+                       (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
+  kinetis_qh_print(qh);
+
+  /* Check if this is the QH that we are looking for */
+
+  if (qh->epinfo == epinfo)
+    {
+      /* No... keep looking */
+
+      return OK;
+    }
+
+  /* Disable both the asynchronous and period schedules */
+
+  regval = kinetis_getreg(&HCOR->usbcmd);
+  kinetis_putreg(regval & ~(EHCI_USBCMD_ASEN | EHCI_USBCMD_PSEN),
+               &HCOR->usbcmd);
+
+  /* Remove the QH from the list
+   *
+   * NOTE that we don't check if the qTD is active nor do we check if there
+   * are any errors reported in the qTD.  If the transfer halted due to
+   * an error, then qTDs in the list after the error qTD will still appear
+   * to be active.
+   *
+   * REVISIT: There is a race condition here that needs to be resolved.
+   */
+
+  **bp = qh->hw.hlp;
+  up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t));
+
+  /* Re-enable the schedules (if they were enabled before. */
+
+  kinetis_putreg(regval, &HCOR->usbcmd);
+
+  /* Remove all active, attached qTD structures from the removed QH */
+
+  ret = kinetis_qtd_foreach(qh, kinetis_qtd_cancel, NULL);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret);
+    }
+
+  /* Then release this QH by returning it to the free list.  Return 1
+   * to stop the traverse without an error.
+   */
+
+  kinetis_qh_free(qh);
+  return 1;
+}
+#endif /* CONFIG_USBHOST_ASYNCH */
+
+/****************************************************************************
+ * Name: kinetis_ioc_bottomhalf
+ *
+ * Description:
+ *   EHCI USB Interrupt (USBINT) "Bottom Half" interrupt handler
+ *
+ *  "The Host Controller sets this bit to 1 on the completion of a USB
+ *   transaction, which results in the retirement of a Transfer Descriptor
+ *   that had its IOC bit set.
+ *
+ *  "The Host Controller also sets this bit to 1 when a short packet is
+ *   detected (actual number of bytes received was less than the expected
+ *   number of bytes)."
+ *
+ * Assumptions:  The caller holds the EHCI exclsem
+ *
+ ****************************************************************************/
+
+static inline void kinetis_ioc_bottomhalf(void)
+{
+  struct kinetis_qh_s *qh;
+  uint32_t *bp;
+  int ret;
+
+  /* Check the Asynchronous Queue
+   * Make sure that the head of the asynchronous queue is invalidated.
+   */
+
+  up_invalidate_dcache((uintptr_t)&g_asynchead.hw,
+                       (uintptr_t)&g_asynchead.hw +
+                       sizeof(struct ehci_qh_s));
+
+  /* Set the back pointer to the forward QH pointer of the asynchronous
+   * queue head.
+   */
+
+  bp = (uint32_t *)&g_asynchead.hw.hlp;
+  qh = (struct kinetis_qh_s *)
+       kinetis_virtramaddr(kinetis_swap32(*bp) & QH_HLP_MASK);
+
+  /* If the asynchronous queue is empty, then the forward point in the
+   * asynchronous queue head will point back to the queue head.
+   */
+
+  if (qh && qh != &g_asynchead)
+    {
+      /* Then traverse and operate on every QH and qTD in the asynchronous
+       * queue
+       */
+
+      ret = kinetis_qh_foreach(qh, &bp, kinetis_qh_ioccheck, NULL);
+      if (ret < 0)
+        {
+          usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret);
+        }
+    }
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+
+  /* Check the Interrupt Queue
+   * Make sure that the head of the interrupt queue is invalidated.
+   */
+
+  up_invalidate_dcache((uintptr_t)&g_intrhead.hw,
+                       (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s));
+
+  /* Set the back pointer to the forward qTD pointer of the asynchronous
+   * queue head.
+   */
+
+  bp = (uint32_t *)&g_intrhead.hw.hlp;
+  qh = (struct kinetis_qh_s *)
+       kinetis_virtramaddr(kinetis_swap32(*bp) & QH_HLP_MASK);
+  if (qh)
+    {
+      /* Then traverse and operate on every QH and qTD in the asynchronous
+       * queue.
+       */
+
+      ret = kinetis_qh_foreach(qh, &bp, kinetis_qh_ioccheck, NULL);
+      if (ret < 0)
+        {
+          usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret);
+        }
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: kinetis_portsc_bottomhalf
+ *
+ * Description:
+ *   EHCI Port Change Detect "Bottom Half" interrupt handler
+ *
+ *  "The Host Controller sets this bit to a one when any port for which the
+ *   Port Owner bit is set to zero ... has a change bit transition from a
+ *   zero to a one or a Force Port Resume bit transition from a zero to a
+ *   one as a result of a J-K transition detected on a suspended port.
+ *   This bit will also be set as a result of the Connect Status Change
+ *   being set to a one after system software has relinquished ownership of
+ *   a connected port by writing a one to a port's Port Owner bit...
+ *
+ *  "This bit is allowed to be maintained in the Auxiliary power well.
+ *   Alternatively, it is also acceptable that on a D3 to D0 transition of
+ *   the EHCI HC device, this bit is loaded with the OR of all of the PORTSC
+ *   change bits (including: Force port resume, over-current change,
+ *   enable/disable change and connect status change)."
+ *
+ ****************************************************************************/
+
+static inline void kinetis_portsc_bottomhalf(void)
+{
+  struct kinetis_rhport_s *rhport;
+  struct usbhost_hubport_s *hport;
+  uint32_t portsc;
+  int rhpndx;
+
+  /* Handle root hub status change on each root port */
+
+  for (rhpndx = 0; rhpndx < KINETIS_EHCI_NRHPORT; rhpndx++)
+    {
+      rhport = &g_ehci.rhport[rhpndx];
+      portsc = kinetis_getreg(&HCOR->portsc[rhpndx]);
+
+      usbhost_vtrace2(EHCI_VTRACE2_PORTSC, rhpndx + 1, portsc);
+
+      /* Handle port connection status change (CSC) events */
+
+      if ((portsc & EHCI_PORTSC_CSC) != 0)
+        {
+          usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CSC, portsc);
+
+          /* Check current connect status */
+
+          if ((portsc & EHCI_PORTSC_CCS) != 0)
+            {
+              /* Connected ... Did we just become connected? */
+
+              if (!rhport->connected)
+                {
+                  /* Yes.. connected. */
+
+                  rhport->connected = true;
+
+                  usbhost_vtrace2(EHCI_VTRACE2_PORTSC_CONNECTED,
+                                  rhpndx + 1, g_ehci.pscwait);
+
+                  /* Notify any waiters */
+
+                  if (g_ehci.pscwait)
+                    {
+                      kinetis_givesem(&g_ehci.pscsem);
+                      g_ehci.pscwait = false;
+                    }
+                }
+              else
+                {
+                  usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CONNALREADY, portsc);
+                }
+            }
+          else
+            {
+              /* Disconnected... Did we just become disconnected? */
+
+              if (rhport->connected)
+                {
+                  /* Yes.. disconnect the device */
+
+                  usbhost_vtrace2(EHCI_VTRACE2_PORTSC_DISCONND,
+                                  rhpndx + 1, g_ehci.pscwait);
+
+                  rhport->connected = false;
+                  rhport->lowspeed  = false;
+
+                  /* Are we bound to a class instance? */
+
+                  hport = &rhport->hport.hport;
+
+#ifdef USBPHY_CTRLn_ENHOSTDISCONDETECT
+                  /* Highspeed needs special handling */
+
+                  if (hport->speed == USB_SPEED_HIGH)
+                    {
+                      uint32_t regval = getreg32(KINETIS_USBHSPHY_CTRL);
+                      regval &= ~(USBPHY_CTRLn_ENHOSTDISCONDETECT);
+                      putreg32(regval, KINETIS_USBHSPHY_CTRL);
+                    }
+#endif
+
+                  if (hport->devclass)
+                    {
+                      /* Yes.. Disconnect the class */
+
+                      CLASS_DISCONNECTED(hport->devclass);
+                      hport->devclass = NULL;
+                    }
+
+                  /* Notify any waiters for the Root Hub Status change
+                   * event.
+                   */
+
+                  if (g_ehci.pscwait)
+                    {
+                      kinetis_givesem(&g_ehci.pscsem);
+                      g_ehci.pscwait = false;
+                    }
+                }
+              else
+                {
+                   usbhost_vtrace1(EHCI_VTRACE1_PORTSC_DISCALREADY, portsc);
+                }
+            }
+        }
+
+      /* Clear all pending port interrupt sources by writing a '1' to the
+       * corresponding bit in the PORTSC register.  In addition, we need
+       * to preserve the values of all R/W bits (RO bits don't matter)
+       */
+
+      kinetis_putreg(portsc, &HCOR->portsc[rhpndx]);
+    }
+}
+
+/****************************************************************************
+ * Name: kinetis_syserr_bottomhalf
+ *
+ * Description:
+ *   EHCI Host System Error "Bottom Half" interrupt handler
+ *
+ *  "The Host Controller sets this bit to 1 when a serious error occurs
+ *   during a host system access involving the Host Controller module. ...
+ *   When this error occurs, the Host Controller clears the Run/Stop bit in
+ *   the Command register to prevent further execution of the scheduled TDs."
+ *
+ ****************************************************************************/
+
+static inline void kinetis_syserr_bottomhalf(void)
+{
+  usbhost_trace1(EHCI_TRACE1_SYSERR_INTR, 0);
+  DEBUGPANIC();
+}
+
+/****************************************************************************
+ * Name: kinetis_async_advance_bottomhalf
+ *
+ * Description:
+ *   EHCI Async Advance "Bottom Half" interrupt handler
+ *
+ *  "System software can force the host controller to issue an interrupt the
+ *   next time the host controller advances the asynchronous schedule by
+ *   writing a one to the Interrupt on Async Advance Doorbell bit in the
+ *   USBCMD register. This status bit indicates the assertion of that
+ *   interrupt source."
+ *
+ ****************************************************************************/
+
+static inline void kinetis_async_advance_bottomhalf(void)
+{
+  usbhost_vtrace1(EHCI_VTRACE1_AAINTR, 0);
+
+  /* REVISIT: Could remove all tagged QH entries here */
+}
+
+/****************************************************************************
+ * Name: kinetis_ehci_bottomhalf
+ *
+ * Description:
+ *   EHCI "Bottom Half" interrupt handler.  Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void kinetis_ehci_bottomhalf(FAR void *arg)
+{
+  uint32_t pending = (uint32_t)arg;
+
+  /* We need to have exclusive access to the EHCI data structures.  Waiting
+   * here is not a good thing to do on the worker thread, but there is no
+   * real option (other than to reschedule and delay).
+   */
+
+  kinetis_takesem_noncancelable(&g_ehci.exclsem);
+
+  /* Handle all unmasked interrupt sources
+   * USB Interrupt (USBINT)
+   *
+   *  "The Host Controller sets this bit to 1 on the completion of a USB
+   *   transaction, which results in the retirement of a Transfer Descriptor
+   *   that had its IOC bit set.
+   *
+   *  "The Host Controller also sets this bit to 1 when a short packet is
+   *   detected (actual number of bytes received was less than the expected
+   *   number of bytes)."
+   *
+   * USB Error Interrupt (USBERRINT)
+   *
+   *  "The Host Controller sets this bit to 1 when completion of a USB
+   *   transaction results in an error condition (e.g., error counter
+   *   underflow). If the TD on which the error interrupt occurred also
+   *   had its IOC bit set, both this bit and USBINT bit are set. ..."
+   *
+   * We do the same thing in either case:  Traverse the asynchronous queue
+   * and remove all of the transfers that are no longer active.
+   */
+
+  if ((pending & (EHCI_INT_USBINT | EHCI_INT_USBERRINT)) != 0)
+    {
+      if ((pending & EHCI_INT_USBERRINT) != 0)
+        {
+          usbhost_trace1(EHCI_TRACE1_USBERR_INTR, pending);
+        }
+      else
+        {
+          usbhost_vtrace1(EHCI_VTRACE1_USBINTR, pending);
+        }
+
+      kinetis_ioc_bottomhalf();
+    }
+
+  /* Port Change Detect
+   *
+   *  "The Host Controller sets this bit to a one when any port for which
+   *   the Port Owner bit is set to zero ... has a change bit transition
+   *   from a zero to a one or a Force Port Resume bit transition from a zero
+   *   to a one as a result of a J-K transition detected on a suspended port.
+   *   This bit will also be set as a result of the Connect Status Change
+   *   being set to a one after system software has relinquished ownership
+   *    of a connected port by writing a one to a port's Port Owner bit...
+   *
+   *  "This bit is allowed to be maintained in the Auxiliary power well.
+   *   Alternatively, it is also acceptable that on a D3 to D0 transition
+   *   of the EHCI HC device, this bit is loaded with the OR of all of the
+   *   PORTSC change bits (including: Force port resume, over-current change,
+   *   enable/disable change and connect status change)."
+   */
+
+  if ((pending & EHCI_INT_PORTSC) != 0)
+    {
+      kinetis_portsc_bottomhalf();
+    }
+
+  /* Frame List Rollover
+   *
+   *  "The Host Controller sets this bit to a one when the Frame List Index
+   *   ... rolls over from its maximum value to zero. The exact value at
+   *   which the rollover occurs depends on the frame list size. For example,
+   *   if the frame list size (as programmed in the Frame List Size field of
+   *   the USBCMD register) is 1024, the Frame Index Register rolls over
+   *   every time FRINDEX[13] toggles. Similarly, if the size is 512, the
+   *   Host Controller sets this bit to a one every time FRINDEX[12]
+   *   toggles."
+   */
+
+#if 0 /* Not used */
+  if ((pending & EHCI_INT_FLROLL) != 0)
+    {
+      kinetis_flroll_bottomhalf();
+    }
+#endif
+
+  /* Host System Error
+   *
+   *  "The Host Controller sets this bit to 1 when a serious error occurs
+   *   during a host system access involving the Host Controller module. ...
+   *   When this error occurs, the Host Controller clears the Run/Stop bit
+   *   in the Command register to prevent further execution of the scheduled
+   *   TDs."
+   */
+
+  if ((pending & EHCI_INT_SYSERROR) != 0)
+    {
+      uerr("Syserror\n");
+      kinetis_syserr_bottomhalf();
+    }
+
+  /* Interrupt on Async Advance
+   *
+   *  "System software can force the host controller to issue an interrupt
+   *   the next time the host controller advances the asynchronous schedule
+   *   by writing a one to the Interrupt on Async Advance Doorbell bit in
+   *   the USBCMD register. This status bit indicates the assertion of that
+   *   interrupt source."
+   */
+
+  if ((pending & EHCI_INT_AAINT) != 0)
+    {
+      uerr("Async Advance\n");
+      kinetis_async_advance_bottomhalf();
+    }
+
+  /* We are done with the EHCI structures */
+
+  kinetis_givesem(&g_ehci.exclsem);
+
+  /* Re-enable relevant EHCI interrupts.  Interrupts should still be enabled
+   * at the level of the interrupt controller.
+   */
+
+  kinetis_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr);
+}
+
+/****************************************************************************
+ * Name: kinetis_ehci_interrupt
+ *
+ * Description:
+ *   EHCI "Top Half" interrupt handler
+ *
+ ****************************************************************************/
+
+static int kinetis_ehci_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  uint32_t usbsts;
+  uint32_t pending;
+  uint32_t regval;
+
+  /* Read Interrupt Status and mask out interrupts that are not enabled. */
+
+  usbsts = kinetis_getreg(&HCOR->usbsts);
+  regval = kinetis_getreg(&HCOR->usbintr);
+
+#ifdef CONFIG_USBHOST_TRACE
+  usbhost_vtrace1(EHCI_VTRACE1_TOPHALF, usbsts & regval);
+#else
+  uinfo("USBSTS: %08x USBINTR: %08x\n", usbsts, regval);
+#endif
+
+  /* Handle all unmasked interrupt sources */
+
+  pending = usbsts & regval;
+  if (pending != 0)
+    {
+      /* Schedule interrupt handling work for the high priority worker
+       * thread so that we are not pressed for time and so that we can
+       * interrupt with other USB threads gracefully.
+       *
+       * The worker should be available now because we implement a handshake
+       * by controlling the EHCI interrupts.
+       */
+
+      DEBUGASSERT(work_available(&g_ehci.work));
+      DEBUGVERIFY(work_queue(HPWORK, &g_ehci.work, kinetis_ehci_bottomhalf,
+                            (FAR void *)pending, 0));
+
+      /* Disable further EHCI interrupts so that we do not overrun the work
+       * queue.
+       */
+
+      kinetis_putreg(0, &HCOR->usbintr);
+
+      /* Clear all pending status bits by writing the value of the pending
+       * interrupt bits back to the status register.
+       */
+
+      kinetis_putreg(usbsts & EHCI_INT_ALLINTS, &HCOR->usbsts);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_wait
+ *
+ * Description:
+ *   Wait for a device to be connected or disconnected to/from a hub port.
+ *
+ * Input Parameters:
+ *   conn - The USB host connection instance obtained as a parameter from the
+ *      call to the USB driver initialization logic.
+ *   hport - The location to return the hub port descriptor that detected the
+ *      connection related event.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success when a device is connected or
+ *   disconnected. This function will not return until either (1) a device is
+ *   connected or disconnect to/from any hub port or until (2) some failure
+ *   occurs.  On a failure, a negated errno value is returned indicating the
+ *   nature of the failure
+ *
+ * Assumptions:
+ *   - Called from a single thread so no mutual exclusion is required.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_wait(FAR struct usbhost_connection_s *conn,
+                      FAR struct usbhost_hubport_s **hport)
+{
+  irqstate_t flags;
+  int rhpndx;
+  int ret;
+
+  /* Loop until the connection state changes on one of the root hub ports or
+   * until an error occurs.
+   */
+
+  flags = enter_critical_section();
+  for (; ; )
+    {
+      /* Check for a change in the connection state on any root hub port */
+
+      for (rhpndx = 0; rhpndx < KINETIS_EHCI_NRHPORT; rhpndx++)
+        {
+          struct kinetis_rhport_s *rhport;
+          struct usbhost_hubport_s *connport;
+
+          /* Has the connection state changed on the RH port? */
+
+          rhport   = &g_ehci.rhport[rhpndx];
+          connport = &rhport->hport.hport;
+          if (rhport->connected != connport->connected)
+            {
+              /* Yes.. Return the RH port to inform the caller which
+               * port has the connection change.
+               */
+
+              connport->connected = rhport->connected;
+              *hport = connport;
+              leave_critical_section(flags);
+
+              usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP,
+                              rhpndx + 1, rhport->connected);
+              return OK;
+            }
+        }
+
+#ifdef CONFIG_USBHOST_HUB
+      /* Is a device connected to an external hub? */
+
+      if (g_ehci.hport)
+        {
+          volatile struct usbhost_hubport_s *connport;
+
+          /* Yes.. return the external hub port */
+
+          connport = g_ehci.hport;
+          g_ehci.hport = NULL;
+
+          *hport = (struct usbhost_hubport_s *)connport;
+          leave_critical_section(flags);
+
+          usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP,
+                          connport->port + 1, connport->connected);
+          return OK;
+        }
+#endif
+
+      /* No changes on any port. Wait for a connection/disconnection event
+       * and check again
+       */
+
+      g_ehci.pscwait = true;
+      ret = kinetis_takesem(&g_ehci.pscsem);
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: kinetis_enumerate
+ *
+ * Description:
+ *   Enumerate the connected device.  As part of this enumeration process,
+ *   the driver will (1) get the device's configuration descriptor, (2)
+ *   extract the class ID info from the configuration descriptor, (3) call
+ *   usbhost_findclass() to find the class that supports this device, (4)
+ *   call the create() method on the struct usbhost_registry_s interface
+ *   to get a class instance, and finally (5) call the connect() method
+ *   of the struct usbhost_class_s interface.  After that, the class is in
+ *   charge of the sequence of operations.
+ *
+ * Input Parameters:
+ *   conn  - The USB host connection instance obtained as a parameter from
+ *           the call to the USB driver initialization logic.
+ *   hport - The descriptor of the hub port that has the newly connected
+ *           device.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_rh_enumerate(FAR struct usbhost_connection_s *conn,
+                              FAR struct usbhost_hubport_s *hport)
+{
+  struct kinetis_rhport_s *rhport;
+  volatile uint32_t *regaddr;
+  uint32_t regval;
+  int rhpndx;
+
+  DEBUGASSERT(conn != NULL && hport != NULL);
+  rhpndx = hport->port;
+
+  DEBUGASSERT(rhpndx >= 0 && rhpndx < KINETIS_EHCI_NRHPORT);
+  rhport = &g_ehci.rhport[rhpndx];
+
+  /* Are we connected to a device?  The caller should have called the wait()
+   * method first to be assured that a device is connected.
+   */
+
+  while (!rhport->connected)
+    {
+      /* No, return an error */
+
+      usbhost_vtrace1(EHCI_VTRACE1_ENUM_DISCONN, 0);
+      return -ENODEV;
+    }
+
+  /* USB 2.0 spec says at least 50ms delay before port reset.
+   * REVISIT:  I think this is wrong.  It needs to hold the port in
+   * reset for 50Msec, not wait 50Msec before resetting.
+   */
+
+  nxsig_usleep(100 * 1000);
+
+  /* Paragraph 2.3.9:
+   *
+   *  "Line Status ... These bits reflect the current logical levels of the
+   *   D+ (bit 11) and D- (bit 10) signal lines. These bits are used for
+   *   detection of low-speed USB devices prior to the port reset and enable
+   *   sequence. This field is valid only when the port enable bit is zero
+   *   and the current connect status bit is set to a one."
+   *
+   *   Bits[11:10] USB State Interpretation
+   *   ----------- --------- --------------
+   *   00b         SE0       Not Low-speed device, perform EHCI reset
+   *   10b         J-state   Not Low-speed device, perform EHCI reset
+   *   01b         K-state   Low-speed device, release ownership of port
+   *
+   * NOTE: Low-speed devices could be detected by examining the PORTSC PSPD
+   * field after resetting the device.  The more conventional way here,
+   * however, also appears to work.
+   */
+
+  regval = kinetis_getreg(&HCOR->portsc[rhpndx]);
+  if ((regval & EHCI_PORTSC_LSTATUS_MASK) == EHCI_PORTSC_LSTATUS_KSTATE)
+    {
+      /* EHCI Paragraph 2.3.9:
+       *
+       *   "Port Owner ... This bit unconditionally goes to a 0b when the
+       *    Configured bit in the CONFIGFLAG register makes a 0b to 1b
+       *    transition. This bit unconditionally goes to 1b whenever the
+       *    Configured bit is zero.
+       *
+       *   "System software uses this field to release ownership of the
+       *    port to a selected host controller (in the event that the
+       *    attached device is not a high-speed device). Software writes
+       *    a one to this bit when the attached device is not a high-speed
+       *    device. A one in this bit means that a companion host
+       *    controller owns and controls the port. ....
+       *
+       * EHCI Paragraph 4.2:
+       *
+       *   "When a port is routed to a companion HC, it remains under the
+       *    control of the companion HC until the device is disconnected
+       *    from the root por ... When a disconnect occurs, the disconnect
+       *    event is detected by both the companion HC port control and the
+       *    EHCI port ownership control. On the event, the port ownership
+       *    is returned immediately to the EHCI controller. The companion
+       *    HC stack detects the disconnect and acknowledges as it would
+       *    in an ordinary standalone implementation. Subsequent connects
+       *    will be detected by the EHCI port register and the process will
+       *    repeat."
+       */
+
+      hport->speed = USB_SPEED_LOW;
+    }
+  else
+    {
+      /* Assume full-speed for now */
+
+      hport->speed = USB_SPEED_FULL;
+    }
+
+  /* Put the root hub port in reset.
+   *
+   * EHCI Paragraph 2.3.9:
+   *
+   *  "The HCHalted bit in the USBSTS register should be a zero before
+   *   software attempts to use [the Port Reset] bit. The host controller
+   *   may hold Port Reset asserted to a one when the HCHalted bit is a one.
+   */
+
+  DEBUGASSERT((kinetis_getreg(&HCOR->usbsts) & EHCI_USBSTS_HALTED) == 0);
+
+  /* EHCI paragraph 2.3.9:
+   *
+   *  "When software writes a one to [the Port Reset] bit (from a zero), the
+   *   bus reset sequence as defined in the USB Specification Revision 2.0
+   *   is started.  Software writes a zero to this bit to terminate the bus
+   *   reset sequence.  Software must keep this bit at a one long enough to
+   *   ensure the reset sequence, as specified in the USB Specification
+   *   Revision 2.0, completes. Note: when software writes this bit to a
+   *   one, it must also write a zero to the Port Enable bit."
+   */
+
+  regaddr = &HCOR->portsc[RHPNDX(rhport)];
+  regval  = kinetis_getreg(regaddr);
+  regval |= EHCI_PORTSC_RESET;
+  kinetis_putreg(regval, regaddr);
+
+  /* Wait for the port reset to complete,
+   * Kinetis EHCI clears Reset pin automatically
+   *
+   * EHCI Paragraph 2.3.9:
+   *
+   *  "Note that when software writes a zero to this bit there may be a
+   *   delay before the bit status changes to a zero. The bit status will
+   *   not read as a zero until after the reset has completed. If the port
+   *   is in high-speed mode after reset is complete, the host controller
+   *   will automatically enable this port (e.g. set the Port Enable bit
+   *   to a one). A host controller must terminate the reset and stabilize
+   *   the state of the port within 2 milliseconds of software transitioning
+   *   this bit from a one to a zero ..."
+   */
+
+  while ((kinetis_getreg(regaddr) & EHCI_PORTSC_RESET) != 0);
+  nxsig_usleep(200 * 1000);
+
+  /* EHCI Paragraph 4.2.2:
+   *
+   *  "... The reset process is actually complete when software reads a zero
+   *   in the PortReset bit. The EHCI Driver checks the PortEnable bit in
+   *   the PORTSC register. If set to a one, the connected device is a high-
+   *   speed device and EHCI Driver (root hub emulator) issues a change
+   *   report to the hub driver and the hub driver continues to enumerate
+   *   the attached device."
+   *
+   *  "At the time the EHCI Driver receives the port reset and enable request
+   *   the LineStatus bits might indicate a low-speed device. Additionally,
+   *   when the port reset process is complete, the PortEnable field may
+   *   indicate that a full-speed device is attached. In either case the EHCI
+   *   driver sets the PortOwner bit in the PORTSC register to a one to
+   *   release port ownership to a companion host controller."
+   *
+   * LPC31xx User Manual Paragraph 6.1.3:
+   *
+   *  "In a standard EHCI controller design, the EHCI host controller driver
+   *   detects a Full speed (FS) or Low speed (LS) device by noting if the
+   *   port enable bit is set after the port reset operation. The port enable
+   *   will only be set in a standard EHCI controller implementation after
+   *   the port reset operation and when the host and device negotiate a
+   *   High-Speed connection (i.e. Chirp completes successfully). Since this
+   *   controller has an embedded Transaction Translator, the port enable
+   *   will always be set after the port reset operation regardless of the
+   *   result of the host device chirp result and the resulting port speed
+   *   will be indicated by the PSPD field in PORTSC1.
+   */
+
+  regval = kinetis_getreg(&HCOR->portsc[rhpndx]);
+
+  if ((regval & USBHS_PORTSC1_PSPD_MASK) == USBHS_PORTSC1_PSPD_HS)
+    {
+      /* High speed device */
+
+      hport->speed = USB_SPEED_HIGH;
+
+#ifdef USBPHY_CTRLn_ENHOSTDISCONDETECT
+      /* Highspeed needs special handling */
+
+      regval  = getreg32(KINETIS_USBHSPHY_CTRL);
+      regval |= USBPHY_CTRLn_ENHOSTDISCONDETECT;
+      putreg32(regval, KINETIS_USBHSPHY_CTRL);
+#endif
+
+    }
+  else if ((regval & USBHS_PORTSC1_PSPD_MASK) == USBHS_PORTSC1_PSPD_FS)
+    {
+      /* Low- or Full- speed device.  Set the port ownership bit.
+       *
+       * EHCI Paragraph 4.2:
+       *
+       *   "When a port is routed to a companion HC, it remains under the
+       *    control of the companion HC until the device is disconnected
+       *    from the root por ... When a disconnect occurs, the disconnect
+       *    event is detected by both the companion HC port control and the
+       *    EHCI port ownership control. On the event, the port ownership
+       *    is returned immediately to the EHCI controller. The companion
+       *    HC stack detects the disconnect and acknowledges as it would
+       *    in an ordinary standalone implementation. Subsequent connects
+       *    will be detected by the EHCI port register and the process will
+       *    repeat."
+       */
+
+      DEBUGASSERT(hport->speed == USB_SPEED_FULL);
+    }
+
+  /* Otherwise it must be a low speed device */
+
+  else
+    {
+      DEBUGASSERT(hport->speed == USB_SPEED_LOW);
+      DEBUGASSERT((regval & USBHS_PORTSC1_PSPD_MASK) ==
+                  USBHS_PORTSC1_PSPD_LS);
+    }
+
+  return OK;
+}
+
+static int kinetis_enumerate(FAR struct usbhost_connection_s *conn,
+                           FAR struct usbhost_hubport_s *hport)
+{
+  int ret;
+
+  /* If this is a connection on the root hub, then we need to go to
+   * little more effort to get the device speed.  If it is a connection
+   * on an external hub, then we already have that information.
+   */
+
+  DEBUGASSERT(hport);
+#ifdef CONFIG_USBHOST_HUB
+  if (ROOTHUB(hport))
+#endif
+    {
+      ret = kinetis_rh_enumerate(conn, hport);
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+
+  /* Then let the common usbhost_enumerate do the real enumeration. */
+
+  usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, hport->port);
+  ret = usbhost_enumerate(hport, &hport->devclass);
+  if (ret < 0)
+    {
+      /* Failed to enumerate */
+
+      usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, hport->port + 1, -ret);
+
+      /* If this is a root hub port, then marking the hub port not connected
+       * will cause kinetis_wait() to return and we will try the connection
+       * again.
+       */
+
+      hport->connected = false;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_ep0configure
+ *
+ * Description:
+ *   Configure endpoint 0.  This method is normally used internally by the
+ *   enumerate() method but is made available at the interface to support
+ *   an external implementation of the enumeration logic.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *     call to the class create() method.
+ *   funcaddr - The USB address of the function containing the endpoint that
+ *     EP0 controls.  A funcaddr of zero will be received if no address is
+ *     yet assigned to the device.
+ *   speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH
+ *   maxpacketsize - The maximum number of bytes that can be sent to or
+ *    received from the endpoint in a single data packet
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure.
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_ep0configure(FAR struct usbhost_driver_s *drvr,
+                              usbhost_ep_t ep0, uint8_t funcaddr,
+                              uint8_t speed, uint16_t maxpacketsize)
+{
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)ep0;
+  int ret;
+
+  DEBUGASSERT(drvr != NULL && epinfo != NULL && maxpacketsize < 2048);
+
+  /* We must have exclusive access to the EHCI data structures. */
+
+  ret = kinetis_takesem(&g_ehci.exclsem);
+  if (ret >= 0)
+    {
+      /* Remember the new device address and max packet size */
+
+      epinfo->devaddr   = funcaddr;
+      epinfo->speed     = speed;
+      epinfo->maxpacket = maxpacketsize;
+
+      kinetis_givesem(&g_ehci.exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_epalloc
+ *
+ * Description:
+ *   Allocate and configure one endpoint.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *     call to the class create() method.
+ *   epdesc - Describes the endpoint to be allocated.
+ *   ep - A memory location provided by the caller in which to receive the
+ *      allocated endpoint descriptor.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure.
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_epalloc(FAR struct usbhost_driver_s *drvr,
+                         const FAR struct usbhost_epdesc_s *epdesc,
+                         usbhost_ep_t *ep)
+{
+  struct kinetis_epinfo_s *epinfo;
+  struct usbhost_hubport_s *hport;
+
+  /* Sanity check.  NOTE that this method should only be called if a device
+   * is connected (because we need a valid low speed indication).
+   */
+
+  DEBUGASSERT(drvr != 0 && epdesc != NULL && epdesc->hport != NULL &&
+              ep != NULL);
+  hport = epdesc->hport;
+
+  /* Terse output only if we are tracing */
+
+#ifdef CONFIG_USBHOST_TRACE
+  usbhost_vtrace2(EHCI_VTRACE2_EPALLOC, epdesc->addr, epdesc->xfrtype);
+#else
+  uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n",
+        epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr,
+        epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize);
+#endif
+
+  /* Allocate a endpoint information structure */
+
+  epinfo = (struct kinetis_epinfo_s *)
+    kmm_zalloc(sizeof(struct kinetis_epinfo_s));
+  if (!epinfo)
+    {
+      usbhost_trace1(EHCI_TRACE1_EPALLOC_FAILED, 0);
+      return -ENOMEM;
+    }
+
+  /* Initialize the endpoint container (which is really just another form of
+   * 'struct usbhost_epdesc_s', packed differently and with additional
+   * information.  A cleaner design might just embed struct usbhost_epdesc_s
+   * inside of struct kinetis_epinfo_s and just memcpy() here.
+   */
+
+  epinfo->epno      = epdesc->addr;
+  epinfo->dirin     = epdesc->in;
+  epinfo->devaddr   = hport->funcaddr;
+#ifndef CONFIG_USBHOST_INT_DISABLE
+  epinfo->interval  = epdesc->interval;
+#endif
+  epinfo->maxpacket = epdesc->mxpacketsize;
+  epinfo->xfrtype   = epdesc->xfrtype;
+  epinfo->speed     = hport->speed;
+
+  /* The iocsem semaphore is used for signaling and, hence, should not have
+   * priority inheritance enabled.
+   */
+
+  nxsem_init(&epinfo->iocsem, 0, 0);
+  nxsem_set_protocol(&epinfo->iocsem, SEM_PRIO_NONE);
+
+  /* Success.. return an opaque reference to the endpoint information
+   * structure instance
+   */
+
+  *ep = (usbhost_ep_t)epinfo;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_epfree
+ *
+ * Description:
+ *   Free and endpoint previously allocated by DRVR_EPALLOC.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *           call to the class create() method.
+ *   ep   - The endpint to be freed.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
+{
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)ep;
+
+  /* There should not be any pending, transfers */
+
+  DEBUGASSERT(drvr && epinfo && epinfo->iocwait == 0);
+
+  /* Free the container */
+
+  kmm_free(epinfo);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_alloc
+ *
+ * Description:
+ *   Some hardware supports special memory in which request and descriptor
+ *   data can be accessed more efficiently.  This method provides a
+ *   mechanism to allocate the request/descriptor memory.  If the underlying
+ *   hardware does not support such "special" memory, this functions may
+ *   simply map to kmm_malloc().
+ *
+ *   This interface was optimized under a particular assumption.  It was
+ *   assumed that the driver maintains a pool of small, pre-allocated buffers
+ *   for descriptor traffic.  NOTE that size is not an input, but an output:
+ *   The size of the pre-allocated buffer is returned.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *     call to the class create() method.
+ *   buffer - The address of a memory location provided by the caller in
+ *     which to return the allocated buffer memory address.
+ *   maxlen - The address of a memory location provided by the caller in
+ *     which to return the maximum size of the allocated buffer memory.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   - Called from a single thread so no mutual exclusion is required.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_alloc(FAR struct usbhost_driver_s *drvr,
+                       FAR uint8_t **buffer, FAR size_t *maxlen)
+{
+  int ret = -ENOMEM;
+  DEBUGASSERT(drvr && buffer && maxlen);
+
+  /* The only special requirements for transfer/descriptor buffers are that
+   * (1) they be aligned to a cache line boundary and (2) they are a
+   * multiple of the cache line size in length.
+   */
+
+  *buffer = (FAR uint8_t *)kmm_memalign(ARMV7M_DCACHE_LINESIZE,
+                                        KINETIS_EHCI_BUFSIZE);
+  if (*buffer)
+    {
+      *maxlen = KINETIS_EHCI_BUFSIZE;
+      ret = OK;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_free
+ *
+ * Description:
+ *   Some hardware supports special memory in which request and descriptor
+ *   data can be accessed more efficiently.  This method provides a
+ *   mechanism to free that request/descriptor memory.  If the underlying
+ *   hardware does not support such "special" memory, this functions may
+ *   simply map to kmm_free().
+ *
+ * Input Parameters:
+ *   drvr   - The USB host driver instance obtained as a parameter from the
+ *            call to the class create() method.
+ *   buffer - The address of the allocated buffer memory to be freed.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
+{
+  DEBUGASSERT(drvr && buffer);
+
+  /* No special action is require to free the transfer/descriptor buffer memory */
+
+  kmm_free(buffer);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_ioalloc
+ *
+ * Description:
+ *   Some hardware supports special memory in which larger IO buffers can
+ *   be accessed more efficiently.  This method provides a mechanism to
+ *   allocate the request/descriptor memory.  If the underlying hardware
+ *   does not support such "special" memory, this functions may simply map
+ *   to kumm_malloc.
+ *
+ *   This interface differs from DRVR_ALLOC in that the buffers are variable-
+ *   sized.
+ *
+ * Input Parameters:
+ *   drvr   - The USB host driver instance obtained as a parameter from the
+ *            call to the class create() method.
+ *   buffer - The address of a memory location provided by the caller in
+ *            which to return the allocated buffer memory address.
+ *   buflen - The size of the buffer required.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_ioalloc(FAR struct usbhost_driver_s *drvr,
+                         FAR uint8_t **buffer, size_t buflen)
+{
+  DEBUGASSERT(drvr && buffer && buflen > 0);
+
+  /* The only special requirements for I/O buffers are that (1) they be
+   * aligned to a cache line boundary, (2) they are a multiple of the cache
+   * line size in length, and (3) they might need to be user accessible
+   * (depending on how the class driver implements its buffering).
+   */
+
+  buflen  = (buflen + DCACHE_LINEMASK) & ~DCACHE_LINEMASK;
+  *buffer = (FAR uint8_t *)kumm_memalign(ARMV7M_DCACHE_LINESIZE, buflen);
+  return *buffer ? OK : -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: kinetis_iofree
+ *
+ * Description:
+ *   Some hardware supports special memory in which IO data can  be accessed
+ *   more efficiently.  This method provides a mechanism to free that IO
+ *   buffer memory.  If the underlying hardware does not support such
+ *   "special" memory, this functions may simply map to kumm_free().
+ *
+ * Input Parameters:
+ *   drvr   - The USB host driver instance obtained as a parameter from the
+ *            call to the class create() method.
+ *   buffer - The address of the allocated buffer memory to be freed.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_iofree(FAR struct usbhost_driver_s *drvr,
+                        FAR uint8_t *buffer)
+{
+  DEBUGASSERT(drvr && buffer);
+
+  /* No special action is require to free the I/O buffer memory */
+
+  kumm_free(buffer);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: kinetis_ctrlin and kinetis_ctrlout
+ *
+ * Description:
+ *   Process a IN or OUT request on the control endpoint.  These methods
+ *   will enqueue the request and wait for it to complete.  Only one
+ *   transfer may be queued; Neither these methods nor the transfer() method
+ *   can be called again until the control transfer functions returns.
+ *
+ *   These are blocking methods; these functions will not return until the
+ *   control transfer has completed.
+ *
+ * Input Parameters:
+ *   drvr   - The USB host driver instance obtained as a parameter from the
+ *            call to the class create() method.
+ *   ep0    - The control endpoint to send/receive the control request.
+ *   req    - Describes the request to be sent.  This request must lie in
+ *            memory created by DRVR_ALLOC.
+ *   buffer - A buffer used for sending the request and for returning any
+ *            responses.  This buffer must be large enough to hold the
+ *            length value in the request description. buffer must have been
+ *            allocated using DRVR_ALLOC.
+ *
+ *   NOTE: On an IN transaction, req and buffer may refer to the same
+ *   allocated memory.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   - Called from a single thread so no mutual exclusion is required.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int kinetis_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
+                        FAR const struct usb_ctrlreq_s *req,
+                        FAR uint8_t *buffer)
+{
+  struct kinetis_rhport_s *rhport = (struct kinetis_rhport_s *)drvr;
+  struct kinetis_epinfo_s *ep0info = (struct kinetis_epinfo_s *)ep0;
+  uint16_t len;
+  ssize_t nbytes;
+  int ret;
+
+  DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL);
+
+  len = kinetis_read16(req->len);
+
+  /* Terse output only if we are tracing */
+
+#ifdef CONFIG_USBHOST_TRACE
+  usbhost_vtrace2(EHCI_VTRACE2_CTRLINOUT, RHPORT(rhport), req->req);
+#else
+  uinfo("RHPort%d type: %02x req: %02x value: %02x%02x index: %02x%02x "
+        "len: %04x\n",
+        RHPORT(rhport), req->type, req->req, req->value[1], req->value[0],
+        req->index[1], req->index[0], len);
+#endif
+
+  /* We must have exclusive access to the EHCI hardware and data structures. */
+
+  ret = kinetis_takesem(&g_ehci.exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = kinetis_ioc_setup(rhport, ep0info);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Now initiate the transfer */
+
+  ret = kinetis_async_setup(rhport, ep0info, req, buffer, len);
+  if (ret < 0)
+    {
+      uerr("ERROR: kinetis_async_setup failed: %d\n", ret);
+      goto errout_with_iocwait;
+    }
+
+  /* And wait for the transfer to complete */
+
+  nbytes = kinetis_transfer_wait(ep0info);
+  kinetis_givesem(&g_ehci.exclsem);
+  return nbytes >= 0 ? OK : (int)nbytes;
+
+errout_with_iocwait:
+  ep0info->iocwait = false;
+errout_with_sem:
+  kinetis_givesem(&g_ehci.exclsem);
+  return ret;
+}
+
+static int kinetis_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
+                         FAR const struct usb_ctrlreq_s *req,
+                         FAR const uint8_t *buffer)
+{
+  /* kinetis_ctrlin can handle both directions.  We just need to work around
+   * the differences in the function signatures.
+   */
+
+  return kinetis_ctrlin(drvr, ep0, req, (uint8_t *)buffer);
+}
+
+/****************************************************************************
+ * Name: kinetis_transfer
+ *
+ * Description:
+ *   Process a request to handle a transfer descriptor.  This method will
+ *   enqueue the transfer request, blocking until the transfer completes.
+ *   Only one transfer may be  queued; Neither this method nor the ctrlin or
+ *   ctrlout methods can be called again until this function returns.
+ *
+ *   This is a blocking method; this functions will not return until the
+ *   transfer has completed.
+ *
+ * Input Parameters:
+ *   drvr   - The USB host driver instance obtained as a parameter from the
+ *            call to the class create() method.
+ *   ep     - The IN or OUT endpoint descriptor for the device endpoint on
+ *            which to perform the transfer.
+ *   buffer - A buffer containing the data to be sent (OUT endpoint) or
+ *            received (IN endpoint).  buffer must have been allocated using
+ *            DRVR_ALLOC
+ *   buflen - The length of the data to be sent or received.
+ *
+ * Returned Value:
+ *   On success, a non-negative value is returned that indicates the number
+ *   of bytes successfully transferred.  On a failure, a negated errno value
+ *   is returned that indicates the nature of the failure:
+ *
+ *     EAGAIN - If devices NAKs the transfer (or NYET or other error where
+ *              it may be appropriate to restart the entire transaction).
+ *     EPERM  - If the endpoint stalls
+ *     EIO    - On a TX or data toggle error
+ *     EPIPE  - Overrun errors
+ *
+ * Assumptions:
+ *   - Called from a single thread so no mutual exclusion is required.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static ssize_t kinetis_transfer(FAR struct usbhost_driver_s *drvr,
+                              usbhost_ep_t ep, FAR uint8_t *buffer,
+                              size_t buflen)
+{
+  struct kinetis_rhport_s *rhport = (struct kinetis_rhport_s *)drvr;
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)ep;
+  ssize_t nbytes;
+  int ret;
+
+  DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
+
+  /* We must have exclusive access to the EHCI hardware and data structures. */
+
+  ret = kinetis_takesem(&g_ehci.exclsem);
+  if (ret < 0)
+    {
+      return (ssize_t)ret;
+    }
+
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = kinetis_ioc_setup(rhport, epinfo);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Initiate the transfer */
+
+  switch (epinfo->xfrtype)
+    {
+      case USB_EP_ATTR_XFER_BULK:
+        ret = kinetis_async_setup(rhport, epinfo, NULL, buffer, buflen);
+        break;
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+      case USB_EP_ATTR_XFER_INT:
+        ret = kinetis_intr_setup(rhport, epinfo, buffer, buflen);
+        break;
+#endif
+
+#ifndef CONFIG_USBHOST_ISOC_DISABLE
+      case USB_EP_ATTR_XFER_ISOC:
+# warning "Isochronous endpoint support not emplemented"
+#endif
+      case USB_EP_ATTR_XFER_CONTROL:
+      default:
+        usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
+        ret = -ENOSYS;
+        break;
+    }
+
+  /* Check for errors in the setup of the transfer */
+
+  if (ret < 0)
+    {
+      goto errout_with_iocwait;
+    }
+
+  /* Then wait for the transfer to complete */
+
+  nbytes = kinetis_transfer_wait(epinfo);
+  kinetis_givesem(&g_ehci.exclsem);
+  return nbytes;
+
+errout_with_iocwait:
+  epinfo->iocwait = false;
+errout_with_sem:
+  uerr("!!!\n");
+  kinetis_givesem(&g_ehci.exclsem);
+  return (ssize_t)ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_asynch
+ *
+ * Description:
+ *   Process a request to handle a transfer descriptor.  This method will
+ *   enqueue the transfer request and return immediately.  When the transfer
+ *   completes, the callback will be invoked with the provided transfer.
+ *   This method is useful for receiving interrupt transfers which may come
+ *   infrequently.
+ *
+ *   Only one transfer may be queued; Neither this method nor the ctrlin or
+ *   ctrlout methods can be called again until the transfer completes.
+ *
+ * Input Parameters:
+ *   drvr     - The USB host driver instance obtained as a parameter from
+ *              the call to the class create() method.
+ *   ep       - The IN or OUT endpoint descriptor for the device endpoint on
+ *              which to perform the transfer.
+ *   buffer   - A buffer containing the data to be sent (OUT endpoint) or
+ *              received (IN endpoint).  buffer must have been allocated
+ *              using DRVR_ALLOC
+ *   buflen   - The length of the data to be sent or received.
+ *   callback - This function will be called when the transfer completes.
+ *   arg      - The arbitrary parameter that will be passed to the callback
+ *              function when the transfer completes.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ *   - Called from a single thread so no mutual exclusion is required.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static int kinetis_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
+                        FAR uint8_t *buffer, size_t buflen,
+                        usbhost_asynch_t callback, FAR void *arg)
+{
+  struct kinetis_rhport_s *rhport = (struct kinetis_rhport_s *)drvr;
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)ep;
+  int ret;
+
+  DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
+
+  /* We must have exclusive access to the EHCI hardware and data structures. */
+
+  ret = kinetis_takesem(&g_ehci.exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the request for the callback well BEFORE initiating the transfer. */
+
+  ret = kinetis_ioc_async_setup(rhport, epinfo, callback, arg);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Initiate the transfer */
+
+  switch (epinfo->xfrtype)
+    {
+      case USB_EP_ATTR_XFER_BULK:
+        ret = kinetis_async_setup(rhport, epinfo, NULL, buffer, buflen);
+        break;
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+      case USB_EP_ATTR_XFER_INT:
+        ret = kinetis_intr_setup(rhport, epinfo, buffer, buflen);
+        break;
+#endif
+
+#ifndef CONFIG_USBHOST_ISOC_DISABLE
+      case USB_EP_ATTR_XFER_ISOC:
+# warning "Isochronous endpoint support not emplemented"
+#endif
+      case USB_EP_ATTR_XFER_CONTROL:
+      default:
+        usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
+        ret = -ENOSYS;
+        break;
+    }
+
+  /* Check for errors in the setup of the transfer */
+
+  if (ret < 0)
+    {
+      goto errout_with_callback;
+    }
+
+  /* The transfer is in progress */
+
+  kinetis_givesem(&g_ehci.exclsem);
+  return OK;
+
+errout_with_callback:
+  epinfo->callback = NULL;
+  epinfo->arg      = NULL;
+errout_with_sem:
+  kinetis_givesem(&g_ehci.exclsem);
+  return ret;
+}
+#endif /* CONFIG_USBHOST_ASYNCH */
+
+/****************************************************************************
+ * Name: kinetis_cancel
+ *
+ * Description:
+ *   Cancel a pending transfer on an endpoint.  Canceled synchronous or
+ *   asynchronous transfer will complete normally with the error -ESHUTDOWN.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *          call to the class create() method.
+ *   ep   - The IN or OUT endpoint descriptor for the device endpoint on
+ *          which an asynchronous transfer should be transferred.
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ ****************************************************************************/
+
+static int kinetis_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
+{
+  struct kinetis_epinfo_s *epinfo = (struct kinetis_epinfo_s *)ep;
+  struct kinetis_qh_s *qh;
+#ifdef CONFIG_USBHOST_ASYNCH
+  usbhost_asynch_t callback;
+  void *arg;
+#endif
+  uint32_t *bp;
+  irqstate_t flags;
+  bool iocwait;
+  int ret;
+
+  DEBUGASSERT(epinfo);
+
+  /* We must have exclusive access to the EHCI hardware and data structures.
+   * This will prevent servicing any transfer completion events while we
+   * perform the the cancellation, but will not prevent DMA-related race
+   * conditions.
+   *
+   * REVISIT: This won't work.  This function must be callable from the
+   * interrupt level.
+   */
+
+  ret = kinetis_takesem(&g_ehci.exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Sample and reset all transfer termination information.  This will
+   * prevent any callbacks from occurring while are performing the
+   * cancellation.  The transfer may still be in progress, however, so this
+   * does not eliminate other DMA-related race conditions.
+   */
+
+  flags = enter_critical_section();
+#ifdef CONFIG_USBHOST_ASYNCH
+  callback         = epinfo->callback;
+  arg              = epinfo->arg;
+#endif
+  iocwait          = epinfo->iocwait;
+
+#ifdef CONFIG_USBHOST_ASYNCH
+  epinfo->callback = NULL;
+  epinfo->arg      = NULL;
+#endif
+  epinfo->iocwait  = false;
+
+  /* This will prevent any callbacks from occurring while are performing
+   * the cancellation.  The transfer may still be in progress, however, so
+   * this does not eliminate other DMA-related race conditions.
+   */
+
+  epinfo->callback = NULL;
+  epinfo->arg      = NULL;
+  leave_critical_section(flags);
+
+  /* Bail if there is no transfer in progress for this endpoint */
+
+#ifdef CONFIG_USBHOST_ASYNCH
+  if (callback == NULL && !iocwait)
+#else
+  if (!iocwait)
+#endif
+    {
+      ret = OK;
+      goto errout_with_sem;
+    }
+
+  /* Handle the cancellation according to the type of the transfer */
+
+  switch (epinfo->xfrtype)
+    {
+      case USB_EP_ATTR_XFER_CONTROL:
+      case USB_EP_ATTR_XFER_BULK:
+        {
+          /* Get the horizontal pointer from the head of the asynchronous
+           * queue.
+           */
+
+          bp = (uint32_t *)&g_asynchead.hw.hlp;
+          qh = (struct kinetis_qh_s *)
+               kinetis_virtramaddr(kinetis_swap32(*bp) & QH_HLP_MASK);
+
+          /* If the asynchronous queue is empty, then the forward point in
+           * the asynchronous queue head will point back to the queue
+           * head.
+           */
+
+          if (qh && qh != &g_asynchead)
+            {
+              /* Claim that we successfully cancelled the transfer */
+
+              ret = OK;
+              goto exit_terminate;
+            }
+        }
+        break;
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+      case USB_EP_ATTR_XFER_INT:
+        {
+          /* Get the horizontal pointer from the head of the interrupt
+           * queue.
+           */
+
+          bp = (uint32_t *)&g_intrhead.hw.hlp;
+          qh = (struct kinetis_qh_s *)
+               kinetis_virtramaddr(kinetis_swap32(*bp) & QH_HLP_MASK);
+          if (qh)
+            {
+              /* if the queue is empty, then just claim that we successfully
+               * canceled the transfer.
+               */
+
+              ret = OK;
+              goto exit_terminate;
+            }
+        }
+        break;
+#endif
+
+#ifndef CONFIG_USBHOST_ISOC_DISABLE
+      case USB_EP_ATTR_XFER_ISOC:
+# warning "Isochronous endpoint support not emplemented"
+#endif
+      default:
+        usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
+        ret = -ENOSYS;
+        goto errout_with_sem;
+    }
+
+  /* Find and remove the QH.  There are four possibilities:
+   *
+   * 1)  The transfer has already completed and the QH is no longer in the
+   *     list.  In this case, sam_hq_foreach will return zero
+   * 2a) The transfer is not active and still pending.  It was removed from
+   *     the list and sam_hq_foreach will return one.
+   * 2b) The is active but not yet complete.  This is currently handled the
+   *     same as 2a).  REVISIT: This needs to be fixed.
+   * 3)  Some bad happened and sam_hq_foreach returned an error code < 0.
+   */
+
+  ret = kinetis_qh_foreach(qh, &bp, kinetis_qh_cancel, epinfo);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret);
+    }
+
+  /* Was there a pending synchronous transfer? */
+
+exit_terminate:
+  epinfo->result = -ESHUTDOWN;
+#ifdef CONFIG_USBHOST_ASYNCH
+  if (iocwait)
+    {
+      /* Yes... wake it up */
+
+      DEBUGASSERT(callback == NULL);
+      kinetis_givesem(&epinfo->iocsem);
+    }
+
+  /* No.. Is there a pending asynchronous transfer? */
+
+  else /* if (callback != NULL) */
+    {
+      /* Yes.. perform the callback */
+
+      callback(arg, -ESHUTDOWN);
+    }
+
+#else
+  /* Wake up the waiting thread */
+
+  kinetis_givesem(&epinfo->iocsem);
+#endif
+
+errout_with_sem:
+  kinetis_givesem(&g_ehci.exclsem);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: kinetis_connect
+ *
+ * Description:
+ *   New connections may be detected by an attached hub.  This method is the
+ *   mechanism that is used by the hub class to introduce a new connection
+ *   and port description to the system.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *     call to the class create() method.
+ *   hport - The descriptor of the hub port that detected the connection
+ *     related event
+ *   connected - True: device connected; false: device disconnected
+ *
+ * Returned Value:
+ *   On success, zero (OK) is returned. On a failure, a negated errno value
+ *   is returned indicating the nature of the failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_HUB
+static int kinetis_connect(FAR struct usbhost_driver_s *drvr,
+                         FAR struct usbhost_hubport_s *hport,
+                         bool connected)
+{
+  irqstate_t flags;
+
+  /* Set the connected/disconnected flag */
+
+  hport->connected = connected;
+  uinfo("Hub port %d connected: %s\n",
+        hport->port, connected ? "YES" : "NO");
+
+  /* Report the connection event */
+
+  flags = enter_critical_section();
+  DEBUGASSERT(g_ehci.hport == NULL); /* REVISIT */
+
+  g_ehci.hport = hport;
+  if (g_ehci.pscwait)
+    {
+      g_ehci.pscwait = false;
+      kinetis_givesem(&g_ehci.pscsem);
+    }
+
+  leave_critical_section(flags);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: kinetis_disconnect
+ *
+ * Description:
+ *   Called by the class when an error occurs and driver has been
+ *   disconnected.  The USB host driver should discard the handle to the
+ *   class instance (it is stale) and not attempt any further interaction
+ *   with the class driver instance (until a new instance is received from
+ *   the create() method).  The driver should not called the class'
+ *   disconnected() method.
+ *
+ * Input Parameters:
+ *   drvr - The USB host driver instance obtained as a parameter from the
+ *     call to the class create() method.
+ *   hport - The port from which the device is being disconnected.  Might be
+ *     a port on a hub.
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   - Only a single class bound to a single device is supported.
+ *   - Never called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void kinetis_disconnect(FAR struct usbhost_driver_s *drvr,
+                             FAR struct usbhost_hubport_s *hport)
+{
+  DEBUGASSERT(hport != NULL);
+  hport->devclass = NULL;
+}
+
+/****************************************************************************
+ * Name: kinetis_reset
+ *
+ * Description:
+ *   Set the HCRESET bit in the USBCMD register to reset the EHCI hardware.
+ *
+ *   Table 2-9. USBCMD - USB Command Register Bit Definitions
+ *
+ *    "Host Controller Reset (HCRESET) ... This control bit is used by
+ *     software to reset the host controller. The effects of this on Root
+ *     Hub registers are similar to a Chip Hardware Reset.
+ *
+ *    "When software writes a one to this bit, the Host Controller resets its
+ *     internal pipelines, timers, counters, state machines, etc. to their
+ *     initial value. Any transaction currently in progress on USB is
+ *     immediately terminated. A USB reset is not driven on downstream
+ *     ports.
+ *
+ *    "PCI Configuration registers are not affected by this reset. All
+ *     operational registers, including port registers and port state
+ *     machines are set to their initial values. Port ownership reverts
+ *     to the companion host controller(s)... Software must reinitialize
+ *     the host controller ... in order to return the host controller to
+ *     an operational state.
+ *
+ *    "This bit is set to zero by the Host Controller when the reset process
+ *     is complete. Software cannot terminate the reset process early by
+ *     writing a zero to this register. Software should not set this bit to
+ *     a one when the HCHalted bit in the USBSTS register is a zero.
+ *     Attempting to reset an actively running host controller will result
+ *     in undefined behavior."
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; A negated errno value is returned
+ *   on failure.
+ *
+ * Assumptions:
+ * - Called during the initialization of the EHCI.
+ *
+ ****************************************************************************/
+
+static int kinetis_reset(void)
+{
+  uint32_t regval;
+  unsigned int timeout;
+
+  /* Make sure that the EHCI is halted:  "When [the Run/Stop] bit is set to
+   * 0, the Host Controller completes the current transaction on the USB and
+   * then halts. The HC Halted bit in the status register indicates when the
+   * Host Controller has finished the transaction and has entered the
+   * stopped state..."
+   */
+
+  kinetis_putreg(0, &HCOR->usbcmd);
+
+  /* "... Software should not set [HCRESET] to a one when the HCHalted bit in
+   *  the USBSTS register is a zero. Attempting to reset an actively running
+   *  host controller will result in undefined behavior."
+   */
+
+  timeout = 0;
+  do
+    {
+      /* Wait one microsecond and update the timeout counter */
+
+      up_udelay(1);
+      timeout++;
+
+      /* Get the current value of the USBSTS register.  This loop will
+       * terminate when either the timeout exceeds one millisecond or when
+       * the HCHalted bit is no longer set in the USBSTS register.
+       */
+
+      regval = kinetis_getreg(&HCOR->usbsts);
+    }
+  while (((regval & EHCI_USBSTS_HALTED) == 0) && (timeout < 1000));
+
+  /* Is the EHCI still running?  Did we timeout? */
+
+  if ((regval & EHCI_USBSTS_HALTED) == 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_HCHALTED_TIMEOUT, regval);
+      return -ETIMEDOUT;
+    }
+
+  /* Now we can set the HCReset bit in the USBCMD register to initiate the reset */
+
+  regval  = kinetis_getreg(&HCOR->usbcmd);
+  regval |= EHCI_USBCMD_HCRESET;
+  kinetis_putreg(regval, &HCOR->usbcmd);
+
+  /* Wait for the HCReset bit to become clear */
+
+  do
+    {
+      /* Wait five microsecondw and update the timeout counter */
+
+      up_udelay(5);
+      timeout += 5;
+
+      /* Get the current value of the USBCMD register.  This loop will
+       * terminate when either the timeout exceeds one second or when the
+       * HCReset bit is no longer set in the USBSTS register.
+       */
+
+      regval = kinetis_getreg(&HCOR->usbcmd);
+    }
+  while (((regval & EHCI_USBCMD_HCRESET) != 0) && (timeout < 1000000));
+
+  /* Return either success or a timeout */
+
+  return (regval & EHCI_USBCMD_HCRESET) != 0 ? -ETIMEDOUT : OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kinetis_ehci_initialize
+ *
+ * Description:
+ *   Initialize USB EHCI host controller hardware.
+ *
+ * Input Parameters:
+ *   controller -- If the device supports more than one EHCI interface, then
+ *     this identifies which controller is being initialized.  Normally, this
+ *     is just zero.
+ *
+ * Returned Value:
+ *   And instance of the USB host interface.  The controlling task should
+ *   use this interface to (1) call the wait() method to wait for a device
+ *   to be connected, and (2) call the enumerate() method to bind the device
+ *   to a class driver.
+ *
+ * Assumptions:
+ * - This function should called in the initialization sequence in order
+ *   to initialize the USB device functionality.
+ * - Class drivers should be initialized prior to calling this function.
+ *   Otherwise, there is a race condition if the device is already connected.
+ *
+ ****************************************************************************/
+
+FAR struct usbhost_connection_s *kinetis_ehci_initialize(int controller)
+{
+  FAR struct usbhost_hubport_s *hport;
+  uint32_t regval;
+#  if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
+  uint16_t regval16;
+  unsigned int nports;
+#  endif
+  uintptr_t physaddr;
+  int ret;
+  int i;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(controller == 0);
+  DEBUGASSERT(((uintptr_t) & g_asynchead & 0x1f) == 0);
+  DEBUGASSERT((sizeof(struct kinetis_qh_s) & 0x1f) == 0);
+  DEBUGASSERT((sizeof(struct kinetis_qtd_s) & 0x1f) == 0);
+
+#  ifdef CONFIG_KINETIS_EHCI_PREALLOCATE
+  DEBUGASSERT(((uintptr_t) & g_qhpool & 0x1f) == 0);
+  DEBUGASSERT(((uintptr_t) & g_qtdpool & 0x1f) == 0);
+#  endif
+
+#  ifndef CONFIG_USBHOST_INT_DISABLE
+  DEBUGASSERT(((uintptr_t) & g_intrhead & 0x1f) == 0);
+#    ifdef CONFIG_KINETIS_EHCI_PREALLOCATE
+  DEBUGASSERT(((uintptr_t) g_framelist & 0xfff) == 0);
+#    endif
+#  endif                               /* CONFIG_USBHOST_INT_DISABLE */
+
+  /* Software Configuration *************************************************/
+
+  usbhost_vtrace1(EHCI_VTRACE1_INITIALIZING, 0);
+
+  /* Initialize the EHCI state data structure */
+
+  nxsem_init(&g_ehci.exclsem, 0, 1);
+  nxsem_init(&g_ehci.pscsem, 0, 0);
+
+  /* The pscsem semaphore is used for signaling and, hence, should not have
+   * priority inheritance enabled.
+   */
+
+  nxsem_set_protocol(&g_ehci.pscsem, SEM_PRIO_NONE);
+
+  /* Initialize EP0 */
+
+  nxsem_init(&g_ehci.ep0.iocsem, 0, 1);
+
+  /* Initialize the root hub port structures */
+
+  for (i = 0; i < KINETIS_EHCI_NRHPORT; i++)
+    {
+      struct kinetis_rhport_s *rhport = &g_ehci.rhport[i];
+
+      /* Initialize the device operations */
+
+      rhport->drvr.ep0configure = kinetis_ep0configure;
+      rhport->drvr.epalloc = kinetis_epalloc;
+      rhport->drvr.epfree = kinetis_epfree;
+      rhport->drvr.alloc = kinetis_alloc;
+      rhport->drvr.free = kinetis_free;
+      rhport->drvr.ioalloc = kinetis_ioalloc;
+      rhport->drvr.iofree = kinetis_iofree;
+      rhport->drvr.ctrlin = kinetis_ctrlin;
+      rhport->drvr.ctrlout = kinetis_ctrlout;
+      rhport->drvr.transfer = kinetis_transfer;
+#  ifdef CONFIG_USBHOST_ASYNCH
+      rhport->drvr.asynch = kinetis_asynch;
+#  endif
+      rhport->drvr.cancel = kinetis_cancel;
+#  ifdef CONFIG_USBHOST_HUB
+      rhport->drvr.connect = kinetis_connect;
+#  endif
+      rhport->drvr.disconnect = kinetis_disconnect;
+
+      /* Initialize EP0 */
+
+      rhport->ep0.xfrtype = USB_EP_ATTR_XFER_CONTROL;
+      rhport->ep0.speed = USB_SPEED_FULL;
+      rhport->ep0.maxpacket = 8;
+
+      /* The EP0 iocsem semaphore is used for signaling and, hence, should
+       * not have priority inheritance enabled.
+       */
+
+      nxsem_init(&rhport->ep0.iocsem, 0, 0);
+      nxsem_set_protocol(&rhport->ep0.iocsem, SEM_PRIO_NONE);
+
+      /* Initialize the public port representation */
+
+      hport = &rhport->hport.hport;
+      hport->drvr = &rhport->drvr;
+#  ifdef CONFIG_USBHOST_HUB
+      hport->parent = NULL;
+#  endif
+      hport->ep0 = &rhport->ep0;
+      hport->port = i;
+      hport->speed = USB_SPEED_FULL;
+
+      /* Initialize function address generation logic */
+
+      usbhost_devaddr_initialize(&rhport->hport);
+    }
+
+#  ifndef CONFIG_KINETIS_EHCI_PREALLOCATE
+  /* Allocate a pool of free Queue Head (QH) structures */
+
+  g_qhpool =
+    (struct kinetis_qh_s *)kmm_memalign(32,
+                                      CONFIG_KINETIS_EHCI_NQHS *
+                                      sizeof(struct kinetis_qh_s));
+  if (!g_qhpool)
+    {
+      usbhost_trace1(EHCI_TRACE1_QHPOOLALLOC_FAILED, 0);
+      return NULL;
+    }
+#  endif
+
+  /* Initialize the list of free Queue Head (QH) structures */
+
+  for (i = 0; i < CONFIG_KINETIS_EHCI_NQHS; i++)
+    {
+      /* Put the QH structure in a free list */
+
+      kinetis_qh_free(&g_qhpool[i]);
+    }
+
+#  ifndef CONFIG_KINETIS_EHCI_PREALLOCATE
+  /* Allocate a pool of free Transfer Descriptor (qTD) structures */
+
+  g_qtdpool =
+    (struct kinetis_qtd_s *)kmm_memalign(32,
+                                       CONFIG_KINETIS_EHCI_NQTDS *
+                                       sizeof(struct kinetis_qtd_s));
+  if (!g_qtdpool)
+    {
+      usbhost_trace1(EHCI_TRACE1_QTDPOOLALLOC_FAILED, 0);
+      kmm_free(g_qhpool);
+      return NULL;
+    }
+#  endif
+
+#  if !defined(CONFIG_KINETIS_EHCI_PREALLOCATE) && !defined(CONFIG_USBHOST_INT_DISABLE)
+  /* Allocate the periodic framelist */
+
+  g_framelist = (uint32_t *)
+    kmm_memalign(4096, FRAME_LIST_SIZE * sizeof(uint32_t));
+  if (!g_framelist)
+    {
+      usbhost_trace1(EHCI_TRACE1_PERFLALLOC_FAILED, 0);
+      kmm_free(g_qhpool);
+      kmm_free(g_qtdpool);
+      return NULL;
+    }
+#  endif
+
+  /* Initialize the list of free Transfer Descriptor (qTD) structures */
+
+  for (i = 0; i < CONFIG_KINETIS_EHCI_NQTDS; i++)
+    {
+      /* Put the TD in a free list */
+
+      kinetis_qtd_free(&g_qtdpool[i]);
+    }
+
+  /* Reset the controller from the OTG peripheral */
+
+  putreg32(USBHS_USBCMD_RST, KINETIS_USBHS_USBCMD);
+  while ((getreg32(KINETIS_USBHS_USBCMD) & USBHS_USBCMD_RST) != 0);
+
+  /* Program the controller to be the USB host controller Fixed selections:
+   * CM = Host mode ES = 0, Little endian mode.  SLOM Not used in host mode.
+   * VBPS = 1, off-chip power source Configurable selections: SDIS = 1,
+   * Stream disable mode.  Eliminates overruns/underruns at the expense of
+   * some performance.
+   */
+
+#  ifdef CONFIG_KINETIS_EHCI_SDIS
+  putreg32(USBHS_USBMODE_CM_HOST | USBHS_USBMODE_SDIS,
+      KINETIS_USBHS_MODE);
+#  else
+  putreg32(USBHS_USBMODE_CM_HOST,
+      KINETIS_USBHS_MODE);
+#  endif
+
+  /* Reset the EHCI hardware */
+
+  ret = kinetis_reset();
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_RESET_FAILED, -ret);
+      return NULL;
+    }
+
+  /* Re-program the USB host controller.  As implemented, kinetis_reset()
+   * requires the host mode setup in order to work.  However, we lose the
+   * host configuration in the reset.
+   */
+
+#  ifdef CONFIG_KINETIS_EHCI_SDIS
+  putreg32(USBHS_USBMODE_CM_HOST | USBHS_USBMODE_SDIS,
+      KINETIS_USBHS_MODE);
+#  else
+  putreg32(USBHS_USBMODE_CM_HOST,
+      KINETIS_USBHS_MODE);
+#  endif
+
+  /* Disable all interrupts */
+
+  kinetis_putreg(0, &HCOR->usbintr);
+
+  /* Clear pending interrupts.  Bits in the USBSTS register are cleared by
+   * writing a '1' to the corresponding bit.
+   */
+
+  kinetis_putreg(EHCI_INT_ALLINTS, &HCOR->usbsts);
+
+#  if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
+  /* Show the EHCI version */
+
+  regval16 = kinetis_swap16(HCCR->hciversion);
+  usbhost_vtrace2(EHCI_VTRACE2_HCIVERSION, regval16 >> 8, regval16 & 0xff);
+
+  /* Verify that the correct number of ports is reported */
+
+  regval = kinetis_getreg(&HCCR->hcsparams);
+  nports = (regval & EHCI_HCSPARAMS_NPORTS_MASK) >>
+            EHCI_HCSPARAMS_NPORTS_SHIFT;
+
+  usbhost_vtrace2(EHCI_VTRACE2_HCSPARAMS, nports, regval);
+  DEBUGASSERT(nports == KINETIS_EHCI_NRHPORT);
+
+  /* Show the HCCPARAMS register */
+
+  regval = kinetis_getreg(&HCCR->hccparams);
+  usbhost_vtrace1(EHCI_VTRACE1_HCCPARAMS, regval);
+#  endif
+
+  /* Initialize the head of the asynchronous queue/reclamation list. "In
+   * order to communicate with devices via the asynchronous schedule, system
+   * software must write the ASYNDLISTADDR register with the address of a
+   * control or bulk queue head. Software must then enable the asynchronous
+   * schedule by writing a one to the Asynchronous Schedule Enable bit in
+   * the USBCMD register. In order to communicate with devices via the
+   * periodic schedule, system software must enable the periodic schedule by
+   * writing a one to the Periodic Schedule Enable bit in the USBCMD
+   * register. Note that the schedules can be turned on before the first
+   * port is reset (and enabled)."
+   */
+
+  memset(&g_asynchead, 0, sizeof(struct kinetis_qh_s));
+  physaddr = kinetis_physramaddr((uintptr_t) & g_asynchead);
+  g_asynchead.hw.hlp = kinetis_swap32(physaddr | QH_HLP_TYP_QH);
+  g_asynchead.hw.epchar = kinetis_swap32(QH_EPCHAR_H | QH_EPCHAR_EPS_FULL);
+  g_asynchead.hw.overlay.nqp = kinetis_swap32(QH_NQP_T);
+  g_asynchead.hw.overlay.alt = kinetis_swap32(QH_NQP_T);
+  g_asynchead.hw.overlay.token = kinetis_swap32(QH_TOKEN_HALTED);
+  g_asynchead.fqp = kinetis_swap32(QTD_NQP_T);
+
+  /* Set the Current Asynchronous List Address. */
+
+  up_flush_dcache((uintptr_t)&g_asynchead.hw,
+    (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s));
+
+  kinetis_putreg(kinetis_swap32(physaddr), &HCOR->asynclistaddr);
+
+#  ifndef CONFIG_USBHOST_INT_DISABLE
+
+  /* Initialize the head of the periodic list.  Since Isochronous endpoints
+   * are not not yet supported, each element of the frame list is initialized
+   * to point to the Interrupt Queue Head (g_intrhead).
+   */
+
+  memset(&g_intrhead, 0, sizeof(struct kinetis_qh_s));
+  g_intrhead.hw.hlp = kinetis_swap32(QH_HLP_T);
+  g_intrhead.hw.overlay.nqp = kinetis_swap32(QH_NQP_T);
+  g_intrhead.hw.overlay.alt = kinetis_swap32(QH_NQP_T);
+  g_intrhead.hw.overlay.token = kinetis_swap32(QH_TOKEN_HALTED);
+  g_intrhead.hw.epcaps = kinetis_swap32(QH_EPCAPS_SSMASK(1));
+
+  /* Attach the periodic QH to Period Frame List */
+
+  physaddr = kinetis_physramaddr((uintptr_t) & g_intrhead);
+  for (i = 0; i < FRAME_LIST_SIZE; i++)
+    {
+      g_framelist[i] = kinetis_swap32(physaddr) | PFL_TYP_QH;
+    }
+
+  /* Set the Periodic Frame List Base Address. */
+
+  physaddr = kinetis_physramaddr((uintptr_t) g_framelist);
+  kinetis_putreg(kinetis_swap32(physaddr), &HCOR->periodiclistbase);
+#  endif
+
+  /* Enable the asynchronous schedule and, possibly enable the periodic
+   * schedule and set the frame list size.
+   */
+
+  regval = kinetis_getreg(&HCOR->usbcmd);
+  regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK |
+              EHCI_USBCMD_FLSIZE_MASK | EHCI_USBCMD_PSEN |
+              EHCI_USBCMD_IAADB | EHCI_USBCMD_LRESET);
+  regval |= EHCI_USBCMD_ASEN;
+
+#  ifndef CONFIG_USBHOST_INT_DISABLE
+  regval |= EHCI_USBCMD_PSEN;
+#    if FRAME_LIST_SIZE == 1024
+  regval |= EHCI_USBCMD_FLSIZE_1024;
+#    elif FRAME_LIST_SIZE == 512
+  regval |= EHCI_USBCMD_FLSIZE_512;
+#    elif FRAME_LIST_SIZE == 256
+  regval |= EHCI_USBCMD_FLSIZE_256;
+#    else
+#      error Unsupported frame size list size
+#    endif
+#  endif
+
+  kinetis_putreg(regval, &HCOR->usbcmd);
+
+  /* Start the host controller by setting the RUN bit in the USBCMD register. */
+
+  regval = kinetis_getreg(&HCOR->usbcmd);
+  regval |= EHCI_USBCMD_RUN;
+  kinetis_putreg(regval, &HCOR->usbcmd);
+
+  /* Route all ports to this host controller by setting the CONFIG flag. */
+
+  regval = kinetis_getreg(&HCOR->configflag);
+  regval |= EHCI_CONFIGFLAG;
+  kinetis_putreg(regval, &HCOR->configflag);
+
+  /* Wait for the EHCI to run (i.e., no longer report halted) */
+
+  ret = ehci_wait_usbsts(EHCI_USBSTS_HALTED, 0, 100 * 1000);
+  if (ret < 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_RUN_FAILED, kinetis_getreg(&HCOR->usbsts));
+      return NULL;
+    }
+
+  /* Interrupt Configuration ************************************************/
+
+  ret = irq_attach(KINETIS_IRQ_USB1OTG, kinetis_ehci_interrupt, NULL);
+  if (ret != 0)
+    {
+      usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, KINETIS_IRQ_USB1OTG);
+      return NULL;
+    }
+
+  /* Enable EHCI interrupts.  Interrupts are still disabled at the level of
+   * the interrupt controller.
+   */
+
+  kinetis_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr);
+
+  /* Enable interrupts at the interrupt controller */
+
+  up_enable_irq(KINETIS_IRQ_USB1OTG);
+
+  /* Drive Vbus +5V (the smoke test) */
+
+  for (i = 0; i < KINETIS_EHCI_NRHPORT; i++)
+    {
+      /* Enable VBUS power for the port */
+
+      kinetis_usbhost_vbusdrive(i, true);
+      up_mdelay(25);
+    }
+
+  /* If there is a USB device in the slot at power up, then we will not get
+   * the status change interrupt to signal us that the device is connected.
+   * We need to set the initial connected state accordingly.
+   */
+
+  for (i = 0; i < KINETIS_EHCI_NRHPORT; i++)
+    {
+      g_ehci.rhport[i].connected =
+        ((kinetis_getreg(&HCOR->portsc[i]) & EHCI_PORTSC_CCS) != 0);
+    }
+
+  usbhost_vtrace1(EHCI_VTRACE1_INIITIALIZED, 0);
+
+  /* Initialize and return the connection interface */
+
+  g_ehciconn.wait = kinetis_wait;
+  g_ehciconn.enumerate = kinetis_enumerate;
+  return &g_ehciconn;
+}
+
+/****************************************************************************
+ * Name: usbhost_trformat1 and usbhost_trformat2
+ *
+ * Description:
+ *   This interface must be provided by platform specific logic that knows
+ *   the HCDs encoding of USB trace data.
+ *
+ *   Given an 9-bit index, return a format string suitable for use with, say,
+ *   printf.  The returned format is expected to handle two unsigned integer
+ *   values.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_USBHOST_TRACE
+FAR const char *usbhost_trformat1(uint16_t id)
+{
+  int ndx = TRACE1_INDEX(id);
+
+  if (ndx < TRACE1_NSTRINGS)
+    {
+      return g_trace1[ndx].string;
+    }
+
+  return NULL;
+}
+
+FAR const char *usbhost_trformat2(uint16_t id)
+{
+  int ndx = TRACE2_INDEX(id);
+
+  if (ndx < TRACE2_NSTRINGS)
+    {
+      return g_trace2[ndx].string;
+    }
+
+  return NULL;
+}
+#endif /* HAVE_USBHOST_TRACE */
+
+#endif /* CONFIG_KINETIS_USBOTG && CONFIG_USBHOST */
diff --git a/arch/arm/src/kinetis/kinetis_usbhshost.h b/arch/arm/src/kinetis/kinetis_usbhshost.h
new file mode 100644
index 0000000..cbf817c
--- /dev/null
+++ b/arch/arm/src/kinetis/kinetis_usbhshost.h
@@ -0,0 +1,149 @@
+/*****************************************************************************
+ * arch/arm/src/kinetis/kinetis_usbhshost.h
+ *
+ *   Copyright (C) 2012, 2015, 2019 Gregory Nutt. All rights reserved.
+ *   Authors: Gregory Nutt <gn...@nuttx.org>
+ *            Dave Marples <da...@marples.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_KINETIS_KINETIS_USBHSHOST_H
+#define __ARCH_ARM_SRC_KINETIS_KINETIS_USBHSHOST_H
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Public Types
+ *****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/*****************************************************************************
+ * Public Data
+ *****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/*****************************************************************************
+ * Public Function Prototypes
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: kinetis_usbhost_vbusdrive
+ *
+ * Description:
+ *   Enable/disable driving of VBUS 5V output.  This function must be provided
+ *   by each platform that implements the OHCI or EHCI host interface
+ *
+ * Input Parameters:
+ *   rhport - Selects root hub port to be powered host interface. Since the
+ *   KINETIS has only a downstream port, zero is the only possible value for
+ *           this parameter.
+ *   enable - true: enable VBUS power; false: disable VBUS power
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+extern void kinetis_usbhost_vbusdrive(int rhport, bool enable);
+
+/*****************************************************************************
+ * Name: kinetis_setup_overcurrent
+ *
+ * Description:
+ *   Setup to receive an interrupt-level callback if an over-current condition
+ *   is detected.
+ *
+ * Input Parameters:
+ *   handler - New over-current interrupt handler
+ *   arg     - The argument that will accompany the interrupt
+ *
+ * Returned Value:
+ *   Zero (OK) returned on success; a negated errno value is returned on
+ *   failure.
+ *
+ *****************************************************************************/
+
+extern int kinetis_setup_overcurrent(xcpt_t handler, void *arg);
+
+/*****************************************************************************
+ * Name: kinetis_ehci_initialize
+ *
+ * Description:
+ *   Initialize USB EHCI host controller hardware.
+ *
+ * Input Parameters:
+ *   controller -- If the device supports more than one EHCI interface, then
+ *     this identifies which controller is being initialized.  Normally, this
+ *     is just zero.
+ *
+ * Returned Value:
+ *   And instance of the USB host interface.  The controlling task should
+ *   use this interface to (1) call the wait() method to wait for a device
+ *   to be connected, and (2) call the enumerate() method to bind the device
+ *   to a class driver.
+ *
+ * Assumptions:
+ * - This function should called in the initialization sequence in order
+ *   to initialize the USB device functionality.
+ * - Class drivers should be initialized prior to calling this function.
+ *   Otherwise, there is a race condition if the device is already connected.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_KINETIS_USBHS) && defined(CONFIG_USBHOST)
+struct usbhost_connection_s;
+FAR struct usbhost_connection_s *kinetis_ehci_initialize(int controller);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_KINETIS_KINETIS_USBHSHOST_H */
diff --git a/boards/arm/kinetis/freedom-k28f/src/Makefile b/boards/arm/kinetis/freedom-k28f/src/Makefile
index 10c7977..53b45b6 100644
--- a/boards/arm/kinetis/freedom-k28f/src/Makefile
+++ b/boards/arm/kinetis/freedom-k28f/src/Makefile
@@ -66,4 +66,10 @@ ifeq ($(CONFIG_KINETIS_USBOTG),y)
 CSRCS += k28_usbdev.c
 endif
 
+ifeq ($(CONFIG_KINETIS_USBHS),y)
+ifeq ($(CONFIG_USBHOST),y)
+CSRCS += k28_usbhshost.c
+endif
+endif
+
 include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
index e1ef15c..64cbd86 100644
--- a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
+++ b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
@@ -164,6 +164,15 @@
 #define GPIO_SW2           (GPIO_PULLUP | PIN_INT_BOTH | PIN_PORTA | PIN4)
 #define GPIO_SW3           (GPIO_PULLUP | PIN_INT_BOTH | PIN_PORTD | PIN0)
 
+/* A micro Secure Digital (SD) card slot is available on the FRDM-K28F
+ * connected to the SD Host Controller (SDHC) signals of the MCU.
+ * This slot will accept micro format SD memory cards.
+ * The SD card detect pin (PTB5) is an open switch that shorts with VDD when
+ * card is inserted.
+ */
+
+#define GPIO_SD_CARDDETECT (GPIO_INPUT | PIN_INT_BOTH | PIN_PORTB | PIN5)
+
 /****************************************************************************
  * Public Types
  ****************************************************************************/
@@ -235,6 +244,20 @@ void k28_i2cdev_initialize(void);
 extern void weak_function k28_usbdev_initialize(void);
 
 /****************************************************************************
+ * Name: k28_usbhost_initialize
+ *
+ * Description:
+ *   Inititialize USB High Speed Host
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_KINETIS_USBHS) && defined(CONFIG_USBHOST)
+int k28_usbhost_initialize(void);
+#else
+#  define k28_usbhost_initialize() (OK)
+#endif
+
+/****************************************************************************
  * Name: k28_sdhc_initialize
  *
  * Description:
diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
index e4a3119..741d445 100644
--- a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
+++ b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
@@ -153,6 +153,11 @@ int k28_bringup(void)
     }
 #endif
 
+#if defined(CONFIG_USBHOST) && defined(CONFIG_KINETIS_USBHS)
+  k28_usbhost_initialize();
+#endif
+
+
   UNUSED(ret);
   return OK;
 }
diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c b/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c
new file mode 100644
index 0000000..bf7850b
--- /dev/null
+++ b/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c
@@ -0,0 +1,409 @@
+/*****************************************************************************
+ * boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c
+ *
+ *   Copyright (C) 2013, 2015-2017 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/kthread.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbhost.h>
+#include <nuttx/usb/usbdev_trace.h>
+#include <nuttx/usb/ehci.h>
+
+#include <kinetis_usbhshost.h>
+
+#include "arm_arch.h"
+#include "hardware/kinetis_k28pinmux.h"
+#include "hardware/kinetis_mcg.h"
+#include "hardware/kinetis_sim.h"
+#include "hardware/kinetis_osc.h"
+#include "hardware/kinetis_usbhs.h"
+#include "freedom-k28f.h"
+
+#include <arch/board/board.h>  /* Must always be included last */
+
+#if defined(CONFIG_KINETIS_USBHS) && defined(CONFIG_USBHOST)
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+#define BOARD_USB_PHY_D_CAL     (0x0CU)
+#define BOARD_USB_PHY_TXCAL45DP (0x06U)
+#define BOARD_USB_PHY_TXCAL45DM (0x06U)
+
+#ifndef CONFIG_USBHOST_DEFPRIO
+#  define CONFIG_USBHOST_DEFPRIO 50
+#endif
+
+#ifndef CONFIG_USBHOST_STACKSIZE
+#  ifdef CONFIG_USBHOST_HUB
+#    define CONFIG_USBHOST_STACKSIZE 1536
+#  else
+#    define CONFIG_USBHOST_STACKSIZE 1024
+#  endif
+#endif
+
+/*****************************************************************************
+ * Private Data
+ *****************************************************************************/
+
+/* Retained device driver handle */
+
+static struct usbhost_connection_s *g_ehciconn;
+
+/*****************************************************************************
+ * Private Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: ehci_waiter
+ *
+ * Description:
+ *   Wait for USB devices to be connected to the EHCI root hub.
+ *
+ *****************************************************************************/
+
+static int ehci_waiter(int argc, char *argv[])
+{
+  FAR struct usbhost_hubport_s *hport;
+
+  uinfo("ehci_waiter:  Running\n");
+  for (; ; )
+    {
+      /* Wait for the device to change state */
+
+      DEBUGVERIFY(CONN_WAIT(g_ehciconn, &hport));
+      syslog(LOG_INFO, "ehci_waiter: %s\n",
+             hport->connected ? "connected" : "disconnected");
+
+      /* Did we just become connected? */
+
+      if (hport->connected)
+        {
+          /* Yes.. enumerate the newly connected device */
+
+          CONN_ENUMERATE(g_ehciconn, hport);
+        }
+    }
+
+  /* Keep the compiler from complaining */
+
+  return 0;
+}
+
+/*****************************************************************************
+ * Name: ehci_hw_init
+ *
+ * Description:
+ *   Initialize PHY and clocks for EHCI
+ *
+ *****************************************************************************/
+
+static void ehci_hwinit(void)
+{
+  uint32_t regval;
+  uint8_t  regval8;
+
+  /* Enable Internal Reference Clock */
+
+  regval8  = getreg8(KINETIS_MCG_C1);
+  regval8 |= (MCG_C1_IRCLKEN);
+  putreg8(regval8, KINETIS_MCG_C1);
+
+  /* Enable External Reference Clock */
+
+  regval8  = getreg8(KINETIS_OSC_CR);
+  regval8 |= (OSC_CR_ERCLKEN);
+  putreg8(regval8, KINETIS_OSC_CR);
+
+  /* Enable PLL Regulator */
+
+  regval  = getreg32(KINETIS_SIM_SOPT2);
+  regval |= (SIM_SOPT2_USBREGEN);
+  putreg32(regval, KINETIS_SIM_SOPT2);
+
+  /* Gate USB clock */
+
+  regval  = getreg32(KINETIS_SIM_SCGC3);
+  regval |= (SIM_SCGC3_USBHSPHY);
+  putreg32(regval, KINETIS_SIM_SCGC3);
+
+  /* Release Softreset and ungate PHY Clock */
+
+  regval  = getreg32(KINETIS_USBHSPHY_CTRL);
+  regval &= ~(USBPHY_CTRLn_SFTRST);
+  regval &= ~(USBPHY_CTRLn_CLKGATE);
+  putreg32(regval, KINETIS_USBHSPHY_CTRL);
+
+  /* Set PHY PLL Clock */
+
+  regval  = getreg32(KINETIS_USBHSPHY_PLL_SIC);
+  regval &= ~(USBPHY_PLL_SICn_PLL_BYPASS);
+  regval &= ~(USBPHY_PLL_SICn_PLL_DIV_SEL_MASK);
+  regval |= (USBPHY_PLL_SICn_PLL_POWER);
+  regval |= (USBPHY_PLL_SICn_PLL_EN_USB_CLKS);
+#  if (BOARD_EXTAL_FREQ == 24000000)
+  regval |= USBPHY_PLL_SICn_PLL_DIV_SEL_24MHZ;
+#  elif (BOARD_EXTAL_FREQ == 16000000)
+  regval |= USBPHY_PLL_SICn_PLL_DIV_SEL_16MHZ;
+#  elif (BOARD_EXTAL_FREQ == 12000000)
+  regval |= USBPHY_PLL_SICn_PLL_DIV_SEL_12MHZ;
+#  else
+#    warning Not supported.
+#  endif
+  putreg32(regval, KINETIS_USBHSPHY_PLL_SIC);
+
+  regval  = getreg32(KINETIS_USBHSPHY_TRIM_OVERRIDE_EN);
+  regval |= (USBPHY_TRIM_OVERRIDE_ENn_TRIM_DIV_SEL_OVERRIDE);
+  putreg32(regval, KINETIS_USBHSPHY_TRIM_OVERRIDE_EN);
+
+  do
+    {
+      regval = getreg32(KINETIS_USBHSPHY_PLL_SIC);
+    }
+  while (!(regval & USBPHY_PLL_SICn_PLL_LOCK));
+
+  /* Enable USBHS Clock and Regulator */
+
+  regval  = getreg32(KINETIS_SIM_SCGC3);
+  regval |= (SIM_SCGC3_USBHS);
+  putreg32(regval, KINETIS_SIM_SCGC3);
+
+  regval  = getreg32(KINETIS_SIM_USBPHYCTL);
+  regval &= ~(SIM_USBPHYCTL_USB3VOUTTRG_MASK);
+  regval |= SIM_USBPHYCTL_USB3VOUTTRG_3V310;
+  regval |= SIM_USBPHYCTL_USBVREGSEL;
+  putreg32(regval, KINETIS_SIM_USBPHYCTL);
+
+  /* Disable Powerdown */
+
+  putreg32(0, KINETIS_USBHSPHY_PWD);
+
+  /* Misc */
+
+  regval  = getreg32(KINETIS_USBHSPHY_CTRL);
+  regval |= USBPHY_CTRLn_ENUTMILEVEL2;
+  regval |= USBPHY_CTRLn_ENUTMILEVEL3;
+  putreg32(regval, KINETIS_USBHSPHY_CTRL);
+}
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: k28_usbhost_initialize
+ *
+ * Description:
+ *   Called at application startup time to initialize the USB host
+ *   functionality.
+ *   This function will start a thread that will monitor for device
+ *   connection/disconnection events.
+ *
+ *****************************************************************************/
+
+int k28_usbhost_initialize(void)
+{
+  pid_t    pid;
+  int      ret;
+
+  /* First, register all of the class drivers needed to support the drivers
+   * that we care about
+   */
+
+#ifdef CONFIG_USBHOST_HUB
+  /* Initialize USB hub support */
+
+  ret = usbhost_hub_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: usbhost_hub_initialize failed: %d\n", ret);
+    }
+#endif
+
+#ifdef CONFIG_USBHOST_MSC
+  /* Register the USB host Mass Storage Class */
+
+  ret = usbhost_msc_initialize();
+  if (ret != OK)
+    {
+      syslog(LOG_ERR,
+             "ERROR: Failed to register the mass storage class: %d\n", ret);
+    }
+#endif
+
+#ifdef CONFIG_USBHOST_CDCACM
+  /* Register the CDC/ACM serial class */
+
+  ret = usbhost_cdcacm_initialize();
+  if (ret != OK)
+    {
+      uerr("ERROR: Failed to register the CDC/ACM serial class\n");
+    }
+#endif
+
+#ifdef CONFIG_USBHOST_HIDKBD
+  /* Register the USB host HID keyboard class driver */
+
+  ret = usbhost_kbdinit();
+  if (ret != OK)
+    {
+      uerr("ERROR: Failed to register the KBD class\n");
+    }
+#endif
+
+  /* Enable PHY and clocks */
+
+  ehci_hwinit();
+
+  /* Then get an instance of the USB EHCI interface. */
+
+  g_ehciconn = kinetis_ehci_initialize(0);
+
+  if (!g_ehciconn)
+    {
+      uerr("ERROR: kinetis_ehci_initialize failed\n");
+      return -ENODEV;
+    }
+
+  /* Start a thread to handle device connection. */
+
+  pid = kthread_create("EHCI Monitor", CONFIG_USBHOST_DEFPRIO,
+                       CONFIG_USBHOST_STACKSIZE,
+                       (main_t)ehci_waiter, (FAR char * const *)NULL);
+  if (pid < 0)
+    {
+      uerr("ERROR: Failed to create ehci_waiter task: %d\n", ret);
+      return -ENODEV;
+    }
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: kinetis_usbhost_vbusdrive
+ *
+ * Description:
+ *   Enable/disable driving of VBUS 5V output.  This function must be
+ *   provided by each platform that implements the OHCI or EHCI host
+ *   interface
+ *
+ * Input Parameters:
+ *   rhport - Selects root hub port to be powered host interface.
+ *            Since the KINETIS has only a downstream port, zero is
+ *            the only possible value for this parameter.
+ *   enable - true: enable VBUS power; false: disable VBUS power
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+#define HCOR ((volatile struct ehci_hcor_s *)KINETIS_USBHS_HCOR_BASE)
+
+void kinetis_usbhost_vbusdrive(int rhport, bool enable)
+{
+  uint32_t regval;
+
+  uinfo("RHPort%d: enable=%d\n", rhport + 1, enable);
+
+  /* The KINETIS has only a single root hub port */
+
+  if (rhport == 0)
+    {
+      /* Then enable or disable VBUS power */
+
+      regval = HCOR->portsc[rhport];
+      regval &= ~EHCI_PORTSC_PP;
+      if (enable)
+        {
+          regval |= EHCI_PORTSC_PP;
+        }
+
+      HCOR->portsc[rhport] = regval;
+    }
+}
+
+/****************************************************************************
+ * Name: kinetis_setup_overcurrent
+ *
+ * Description:
+ *   Setup to receive an interrupt-level callback if an overcurrent condition
+ *   is detected.
+ *
+ * Input Parameters:
+ *   handler - New overcurrent interrupt handler
+ *   arg     - The argument that will accompany the interrupt
+ *
+ * Returned Value:
+ *   Zero (OK) returned on success; a negated errno value is returned on
+ *   failure.
+ *
+ ****************************************************************************/
+
+#if 0 /* Not ready yet */
+int kinetis_setup_overcurrent(xcpt_t handler, void *arg)
+{
+  irqstate_t flags;
+
+  /* Disable interrupts until we are done.  This guarantees that the
+   * following operations are atomic.
+   */
+
+  flags = enter_critical_section();
+
+  /* Configure the interrupt */
+
+#warning Missing logic
+
+  leave_critical_section(flags);
+  return OK;
+}
+#endif /* 0 */
+
+#endif /* CONFIG_KINETIS_USBOTG || CONFIG_USBHOST */