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 */