summaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-20 15:46:36 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-20 15:46:36 -0600
commit70e94e05ef4f7aed52722ea114dc59b517ec2695 (patch)
treea8a4d0804f44e216759ec8dbd52c4920ed50e1a6 /nuttx
parent20174857959f8d35c17a780faf5871d62fb274ff (diff)
downloadnuttx-70e94e05ef4f7aed52722ea114dc59b517ec2695.tar.gz
nuttx-70e94e05ef4f7aed52722ea114dc59b517ec2695.tar.bz2
nuttx-70e94e05ef4f7aed52722ea114dc59b517ec2695.zip
Beginning of support for SAMA5 EHCI. Not much there yet
Diffstat (limited to 'nuttx')
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig32
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_ehci.h4
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_pmc.h5
-rw-r--r--nuttx/arch/arm/src/sama5/sam_clockconfig.c47
-rwxr-xr-xnuttx/arch/arm/src/sama5/sam_ehci.c1626
-rw-r--r--nuttx/arch/arm/src/sama5/sam_ohci.c2
-rw-r--r--nuttx/arch/arm/src/sama5/sam_usbhost.h4
-rw-r--r--nuttx/configs/sama5d3x-ek/include/board_384mhz.h6
-rw-r--r--nuttx/configs/sama5d3x-ek/include/board_396mhz.h32
10 files changed, 1744 insertions, 18 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index d12359631..c980ecc48 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5421,3 +5421,7 @@
Add logic for management of device addresses. This logic does not
currently hook into into anything. It will someday be a part of the
NuttX USB hub implementation (2013-8-18).
+ * nuttx/arch/arm/src/sama5/sam_ehci.c and other files: Create a skeleton
+ environment for development of an EHCI driver. Not much in place yet
+ (2013-8-20).
+
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index 085d2090a..0bd3ac4d4 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/arch/arm/src/sama5/Kconfig
@@ -377,8 +377,38 @@ config SAMA5_EHCI
default n
---help---
Build support for the SAMA5 USB high speed Enhanced Host Controller
- Interface (OHCI).
+ Interface (EHCI).
+
+if SAMA5_EHCI
+
+config SAMA5_EHCI_NQHS
+ int "Number of Queue Head (QH) structures"
+ default 4
+ ---help---
+ Configurable number of Queue Head (QH) structures. The default is
+ one per Root hub port plus one for EP0.
+
+config SAMA5_EHCI_NQTDS
+ int "Number of Queue Element Transfer Descriptor (qTDs)"
+ default 4
+ ---help---
+ Configurable number of Queue Element Transfer Descriptor (qTDs).
+ The default is one per root hub plus three from EP0.
+
+config SAMA5_EHCI_BUFSIZE
+ int "Size of one request/descriptor buffer"
+ default 128
+ ---help---
+ The size of one request/descriptor buffer in bytes. The TD buffe
+ size must be an even number of 32-bit words and must be large enough
+ to hangle the largest transfer via a SETUP request.
+
+config SAMA5_EHCI_REGDEBUG
+ bool "Enable low-level EHCI register debug"
+ default n
+ depends on DEBUG
+endif # EHCI
endmenu # USB High Speed Host driver option
endif # SAMA5_UHPHS
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_ehci.h b/nuttx/arch/arm/src/sama5/chip/sam_ehci.h
index 9d1282e30..6d6c801de 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_ehci.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_ehci.h
@@ -69,11 +69,11 @@
/* Host Controller Capability Registers */
-#define HCCR ((struct ehci_hccr_s *)SAM_UHPEHCI_VSECTION
+#define HCCR ((struct ehci_hccr_s *)SAM_UHPEHCI_VSECTION)
/* Host Controller Operational Registers */
-#define HCOR ((volatile struct ehci_hcor_s *)(SAM_UHPEHCI_VSECTION + 0x10)
+#define HCOR ((volatile struct ehci_hcor_s *)(SAM_UHPEHCI_VSECTION + 0x10))
/****************************************************************************
* Public Types
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_pmc.h b/nuttx/arch/arm/src/sama5/chip/sam_pmc.h
index 92d1701a6..53ae2092c 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_pmc.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_pmc.h
@@ -179,9 +179,11 @@
#define PMC_CKGR_UCKR_UPLLEN (1 << 16) /* Bit 16: UTMI PLL Enable */
#define PMC_CKGR_UCKR_UPLLCOUNT_SHIFT (20) /* Bits 20-23: UTMI PLL Start-up Time */
#define PMC_CKGR_UCKR_UPLLCOUNT_MASK (15 << PMC_CKGR_UCKR_UPLLCOUNT_SHIFT)
+# define PMC_CKGR_UCKR_UPLLCOUNT(n) ((n) << PMC_CKGR_UCKR_UPLLCOUNT_SHIFT)
#define PMC_CKGR_UCKR_BIASEN (1 << 24) /* Bit 24: UTMI BIAS Enable */
#define PMC_CKGR_UCKR_BIASCOUNT_SHIFT (28) /* Bits 28-31: UTMI BIAS Start-up Time */
-#define PMC_CKGR_UCKR_BIASCOUNT_MASK (15 << PMC_CKGR_UCKR_BIASCOUNT_SHIFT) */
+#define PMC_CKGR_UCKR_BIASCOUNT_MASK (15 << PMC_CKGR_UCKR_BIASCOUNT_SHIFT)
+# define PMC_CKGR_UCKR_BIASCOUNT(n) ((n) << PMC_CKGR_UCKR_BIASCOUNT_SHIFT)
/* PMC Clock Generator Main Oscillator Register */
@@ -251,6 +253,7 @@
# define PMC_USB_USBS_UPLL PMC_USB_USBS
#define PMC_USB_USBDIV_SHIFT (8) /* Bits 8-11: Divider for USB Clock */
#define PMC_USB_USBDIV_MASK (15 << PMC_USB_USBDIV_SHIFT)
+# define PMC_USB_USBDIV(a) ((a) << PMC_USB_USBDIV_SHIFT)
/* Soft Modem Clock Register */
diff --git a/nuttx/arch/arm/src/sama5/sam_clockconfig.c b/nuttx/arch/arm/src/sama5/sam_clockconfig.c
index 0dc6884cd..42bdbe3e4 100644
--- a/nuttx/arch/arm/src/sama5/sam_clockconfig.c
+++ b/nuttx/arch/arm/src/sama5/sam_clockconfig.c
@@ -352,7 +352,15 @@ static inline void sam_usbclockconfig(void)
#endif
#ifdef CONFIG_SAMA5_EHCI
- /* For High-speed operations, the user has to perform the following:
+ uint32_t regval;
+
+ /* The USB Host High Speed requires a 480 MHz clock (UPLLCK) for the
+ * embedded High-speed transceivers. UPLLCK is the output of the 480 MHz
+ * UTMI PLL (UPLL). The source clock of the UTMI PLL is the Main OSC output:
+ * Either the 12MHz internal oscillator on a 12MHz crystal. The Main OSC
+ * must be 12MHz because the UPLL has a built-in 40x multiplier.
+ *
+ * For High-speed operations, the user has to perform the following:
*
* 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in
* PMC_PCER register.
@@ -371,19 +379,42 @@ static inline void sam_usbclockconfig(void)
* driver is initialized.
*/
-# warning Missing logic
-#endif
+ /* 2) Write CKGR_PLLCOUNT field in PMC_UCKR register. */
-#if 0 // #ifdef CONFIG_USBDEV
- uint32_t regval;
+ regval = PMC_CKGR_UCKR_UPLLCOUNT(BOARD_CKGR_UCKR_UPLLCOUNT);
+ putreg32(regval, SAM_PMC_CKGR_UCKR);
- /* Setup UTMI for USB and wait for LOCKU */
+ /* 3) Enable UPLL, bit AT91C_CKGR_UPLLEN in PMC_UCKR register. */
- regval = getreg32(SAM_PMC_CKGR_UCKR);
- regval |= (BOARD_CKGR_UCKR_UPLLCOUNT | PMC_CKGR_UCKR_UPLLEN);
+ regval |= PMC_CKGR_UCKR_UPLLEN;
putreg32(regval, SAM_PMC_CKGR_UCKR);
+ /* 4) Wait until UTMI_PLL is locked. LOCKU bit in PMC_SR register */
+
sam_pmcwait(PMC_INT_LOCKU);
+
+ /* 5) Enable BIAS, bit AT91C_CKGR_BIASEN in PMC_UCKR register. */
+
+ regval |= PMC_CKGR_UCKR_BIASCOUNT(BOARD_CKGR_UCKR_BIASCOUNT);
+ putreg32(regval, SAM_PMC_CKGR_UCKR);
+
+ regval |= PMC_CKGR_UCKR_BIASEN;
+ putreg32(regval, SAM_PMC_CKGR_UCKR);
+
+ /* 6) Select UPLLCK as Input clock of OHCI part, USBS bit in PMC_USB
+ * register.
+ */
+
+ regval = PMC_USB_USBS_UPLL;
+ putreg32(regval, SAM_PMC_USB);
+
+ /* 7) Program the OHCI clocks (UHP48M and UHP12M) with USBDIV field in
+ * PMC_USB register. USBDIV must be 9 (division by 10) if UPLLCK is
+ * selected.
+ */
+
+ regval |= PMC_USB_USBDIV(9);
+ putreg32(regval, SAM_PMC_USB);
#endif
}
diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c
new file mode 100755
index 000000000..cd8657af9
--- /dev/null
+++ b/nuttx/arch/arm/src/sama5/sam_ehci.c
@@ -0,0 +1,1626 @@
+/*******************************************************************************
+ * arch/arm/src/sama5/sam_ehci.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+#include <nuttx/usb/ehci.h>
+
+#include "up_arch.h"
+#include "sam_periphclks.h"
+#include "sam_usbhost.h"
+#include "chip/sam_ehci.h"
+
+#ifdef CONFIG_SAMA5_EHCI
+
+/*******************************************************************************
+ * Pre-processor Definitions
+ *******************************************************************************/
+/* Configuration ***************************************************************/
+
+/* Configurable number of Queue Head (QH) structures. The default is one per
+ * Root hub port plus one for EP0.
+ */
+
+#ifndef CONFIG_SAMA5_EHCI_NQHS
+# define CONFIG_SAMA5_EHCI_NQHS (SAM_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_SAMA5_EHCI_NQTDS
+# define CONFIG_SAMA5_EHCI_NQTDS (SAM_EHCI_NRHPORT + 3)
+#endif
+
+/* Configurable size of a request/descriptor buffers */
+
+#ifndef CONFIG_SAMA5_EHCI_BUFSIZE
+# define CONFIG_SAMA5_EHCI_BUFSIZE 128
+#endif
+
+/* Debug options */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_SAMA5_EHCI_REGDEBUG
+#endif
+
+/*******************************************************************************
+ * Private Types
+ *******************************************************************************/
+/* Internal representation of the EHCI Queue Head (QH) */
+
+struct sam_qh_s
+{
+ /* Fields visible to hardware */
+
+ struct ehci_qh_s hw; /* Hardware representation of the queue head */
+
+ /* Internal fields used by the EHCI driver */
+
+ uint32_t pad[16]; /* Padding to assure 32-byte alignment */
+};
+
+/* Internal representation of the EHCI Queue Element Transfer Descriptor (qTD) */
+
+struct sam_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 sam_list_s
+{
+ struct sam_list_s *flink; /* Link to next entry in the list */
+ /* Variable length entry data follows */
+};
+
+/* This structure describes one endpoint. */
+
+struct sam_epinfo_s
+{
+ uint8_t epno; /* Endpoint number */
+ uint8_t devaddr; /* Device address */
+ uint8_t xfrtype; /* See USB_EP_ATTR_XFER_* definitions in usb.h */
+ uint8_t speed; /* See USB_*_SPEED definitions in ehci.h */
+ uint8_t flags; /* See EPINFO_FLAG_* definitions above */
+ volatile bool wait; /* TRUE: Thread is waiting for transfer completion */
+ uint16_t maxpacket; /* Maximum packet size */
+ sem_t wsem; /* Semaphore used to wait for transfer completion */
+};
+
+/* This structure retains the state of one root hub port */
+
+struct sam_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 sam_rhport_s.
+ */
+
+ struct usbhost_driver_s drvr;
+
+ /* Root hub port status */
+
+ volatile bool connected; /* Connected to device */
+ volatile bool lowspeed; /* Low speed device attached */
+ uint8_t rhpndx; /* Root hub port index */
+
+ /* The bound device class driver */
+
+ struct usbhost_class_s *class;
+};
+
+/* This structure retains the overall state of the USB host controller */
+
+struct sam_ehci_s
+{
+ volatile bool rhwait; /* TRUE: Thread is waiting for root hub event */
+ sem_t exclsem; /* Support mutually exclusive access */
+ sem_t rhsem; /* Semaphore to wait for root hub events */
+
+ struct sam_epinfo_s ep0; /* Endpoint 0 */
+ struct sam_list_s *qhfree; /* List of free Queue Head (QH) structures */
+ struct sam_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */
+
+ /* Root hub ports */
+
+ struct sam_rhport_s rhport[SAM_EHCI_NRHPORT];
+};
+
+/*******************************************************************************
+ * Private Function Prototypes
+ *******************************************************************************/
+
+/* Register operations ********************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t sam_read16(volatile uint16_t *addr);
+static uint32_t sam_read32(volatile uint32_t *addr);
+static void sam_write16(uint16_t memval, volatile uint16_t *addr);
+static void sam_write32(uint32_t memval, volatile uint32_t *addr);
+#else
+static inline uint16_t sam_read16(volatile uint16_t *addr);
+static inline uint32_t sam_read32(volatile uint32_t *addr);
+static inline void sam_write16(uint16_t memval, volatile uint16_t *addr);
+static inline void sam_write32(uint32_t memval, volatile uint32_t *addr);
+#endif
+
+#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
+static void sam_printreg(volatile uint32_t *regaddr, uint32_t regval,
+ bool iswrite);
+static void sam_checkreg(volatile uint32_t *regaddr, uint32_t regval,
+ bool iswrite);
+static uint32_t sam_getreg(volatile uint32_t *regaddr);
+static void sam_putreg(uint32_t regval, volatile uint32_t *regaddr);
+#else
+static inline uint32_t sam_getreg(volatile uint32_t *regaddr);
+static inline void sam_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 void sam_takesem(sem_t *sem);
+#define sam_givesem(s) sem_post(s);
+
+/* Allocators ******************************************************************/
+
+static struct sam_qh_s *sam_qhalloc(void);
+static void sam_qhfree(struct sam_qh_s *qh);
+static struct sam_qtd_s *sam_qtdalloc(void);
+static void sam_qtdfree(struct sam_qtd_s *qtd);
+
+/* Interrupt Handling **********************************************************/
+
+static int sam_ehci_interrupt(int irq, FAR void *context);
+
+/* USB Host Controller Operations **********************************************/
+
+static int sam_wait(FAR struct usbhost_connection_s *conn,
+ FAR const bool *connected);
+static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx);
+
+static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
+ uint16_t maxpacketsize);
+static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
+ const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
+static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
+static int sam_alloc(FAR struct usbhost_driver_s *drvr,
+ FAR uint8_t **buffer, FAR size_t *maxlen);
+static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer);
+static int sam_ioalloc(FAR struct usbhost_driver_s *drvr,
+ FAR uint8_t **buffer, size_t buflen);
+static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer);
+static int sam_ctrlin(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer);
+static int sam_ctrlout(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer);
+static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
+ FAR uint8_t *buffer, size_t buflen);
+static void sam_disconnect(FAR struct usbhost_driver_s *drvr);
+
+/* Initialization **************************************************************/
+
+static int sam_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 sam_ehci_s g_ehci;
+
+/* This is the connection/enumeration interface */
+
+static struct usbhost_connection_s g_ehciconn;
+
+/* 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 sam_qh_s g_ghpool[CONFIG_SAMA5_EHCI_NQHS]
+ __attribute__ ((aligned(32)));
+
+/* Queue Element Transfer Descriptor (qTD) pool */
+
+static struct sam_qtd_s g_qtdpool[CONFIG_SAMA5_EHCI_NQTDS]
+ __attribute__ ((aligned(32)));
+
+/*******************************************************************************
+ * Private Functions
+ *******************************************************************************/
+/*******************************************************************************
+ * Register Operations
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_read16
+ *
+ * Description:
+ * Read 16-bit little endian data
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t sam_read16(volatile uint16_t *addr)
+{
+ uint8_t *addr8 = (uint8_t *)addr;
+
+ return (uint16_t)addr8[1] << 8 | (uint16_t)addr[0];
+}
+#else
+static inline uint16_t sam_read16(volatile uint16_t *addr)
+{
+ return *addr;
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_read32
+ *
+ * Description:
+ * Read 32-bit little endian data
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint32_t sam_read32(volatile uint32_t *addr)
+{
+ uint16_t *addr16 = (uint16_t *)addr;
+
+ return (uint32_t)sam_read16(&addr16[1]) << 16 |
+ (uint32_t)sam_read16(&addr16[0]);
+}
+#else
+static inline uint32_t sam_read32(volatile uint32_t *addr)
+{
+ return *addr;
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_write16
+ *
+ * Description:
+ * Write 16-bit little endian data
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static void sam_write16(uint16_t memval, volatile uint16_t *addr)
+{
+ volatile uint8_t *addr8 = (uint8_t *)addr;
+
+ addr8[0] = memval & 0xff;
+ addr8[1] = memval >> 8;
+}
+#else
+static inline void sam_write16(uint16_t memval, volatile uint16_t *addr)
+{
+ *addr = memval;
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_write32
+ *
+ * Description:
+ * Write 32-bit little endian data
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static void sam_write32(uint32_t memval, volatile uint32_t *addr)
+{
+ volatile uint16_t *addr16 = (uint16_t *)addr;
+
+ sam_write16(memval & 0xffff, &add16[0]);
+ sam_write16(memval >> 16, &add16[1]);
+}
+#else
+static inline void sam_write32(uint32_t memval, volatile uint32_t *addr)
+{
+ *addr = memval;
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_printreg
+ *
+ * Description:
+ * Print the contents of a SAMA5 EHCI register
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
+static void sam_printreg(volatile uint32_t *regaddr, uint32_t regval,
+ bool iswrite)
+{
+ lldbg("%p%s%08x\n", regaddr, iswrite ? "<-" : "->", regval);
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_checkreg
+ *
+ * Description:
+ * Check if it is time to output debug information for accesses to a SAMA5
+ * EHCI register
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
+static void sam_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 */
+
+ sam_printreg(prevaddr, preval, prevwrite);
+ }
+ else
+ {
+ /* No.. More than one. */
+
+ lldbg("[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 */
+
+ sam_printreg(regaddr, regval, iswrite);
+ }
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_getreg
+ *
+ * Description:
+ * Get the contents of an SAMA5 register
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
+static uint32_t sam_getreg(volatile uint32_t *regaddr)
+{
+ /* Read the value from the register */
+
+ uint32_t regval = *regaddr;
+
+ /* Check if we need to print this value */
+
+ sam_checkreg(regaddr, regval, false);
+ return regval;
+}
+#else
+static inline uint32_t sam_getreg(volatile uint32_t *regaddr)
+{
+ return *regaddr;
+}
+#endif
+
+/*******************************************************************************
+ * Name: sam_putreg
+ *
+ * Description:
+ * Set the contents of an SAMA5 register to a value
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
+static void sam_putreg(uint32_t regval, volatile uint32_t *regaddr)
+{
+ /* Check if we need to print this value */
+
+ sam_checkreg(regaddr, regval, true);
+
+ /* Write the value */
+
+ *regaddr = regval;
+}
+#else
+static inline void sam_putreg(uint32_t regval, volatile uint32_t *regaddr)
+{
+ *regaval = 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 = sam_getreg(&HCOR->usbsts);
+ if ((regval & EHCI_INT_SYSERROR) != 0)
+ {
+ udbg("ERROR: System error: 0x%08X", 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;
+}
+
+/*******************************************************************************
+ * Semaphores
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_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 void sam_takesem(sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occr here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/*******************************************************************************
+ * Allocators
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_qhalloc
+ *
+ * Description:
+ * Allocate a Queue Head (QH) structure by removing it from the free list
+ *
+ *******************************************************************************/
+
+static struct sam_qh_s *sam_qhalloc(void)
+{
+ struct sam_qh_s *qh;
+
+ /* Remove the QH structure from the freelist */
+
+ qh = (struct sam_qh_s *)g_ehci.qhfree;
+ if (qh)
+ {
+ g_ehci.qhfree = ((struct sam_list_s *)qh)->flink;
+ memset(qh, 0, sizeof(struct sam_qh_s));
+ }
+
+ return qh;
+}
+
+/*******************************************************************************
+ * Name: sam_qhfree
+ *
+ * Description:
+ * Free a Queue Head (QH) structure by returning it to the free list
+ *
+ *******************************************************************************/
+
+static void sam_qhfree(struct sam_qh_s *qh)
+{
+ struct sam_list_s *entry = (struct sam_list_s *)qh;
+
+ /* Put the QH structure back into the free list */
+
+ entry->flink = g_ehci.qhfree;
+ g_ehci.qhfree = entry;
+}
+
+/*******************************************************************************
+ * Name: sam_qtdalloc
+ *
+ * Description:
+ * Allocate a Queue Element Transfer Descriptor (qTD) by removing it from the
+ * free list
+ *
+ *******************************************************************************/
+
+static struct sam_qtd_s *sam_qtdalloc(void)
+{
+ struct sam_qtd_s *qtd;
+
+ /* Remove the qTD from the freelist */
+
+ qtd = (struct sam_qtd_s *)g_ehci.qtdfree;
+ if (qtd)
+ {
+ g_ehci.qtdfree = ((struct sam_list_s *)qtd)->flink;
+ memset(qtd, 0, sizeof(struct sam_list_s));
+ }
+
+ return qtd;
+}
+
+/*******************************************************************************
+ * Name: sam_edfree
+ *
+ * Description:
+ * Free a Queue Element Transfer Descriptor (qTD) by returning it to the free
+ * list
+ *
+ *******************************************************************************/
+
+static void sam_qtdfree(struct sam_qtd_s *qtd)
+{
+ struct sam_list_s *entry = (struct sam_list_s *)qtd;
+
+ /* Put the qTD back into the free list */
+
+ entry->flink = g_ehci.qtdfree;
+ g_ehci.qtdfree = entry;
+}
+
+/*******************************************************************************
+ * EHCI Interrupt Handling
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: sam_ehci_interrupt
+ *
+ * Description:
+ * OHCI interrupt handler
+ *
+ *******************************************************************************/
+
+static int sam_ehci_interrupt(int irq, FAR void *context)
+{
+#warning "Missing logic"
+ return OK;
+}
+
+/*******************************************************************************
+ * USB Host Controller Operations
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_wait
+ *
+ * Description:
+ * Wait for a device to be connected or disconnected to/from a root hub port.
+ *
+ * Input Parameters:
+ * conn - The USB host connection instance obtained as a parameter from the call to
+ * the USB driver initialization logic.
+ * connected - A pointer to an array of 3 boolean values corresponding to
+ * root hubs 1, 2, and 3. For each boolean value: TRUE: Wait for a device
+ * to be connected on the root hub; FALSE: wait for device to be
+ * disconnected from the root hub.
+ *
+ * Returned Values:
+ * And index [0, 1, or 2} corresponding to the root hub port number {1, 2,
+ * or 3} is returned when a device is connected or disconnected. This
+ * function will not return until either (1) a device is connected or
+ * disconnected to/from any root 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 sam_wait(FAR struct usbhost_connection_s *conn,
+ FAR const bool *connected)
+{
+ irqstate_t flags;
+ int rhpndx;
+
+ /* Loop until a change in the connection state changes on one of the root hub
+ * ports or until an error occurs.
+ */
+
+ flags = irqsave();
+ for (;;)
+ {
+ /* Check for a change in the connection state on any root hub port */
+
+ for (rhpndx = 0; rhpndx < SAM_EHCI_NRHPORT; rhpndx++)
+ {
+ /* Has the connection state changed on the RH port? */
+
+ if (g_ehci.rhport[rhpndx].connected != connected[rhpndx])
+ {
+ /* Yes.. Return the RH port number */
+
+ irqrestore(flags);
+
+ udbg("RHPort%d connected: %s\n",
+ rhpndx + 1, g_ehci.rhport[rhpndx].connected ? "YES" : "NO");
+
+ return rhpndx;
+ }
+ }
+
+ /* No changes on any port. Wait for a connection/disconnection event
+ * and check again
+ */
+
+ g_ehci.rhwait = true;
+ sam_takesem(&g_ehci.rhsem);
+ }
+}
+
+/*******************************************************************************
+ * Name: sam_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 configdesc() 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.
+ * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n.
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * - Only a single class bound to a single device is supported.
+ * - Called from a single thread so no mutual exclusion is required.
+ * - Never called from an interrupt handler.
+ *
+ *******************************************************************************/
+
+static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
+{
+ struct sam_rhport_s *rhport;
+
+ DEBUGASSERT(rhpndx >= 0 && rhpndx < SAM_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 */
+
+ udbg("Not connected\n");
+ return -ENODEV;
+ }
+
+ /* Add EP0 to the control list */
+#warning Missing logic
+
+ /* USB 2.0 spec says at least 50ms delay before port reset */
+
+ up_mdelay(100);
+
+ /* Put the root hub port in reset (the SAMA5 supports three downstream ports) */
+#warning Missing logic
+
+ /* Wait for the port reset to complete */
+#warning Missing logic
+
+ /* Release RH port 1 from reset and wait a bit */
+#warning Missing logic
+
+ up_mdelay(200);
+
+ /* Let the common usbhost_enumerate do all of the real work. Note that the
+ * FunctionAddress (USB address) is set to the root hub port number for now.
+ */
+
+ uvdbg("Enumerate the device\n");
+ return usbhost_enumerate(&g_ehci.rhport[rhpndx].drvr, rhpndx+1, &rhport->class);
+}
+
+/************************************************************************************
+ * Name: sam_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.
+ * maxpacketsize - The maximum number of bytes that can be sent to or
+ * received from the endpoint in a single data packet
+ *
+ * Returned Values:
+ * 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 sam_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
+ uint16_t maxpacketsize)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+
+ DEBUGASSERT(rhport &&
+ funcaddr >= 0 && funcaddr <= SAM_EHCI_NRHPORT &&
+ maxpacketsize < 2048);
+
+ /* We must have exclusive access to the EHCI data structures. */
+
+ sam_takesem(&g_ehci.exclsem);
+
+#warning Missing logic
+
+ sam_givesem(&g_ehci.exclsem);
+ return -ENOSYS;
+}
+
+/************************************************************************************
+ * Name: sam_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 desciptor.
+ *
+ * Returned Values:
+ * 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 sam_epalloc(FAR struct usbhost_driver_s *drvr,
+ const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+ struct sam_epinfo_s *epinfo;
+ int ret = -ENOMEM;
+
+ /* Sanity check. NOTE that this method should only be called if a device is
+ * connected (because we need a valid low speed indication).
+ */
+
+ DEBUGASSERT(rhport && epdesc && ep && rhport->connected);
+
+ /* Allocate a container for the endpoint data */
+
+ epinfo = (struct sam_epinfo_s *)kzalloc(sizeof(struct sam_epinfo_s));
+ if (!epinfo)
+ {
+ udbg("ERROR: Failed to allocate EP info structure\n");
+ goto errout;
+ }
+
+ /* Initialize the endpoint container */
+
+ sem_init(&epinfo->wsem, 0, 0);
+
+ /* We must have exclusive access to the EHCI data structures. */
+
+ sam_takesem(&g_ehci.exclsem);
+
+#warning Missing logic
+
+ /* Success.. return an opaque reference to the endpoint list container */
+
+ *ep = (usbhost_ep_t)epinfo;
+ sam_givesem(&g_ehci.exclsem);
+ return OK;
+
+errout_with_semaphore:
+ sam_givesem(&g_ehci.exclsem);
+ kfree(epinfo);
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: sam_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 Values:
+ * 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 sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+ struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep;
+ int ret;
+
+ DEBUGASSERT(rhport && epinfo);
+
+ /* There should not be any pending, transfers */
+#warning Missing logic
+
+ /* We must have exclusive access to the EHCI data structures. */
+
+ sam_takesem(&g_ehci.exclsem);
+
+#warning Missing logic
+
+ /* And free the container */
+
+ sem_destroy(&epinfo->wsem);
+ kfree(epinfo);
+ sam_givesem(&g_ehci.exclsem);
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: sam_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 kmalloc.
+ *
+ * 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 Values:
+ * 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 sam_alloc(FAR struct usbhost_driver_s *drvr,
+ FAR uint8_t **buffer, FAR size_t *maxlen)
+{
+ int ret = -ENOMEM;
+ DEBUGASSERT(drvr && buffer && maxlen);
+
+ /* There is no special requirements for transfer/descriptor buffers. */
+
+ *buffer = (FAR uint8_t *)kmalloc(CONFIG_SAMA5_EHCI_BUFSIZE);
+ if (*buffer)
+ {
+ *maxlen = CONFIG_SAMA5_EHCI_BUFSIZE;
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: sam_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 kfree().
+ *
+ * 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 Values:
+ * 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 sam_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 */
+
+ kfree(buffer);
+ return OK;
+}
+
+/************************************************************************************
+ * Name: sam_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 kumalloc.
+ *
+ * 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 Values:
+ * 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 sam_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 they might need to be user
+ * accessible (depending on how the class driver implements its buffering).
+ */
+
+ *buffer = (FAR uint8_t *)kumalloc(buflen);
+ return *buffer ? OK : -ENOMEM;
+}
+
+/************************************************************************************
+ * Name: sam_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 kufree().
+ *
+ * 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 Values:
+ * 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 sam_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 */
+
+ kufree(buffer);
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: sam_ctrlin and sam_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.
+ * 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 Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * - Only a single class bound to a single device is supported.
+ * - Called from a single thread so no mutual exclusion is required.
+ * - Never called from an interrupt handler.
+ *
+ *******************************************************************************/
+
+static int sam_ctrlin(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usb_ctrlreq_s *req,
+ FAR uint8_t *buffer)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+ uint16_t len;
+ int ret;
+
+ DEBUGASSERT(rhport && req);
+
+ len = sam_read16((uint16_t*)req->len);
+ uvdbg("RHPort%d type: %02x req: %02x value: %02x%02x index: %02x%02x len: %04x\n",
+ rhport->rhpndx + 1, req->type, req->req, req->value[1], req->value[0],
+ req->index[1], req->index[0], len);
+
+ /* We must have exclusive access to the EHCI hardware and data structures. */
+
+ sam_takesem(&g_ehci.exclsem);
+
+ /* Now perform the transfer */
+#warning Missing logic
+ ret = -ENOSYS;
+
+ sam_givesem(&g_ehci.exclsem);
+ return ret;
+}
+
+static int sam_ctrlout(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usb_ctrlreq_s *req,
+ FAR const uint8_t *buffer)
+{
+ /* sam_ctrlin can handle both directions. We just need to work around the
+ * differences in the function signatures.
+ */
+
+ return sam_ctrlin(drvr, req, (uint8_t *)buffer);
+}
+
+/*******************************************************************************
+ * Name: sam_transfer
+ *
+ * Description:
+ * Process a request to handle a transfer descriptor. This method will
+ * enqueue the transfer request and return immediately. Only one transfer may be
+ * queued;.
+ *
+ * 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 Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating 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:
+ * - Only a single class bound to a single device is supported.
+ * - Called from a single thread so no mutual exclusion is required.
+ * - Never called from an interrupt handler.
+ *
+ *******************************************************************************/
+
+static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
+ FAR uint8_t *buffer, size_t buflen)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+ struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep;
+ int ret;
+
+ DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
+
+ /* We must have exclusive access to the EHCI hardware and data structures. */
+
+ sam_takesem(&g_ehci.exclsem);
+
+ /* Perform the transfer */
+#warning Missing logic
+ ret = -ENOSYS;
+
+ sam_givesem(&g_ehci.exclsem);
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: sam_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.
+ *
+ * Returned Values:
+ * None
+ *
+ * Assumptions:
+ * - Only a single class bound to a single device is supported.
+ * - Never called from an interrupt handler.
+ *
+ *******************************************************************************/
+
+static void sam_disconnect(FAR struct usbhost_driver_s *drvr)
+{
+ struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
+ DEBUGASSERT(rhport);
+
+ /* Remove the disconnected port */
+#warning Missing logic
+
+ /* Unbind the class */
+
+ rhport->class = NULL;
+}
+
+/*******************************************************************************
+ * Initialization
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_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 initializaation of the EHCI.
+ *
+ *******************************************************************************/
+
+static int sam_reset(void)
+{
+ uint32_t regval;
+ unsigned int timeout;
+
+ /* "... 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."
+ */
+
+ sam_putreg(0, &HCOR->usbcmd);
+ timeout = 0;
+ do
+ {
+ /* Wait one microsecond and update the timeout counter */
+
+ up_udelay(1);
+ timeout++;
+
+ /* Get the current valud 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 = sam_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)
+ {
+ udbg("ERROR: Timed out waiting for HCHalted. USBSTS: %08X", regval);
+ return -ETIMEDOUT;
+ }
+
+ /* Now we can set the HCReset bit in the USBCMD register to initiate the reset */
+
+ regval = sam_getreg(&HCOR->usbcmd);
+ regval |= EHCI_USBCMD_HCRESET;
+ sam_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 valud 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 = sam_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;
+}
+
+/*******************************************************************************
+ * Global Functions
+ *******************************************************************************/
+/*******************************************************************************
+ * Name: sam_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 intialized. 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 *sam_ehci_initialize(int controller)
+{
+ irqstate_t flags;
+ uint32_t regval;
+ int ret;
+ int i;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(controller == 0);
+
+ /* SAMA5 Configuration *******************************************************/
+ /* For High-speed operations, the user has to perform the following:
+ *
+ * 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in
+ * PMC_PCER register.
+ * 2) Write CKGR_PLLCOUNT field in PMC_UCKR register.
+ * 3) Enable UPLL, bit AT91C_CKGR_UPLLEN in PMC_UCKR register.
+ * 4) Wait until UTMI_PLL is locked. LOCKU bit in PMC_SR register
+ * 5) Enable BIAS, bit AT91C_CKGR_BIASEN in PMC_UCKR register.
+ * 6) Select UPLLCK as Input clock of OHCI part, USBS bit in PMC_USB
+ * register.
+ * 7) Program the OHCI clocks (UHP48M and UHP12M) with USBDIV field in
+ * PMC_USB register. USBDIV must be 9 (division by 10) if UPLLCK is
+ * selected.
+ * 8) Enable OHCI clocks, UHP bit in PMC_SCER register.
+ *
+ * Steps 1 and 8 are performed here. Steps 2 through 7 were are performed
+ * by sam_clockconfig() earlier in the boot sequence.
+ */
+
+ /* Enable UHP peripheral clocking */
+
+ flags = irqsave();
+ sam_uhphs_enableclk();
+
+ /* Enable OHCI clocks */
+
+ regval = sam_getreg((volatile uint32_t *)SAM_PMC_SCER);
+ regval |= PMC_UHP;
+ sam_putreg(regval, (volatile uint32_t *)SAM_PMC_SCER);
+ irqrestore(flags);
+
+ /* Note that no pin pinconfiguration is required. All USB HS pins have
+ * dedicated function
+ */
+
+ /* Software Configuration ****************************************************/
+
+ uvdbg("Initializing EHCI Stack\n");
+
+ /* Initialize the EHCI state data structure */
+
+ sem_init(&g_ehci.exclsem, 0, 1);
+ sem_init(&g_ehci.rhsem, 0, 0);
+
+ /* Initialize EP0 */
+
+ sem_init(&g_ehci.ep0.wsem, 0, 1);
+
+ /* Initialize the root hub port structures */
+
+ for (i = 0; i < SAM_EHCI_NRHPORT; i++)
+ {
+ struct sam_rhport_s *rhport = &g_ehci.rhport[i];
+ rhport->rhpndx = i;
+
+ /* Initialize the device operations */
+
+ rhport->drvr.ep0configure = sam_ep0configure;
+ rhport->drvr.epalloc = sam_epalloc;
+ rhport->drvr.epfree = sam_epfree;
+ rhport->drvr.alloc = sam_alloc;
+ rhport->drvr.free = sam_free;
+ rhport->drvr.ioalloc = sam_ioalloc;
+ rhport->drvr.iofree = sam_iofree;
+ rhport->drvr.ctrlin = sam_ctrlin;
+ rhport->drvr.ctrlout = sam_ctrlout;
+ rhport->drvr.transfer = sam_transfer;
+ rhport->drvr.disconnect = sam_disconnect;
+ }
+
+ /* Initialize the list of free Queue Head (QH) structures */
+
+ for (i = 0; i < CONFIG_SAMA5_EHCI_NQHS; i++)
+ {
+ /* Put the QH structure in a free list */
+
+ sam_qhfree(&g_ghpool[i]);
+ }
+
+ /* Initialize the list of free Queue Head (QH) structures */
+
+ for (i = 0; i < CONFIG_SAMA5_EHCI_NQTDS; i++)
+ {
+ /* Put the TD in a free list */
+
+ sam_qtdfree(&g_qtdpool[i]);
+ }
+
+ /* EHCI Hardware Configuration ***********************************************/
+
+ /* Reset the EHCI hardware */
+
+ ret = sam_reset();
+ if (ret < 0)
+ {
+ udbg("ERROR: sam_reset failed: %d\n", ret);
+ return NULL;
+ }
+
+#warning Missing logic
+
+ /* Interrupt Configuration ***************************************************/
+
+ /* Clear pending interrupts */
+#warning Missing logic
+
+ /* Enable EHCI interrupts */
+#warning Missing logic
+
+ /* Attach USB host controller interrupt handler */
+
+ if (irq_attach(SAM_IRQ_UHPHS, sam_ehci_interrupt) != 0)
+ {
+ udbg("ERROR: Failed to attach IRQ\n");
+ return NULL;
+ }
+
+ /* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG
+ * mode.
+ */
+
+ sam_usbhost_vbusdrive(SAM_EHCI_IFACE, true);
+ up_mdelay(50);
+
+ /* 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 < SAM_EHCI_NRHPORT; i++)
+ {
+#warning Missing logic
+ }
+
+ /* Enable interrupts at the interrupt controller */
+
+ up_enable_irq(SAM_IRQ_UHPHS); /* enable USB interrupt */
+ uvdbg("USB OHCI Initialized\n");
+
+ /* Initialize and return the connection interface */
+
+ g_ehciconn.wait = sam_wait;
+ g_ehciconn.enumerate = sam_enumerate;
+ return &g_ehciconn;
+}
+
+#endif /* CONFIG_SAMA5_EHCI */
diff --git a/nuttx/arch/arm/src/sama5/sam_ohci.c b/nuttx/arch/arm/src/sama5/sam_ohci.c
index a8a10ebfa..ee2160fd1 100644
--- a/nuttx/arch/arm/src/sama5/sam_ohci.c
+++ b/nuttx/arch/arm/src/sama5/sam_ohci.c
@@ -192,7 +192,7 @@ struct sam_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 structsam_usbhost_s.
+ * to struct sam_rhport_s.
*/
struct usbhost_driver_s drvr;
diff --git a/nuttx/arch/arm/src/sama5/sam_usbhost.h b/nuttx/arch/arm/src/sama5/sam_usbhost.h
index deb428ce2..47bd7fe15 100644
--- a/nuttx/arch/arm/src/sama5/sam_usbhost.h
+++ b/nuttx/arch/arm/src/sama5/sam_usbhost.h
@@ -137,8 +137,8 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller);
*******************************************************************************/
#ifdef CONFIG_SAMA5_EHCI
-struct usbhost_driver_s;
-FAR struct usbhost_driver_s *sam_ehci_initialize(int controller);
+struct usbhost_connection_s;
+FAR struct usbhost_connection_s *sam_ehci_initialize(int controller);
#endif
/***********************************************************************************
diff --git a/nuttx/configs/sama5d3x-ek/include/board_384mhz.h b/nuttx/configs/sama5d3x-ek/include/board_384mhz.h
index 74494cbb2..8f94da2ed 100644
--- a/nuttx/configs/sama5d3x-ek/include/board_384mhz.h
+++ b/nuttx/configs/sama5d3x-ek/include/board_384mhz.h
@@ -100,6 +100,7 @@
#define BOARD_PMC_MCKR_PLLADIV PMC_MCKR_PLLADIV2
#define BOARD_PMC_MCKR_MDIV PMC_MCKR_MDIV_PCKDIV3
+#ifdef CONFIG_SAMA5_OHCI
/* For OHCI Full-speed operations, the user has to perform the following:
*
* 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in PMC_PCER
@@ -131,8 +132,9 @@
* frame rate. I cannot explain the factor of 2 difference.
*/
-#define BOARD_OHCI_INPUT PMC_USB_USBS_PLLA
-#define BOARD_OHCI_DIVIDER (7)
+# define BOARD_OHCI_INPUT PMC_USB_USBS_PLLA
+# define BOARD_OHCI_DIVIDER (7)
+#endif
/* Resulting frequencies */
diff --git a/nuttx/configs/sama5d3x-ek/include/board_396mhz.h b/nuttx/configs/sama5d3x-ek/include/board_396mhz.h
index f98cbac86..ac3ae724e 100644
--- a/nuttx/configs/sama5d3x-ek/include/board_396mhz.h
+++ b/nuttx/configs/sama5d3x-ek/include/board_396mhz.h
@@ -98,7 +98,7 @@
#define BOARD_PMC_MCKR_PRES PMC_MCKR_PRES_DIV1
#define BOARD_PMC_MCKR_PLLADIV PMC_MCKR_PLLADIV2
#define BOARD_PMC_MCKR_MDIV PMC_MCKR_MDIV_PCKDIV3
-786
+
/* Resulting frequencies */
#define BOARD_MAINOSC_FREQUENCY (12000000) /* MAINOSC: 12MHz crystal on-board */
@@ -106,6 +106,36 @@
#define BOARD_PCK_FREQUENCY (396000000) /* CPU: PLLACK / 2 / 1 */
#define BOARD_MCK_FREQUENCY (132000000) /* MCK: PLLACK / 2 / 1 / 3 */
+#ifdef CONFIG_SAMA5_EHCI
+/* The USB Host High Speed requires a 480 MHz clock (UPLLCK) for the embedded
+ * High-speed transceivers. UPLLCK is the output of the 480 MHz UTMI PLL
+ * (UPLL). The source clock of the UTMI PLL is the Main OSC output: Either
+ * the 12MHz internal RC oscillator on a an external 12MHz crystal. The
+ * Main OSC must be 12MHz because the UPLL has a built-in 40x multiplier.
+ *
+ * For High-speed operations, the user has to perform the following:
+ *
+ * 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in
+ * PMC_PCER register.
+ * 2) Write CKGR_PLLCOUNT field in PMC_UCKR register.
+ * 3) Enable UPLL, bit AT91C_CKGR_UPLLEN in PMC_UCKR register.
+ * 4) Wait until UTMI_PLL is locked. LOCKU bit in PMC_SR register
+ * 5) Enable BIAS, bit AT91C_CKGR_BIASEN in PMC_UCKR register.
+ * 6) Select UPLLCK as Input clock of OHCI part, USBS bit in PMC_USB
+ * register.
+ * 7) Program the OHCI clocks (UHP48M and UHP12M) with USBDIV field in
+ * PMC_USB register. USBDIV must be 9 (division by 10) if UPLLCK is
+ * selected.
+ * 8) Enable OHCI clocks, UHP bit in PMC_SCER register.
+ *
+ * Steps 2 through 7 performed here. 1 and 8 are performed in the EHCI
+ * driver is initialized.
+ */
+
+# define BOARD_CKGR_UCKR_UPLLCOUNT (15) /* Maximum value */
+# define BOARD_CKGR_UCKR_BIASCOUNT (15) /* Maximum value */
+#endif
+
/* HSMCI clocking
*
* Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK)