Skip to content
Snippets Groups Projects
lpc214x_usbdev.c 98.2 KiB
Newer Older
/*******************************************************************************
 * arch/arm/src/lpc214x/lpc214x_usbdev.c
 *
 *   Copyright (C) 2008 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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 <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/usb.h>
#include <nuttx/usbdev.h>
#include <nuttx/usbdev_trace.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "lpc214x_usbdev.h"
#include "lpc214x_pll.h"
#include "lpc214x_power.h"

/*******************************************************************************
 * Definitions
 *******************************************************************************/

/* Configuration ***************************************************************/

#ifndef CONFIG_USBDEV_EP0_MAXSIZE
#  define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif

#ifndef  CONFIG_USBDEV_MAXPOWER
#  define CONFIG_USBDEV_MAXPOWER 100  /* mA */
#endif

#define USB_SLOW_INT USBDEV_DEVINT_EPSLOW
#define USB_DEVSTATUS_INT USBDEV_DEVINT_DEVSTAT

#ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT
#  define USB_FAST_INT USBDEV_DEVINT_EPFAST
#else
#  define USB_FAST_INT 0
#endif

/* Extremely detailed register debug that you would normally never want
 * enabled.
 */

#undef CONFIG_LPC214X_USBDEV_REGDEBUG

/* Enable reading SOF from interrupt handler vs. simply reading on demand.  Probably
 * a bad idea... Unless there is some issue with sampling the SOF from hardware
 * asynchronously.
 */

#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
#  define USB_FRAME_INT USBDEV_DEVINT_FRAME
#else
#  define USB_FRAME_INT 0
#endif

#ifdef CONFIG_DEBUG
#  define USB_ERROR_INT USBDEV_DEVINT_EPRINT
#else
#  define USB_ERROR_INT 0
#endif

/* Number of DMA descriptors */

#ifdef CONFIG_LPC214X_USBDEV_DMA
# error DMA SUPPORT NOT YET FULLY IMPLEMENTED
#  ifndef CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 8
#  elif CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS > 30
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 30
#  endif
#endif

/* Debug ***********************************************************************/

/* Trace error codes */

patacongo's avatar
patacongo committed
#define LPC214X_TRACEERR_ALLOCFAIL            0x0001
#define LPC214X_TRACEERR_BADCLEARFEATURE      0x0002
#define LPC214X_TRACEERR_BADDEVGETSTATUS      0x0003
#define LPC214X_TRACEERR_BADEPNO              0x0004
#define LPC214X_TRACEERR_BADEPGETSTATUS       0x0005
#define LPC214X_TRACEERR_BADEPTYPE            0x0006
#define LPC214X_TRACEERR_BADGETCONFIG         0x0007
#define LPC214X_TRACEERR_BADGETSETDESC        0x0008
#define LPC214X_TRACEERR_BADGETSTATUS         0x0009
#define LPC214X_TRACEERR_BADSETADDRESS        0x000a
#define LPC214X_TRACEERR_BADSETCONFIG         0x000b
#define LPC214X_TRACEERR_BADSETFEATURE        0x000c
#define LPC214X_TRACEERR_BINDFAILED           0x000d
patacongo's avatar
patacongo committed
#define LPC214X_TRACEERR_DISPATCHSTALL        0x000e
#define LPC214X_TRACEERR_DMABUSY              0x000f
#define LPC214X_TRACEERR_DRIVER               0x0010
#define LPC214X_TRACEERR_DRIVERREGISTERED     0x0011
#define LPC214X_TRACEERR_EP0INSTALLED         0x0012
#define LPC214X_TRACEERR_EP0OUTSTALLED        0x0013
#define LPC214X_TRACEERR_EP0SETUPSTALLED      0x0014
#define LPC214X_TRACEERR_EPINNULLPACKET       0x0015
#define LPC214X_TRACEERR_EPOUTNULLPACKET      0x0016
#define LPC214X_TRACEERR_EPREAD               0x0017
#define LPC214X_TRACEERR_INVALIDCMD           0x0018
#define LPC214X_TRACEERR_INVALIDCTRLREQ       0x0019
#define LPC214X_TRACEERR_INVALIDPARMS         0x001a
#define LPC214X_TRACEERR_IRQREGISTRATION      0x001b
#define LPC214X_TRACEERR_NODMADESC            0x001c
#define LPC214X_TRACEERR_NOEP                 0x001d
#define LPC214X_TRACEERR_NOTCONFIGURED        0x001e
#define LPC214X_TRACEERR_REQABORTED           0x001f

/* Trace interrupt codes */

patacongo's avatar
patacongo committed
#define LPC214X_TRACEINTID_USB                0x0001
#define LPC214X_TRACEINTID_CLEARFEATURE       0x0002
#define LPC214X_TRACEINTID_CONNECTCHG         0x0003
#define LPC214X_TRACEINTID_CONNECTED          0x0004
#define LPC214X_TRACEINTID_DEVGETSTATUS       0x0005
#define LPC214X_TRACEINTID_DEVRESET           0x0006
#define LPC214X_TRACEINTID_DEVSTAT            0x0007
#define LPC214X_TRACEINTID_DISCONNECTED       0x0008
#define LPC214X_TRACEINTID_DISPATCH           0x0009
#define LPC214X_TRACEINTID_EP0IN              0x000a
#define LPC214X_TRACEINTID_EP0OUT             0x000b
#define LPC214X_TRACEINTID_EP0SETUP           0x000c
#define LPC214X_TRACEINTID_EPDMA              0x000d
#define LPC214X_TRACEINTID_EPFAST             0x000e
#define LPC214X_TRACEINTID_EPGETSTATUS        0x000f
#define LPC214X_TRACEINTID_EPIN               0x0010
#define LPC214X_TRACEINTID_EPINQEMPTY         0x0011
#define LPC214X_TRACEINTID_EP0INSETADDRESS    0x0012
#define LPC214X_TRACEINTID_EPOUT              0x0013
#define LPC214X_TRACEINTID_EPOUTQEMPTY        0x0014
#define LPC214X_TRACEINTID_EP0SETUPSETADDRESS 0x0015
#define LPC214X_TRACEINTID_EPRINT             0x0016
#define LPC214X_TRACEINTID_EPSLOW             0x0017
#define LPC214X_TRACEINTID_FRAME              0x0018
#define LPC214X_TRACEINTID_GETCONFIG          0x0019
#define LPC214X_TRACEINTID_GETSETDESC         0x001a
#define LPC214X_TRACEINTID_GETSETIF           0x001b
#define LPC214X_TRACEINTID_GETSTATUS          0x001c
#define LPC214X_TRACEINTID_IFGETSTATUS        0x001d
#define LPC214X_TRACEINTID_SETCONFIG          0x001e
#define LPC214X_TRACEINTID_SETFEATURE         0x001f
#define LPC214X_TRACEINTID_SUSPENDCHG         0x0020
#define LPC214X_TRACEINTID_SYNCHFRAME         0x0021

/* Hardware interface **********************************************************/

/* Macros for testing the device status response */

#define DEVSTATUS_CONNECT(s)    (((s)&USBDEV_DEVSTATUS_CONNECT)!=0)
#define DEVSTATUS_CONNCHG(s)    (((s)&USBDEV_DEVSTATUS_CONNCHG)!=0)
#define DEVSTATUS_SUSPEND(s)    (((s)&USBDEV_DEVSTATUS_SUSPEND)!=0)
#define DEVSTATUS_SUSPCHG(s)    (((s)&USBDEV_DEVSTATUS_SUSPCHG)!=0)
#define DEVSTATUS_RESET(s)      (((s)&USBDEV_DEVSTATUS_RESET)!=0)

/* If this bit is set in the lpc214x_epread response, it means that the
 * recevied packet was overwritten by a later setup packet (ep0 only).
 */

#define LPC214X_READOVERRUN_BIT (0x80000000)
#define LPC214X_READOVERRUN(s)  (((s) & LPC214X_READOVERRUN_BIT) != 0)

/* USB RAM  ********************************************************************
 *
 * UBS_UDCA is is list of 32 pointers to DMA desciptors located at the
 * beginning of USB RAM.  Each pointer points to a DMA descriptor with
 * assocated DMA buffer.
 */

#define USB_UDCA           (uint32*)LPC214X_USBDEV_RAMBASE)
#define USB_USCASIZE       (LPC214X_NPHYSENDPOINTS*sizeof(uint32))

/* Each descriptor must be aligned to a 128 address boundary */

#define USB_DDALIGNDOWN(a) ((a)&~0x7f)
#define USB_DDALIGNUP(a)   USB_DDALIGNDOWN((a)+0x7f)

#define USB_DDSIZE USB_DDALIGNDOWN((LPC214X_USBDEV_RAMSIZE-USB_USCASIZE)/CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS)
#define USB_DDESC  ((struct lpc214x_dmadesc_s*)(LPC214X_USBDEV_RAMBASE+USB_USCASIZE))

#ifdef CONFIG_USBDEV_ISOCHRONOUS
#  define USB_DDESCSIZE (5*sizeof(uint32))
#else
#  define USB_DDESCSIZE (4*sizeof(uint32))
#endif

/* Endpoints ******************************************************************/

/* Number of endpoints */

#define LPC214X_NLOGENDPOINTS        (16)          /* ep0-15 */
#define LPC214X_NPHYSENDPOINTS       (32)          /* x2 for IN and OUT */

/* Odd physical endpoint numbers are IN; even are out */

#define LPC214X_EPPHYIN(epphy)       (((epphy)&1)!=0)
#define LPC214X_EPPHYOUT(epphy)      (((epphy)&1)==0)

#define LPC214X_EPPHYIN2LOG(epphy)   (((ubyte)(epphy)>>1)|USB_DIR_IN)
#define LPC214X_EPPHYOUT2LOG(epphy)  (((ubyte)(epphy)>>1)|USB_DIR_OUT)

/* Each endpoint has somewhat different characteristics */

#define LPC214X_EPALLSET             (0xffffffff)  /* All endpoints */
#define LPC214X_EPOUTSET             (0x55555555)  /* Even phy endpoint numbers are OUT EPs */
#define LPC214X_EPINSET              (0xaaaaaaaa)  /* Odd endpoint numbers are IN EPs */
#define LPC214X_EPCTRLSET            (0x00000003)  /* EP0 IN/OUT are control endpoints */
#define LPC214X_EPINTRSET            (0x0c30c30c)  /* Interrupt endpoints */
#define LPC214X_EPBULKSET            (0xf0c30c30)  /* Bulk endpoints */
#define LPC214X_EPISOCSET            (0x030c30c0)  /* Isochronous endpoints */
#define LPC214X_EPDBLBUFFER          (0xf3cf3cf0)  /* Double buffered endpoints */
#define LPC214X_EP0MAXPACKET         (64)          /* EP0 max packet size (1-64) */
#define LPC214X_BULKMAXPACKET        (64)          /* Bulk endpoint max packet (8/16/32/64) */
#define LPC214X_INTRMAXPACKET        (64)          /* Interrupt endpoint max packet (1 to 64) */
#define LPC214X_ISOCMAXPACKET        (512)         /* Acutally 1..1023 */
/* EP0 status.  EP0 transfers occur in a number of different contexts.  A
 * simple state machine is required to handle the various transfer complete
 * interrupt responses.  The following values are the various states:
 */
                                                   /*** INTERRUPT CAUSE ***/
#define LPC214X_EP0REQUEST           (0)           /* Normal request handling */
#define LPC214X_EP0STATUSIN          (1)           /* Status sent */
#define LPC214X_EP0STATUSOUT         (2)           /* Status received */
#define LPC214X_EP0SHORTWRITE        (3)           /* Short data sent with no request */
#define LPC214X_EP0SHORTWRSENT       (4)           /* Short data write complete */
#define LPC214X_EP0SETADDRESS        (5)           /* Set address received */
#define LPC214X_EP0WRITEREQUEST      (6)           /* EP0 write request sent */
/* Request queue operations ****************************************************/

#define lpc214x_rqempty(ep)          ((ep)->head == NULL)
#define lpc214x_rqpeek(ep)           ((ep)->head)

/*******************************************************************************
 * Private Types
 *******************************************************************************/

/* A container for a request so that the request make be retained in a list */

struct lpc214x_req_s
{
  struct usbdev_req_s    req;           /* Standard USB request */
  struct lpc214x_req_s  *flink;         /* Supports a singly linked list */
};

/* This is the internal representation of an endpoint */

struct lpc214x_ep_s
{
  /* Common endpoint fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct usbdev_ep_s
   * to struct lpc214x_ep_s.
   */

  struct usbdev_ep_s      ep;            /* Standard endpoint structure */

  /* LPC214X-specific fields */

  struct lpc214x_usbdev_s *dev;          /* Reference to private driver data */
  struct lpc214x_req_s    *head;         /* Request list for this endpoint */
  struct lpc214x_req_s    *tail;
  ubyte                   epphy;         /* Physical EP address */
  ubyte                   stalled:1;     /* 1: Endpoint is stalled */
  ubyte                   halted:1;      /* 1: Endpoint feature halted */
  ubyte                   txbusy:1;      /* 1: TX endpoint FIFO full */
  ubyte                   txnullpkt:1;   /* Null packet needed at end of transfer */
};

/* This represents a DMA descriptor */

#ifdef CONFIG_LPC214X_USBDEV_DMA
struct lpc214x_dmadesc_s
{
  uint32                  nextdesc;      /* Address of the next DMA descripto in RAM */
  uint32                  config;        /* Misc. bit encoded configuration information */
  uint32                  start;         /* DMA start address */
  uint32                  status;        /* Misc. bit encoded status inforamation */
#ifdef CONFIG_USBDEV_ISOCHRONOUS
  uint32                  size;          /* Isochronous packet size address */
#endif
  ubyte                   buffer[USB_DDSIZE-USB_DDESCSIZE];
};
#endif

/* This structure retains the state of the USB device controller */

struct lpc214x_usbdev_s
{
  /* Common device fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct usbdev_s
   * to structlpc214x_usbdev_s.
   */

  struct usbdev_s         usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* LPC214X-specific fields */

  ubyte                   devstatus;     /* Last response to device status command */
  ubyte                   ep0state;      /* State of certain EP0 operations */
  ubyte                   paddr;         /* Address assigned by SETADDRESS */
  ubyte                   stalled:1;     /* 1: Protocol stalled */
  ubyte                   selfpowered:1; /* 1: Device is self powered */
  ubyte                   paddrset:1;    /* 1: Peripheral addr has been set */
  ubyte                   attached:1;    /* 1: Host attached */
  ubyte                   rxpending:1;   /* 1: RX pending */
  uint32                  softprio;      /* Bitset of high priority interrupts */
  uint32                  epavail;       /* Bitset of available endpoints */
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
  uint32                  sof;           /* Last start-of-frame */
#endif

  /* Allocated DMA descriptor */

#ifdef CONFIG_LPC214X_USBDEV_DMA
  struct lpc214x_dmadesc_s *dmadesc;
#endif

  /* The endpoint list */

  struct lpc214x_ep_s     eplist[LPC214X_NPHYSENDPOINTS];
};

/*******************************************************************************
 * Private Function Prototypes
 *******************************************************************************/

/* Register operations ********************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr);
static void lpc214x_putreg(uint32 val, uint32 addr);
#else
# define lpc214x_getreg(addr)     getreg32(addr)
# define lpc214x_putreg(val,addr) putreg32(val,addr)
#endif

/* Command operations **********************************************************/

static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data);

/* Request queue operations ****************************************************/

static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep);
static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
              FAR struct lpc214x_req_s *req);

/* Low level data transfers and request operations *****************************/

static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes);
static int  lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes);
static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
              struct lpc214x_req_s *privreq, sint16 result);
static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result);
static int  lpc214x_wrrequest(struct lpc214x_ep_s *privep);
static int  lpc214x_rdrequest(struct lpc214x_ep_s *privep);
static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep);

/* Interrupt handling **********************************************************/
static struct lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv,
              uint16 eplog);
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio,
              uint32 packetsize);
static ubyte lpc214x_epclrinterrupt(ubyte epphy);
static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static inline void lpc214x_dmareset(uint32 enable);
#endif
static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv);
static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
patacongo's avatar
patacongo committed
              const struct usb_ctrlreq_s *ctrl);
static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv);
static inline void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv);
static inline void lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv);
static int lpc214x_usbinterrupt(int irq, FAR void *context);

#ifdef CONFIG_LPC214X_USBDEV_DMA
static int  lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, ubyte epphy,
              uint32 epmaxsize, uint32 nbytes, uint32 *isocpacket,
              boolean isochronous);
static void lpc214x_dmarestart(ubyte epphy, uint32 descndx);
static void lpc214x_dmadisable(ubyte epphy);
#endif /* CONFIG_LPC214X_USBDEV_DMA */

/* Endpoint operations *********************************************************/

static int  lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
              const struct usb_epdesc_s *desc, boolean last);
static int  lpc214x_epdisable(FAR struct usbdev_ep_s *ep);
static FAR struct usbdev_req_s *lpc214x_epallocreq(FAR struct usbdev_ep_s *ep);
static void lpc214x_epfreereq(FAR struct usbdev_ep_s *ep,
              FAR struct usbdev_req_s *);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static FAR void *lpc214x_epallocbuffer(FAR struct usbdev_ep_s *ep,
              uint16 nbytes);
static void lpc214x_epfreebuffer(FAR struct usbdev_ep_s *ep, void *buf);
#endif
static int  lpc214x_epsubmit(FAR struct usbdev_ep_s *ep,
              struct usbdev_req_s *req);
static int  lpc214x_epcancel(FAR struct usbdev_ep_s *ep,
              struct usbdev_req_s *req);
static int  lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume);

/* USB device controller operations ********************************************/

static FAR struct usbdev_ep_s *lcp214x_allocep(FAR struct usbdev_s *dev,
              ubyte epno, boolean in, ubyte eptype);
static void lpc214x_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
static int  lpc214x_getframe(struct usbdev_s *dev);
static int  lpc214x_wakeup(struct usbdev_s *dev);
static int  lpc214x_selfpowered(struct usbdev_s *dev, boolean selfpowered);
static int  lpc214x_pullup(struct usbdev_s *dev, boolean enable);

/*******************************************************************************
 * Private Data
 *******************************************************************************/

/* Since there is only a single USB interface, all status information can be
 * be simply retained in a single global instance.
 */

static struct lpc214x_usbdev_s g_usbdev;

static const struct usbdev_epops_s g_epops =
{
  .configure   = lpc214x_epconfigure,
  .disable     = lpc214x_epdisable,
  .allocreq    = lpc214x_epallocreq,
  .freereq     = lpc214x_epfreereq,
#ifdef CONFIG_LPC214X_USBDEV_DMA
  .allocbuffer = lpc214x_epallocbuffer,
  .freebuffer  = lpc214x_epfreebuffer,
#endif
  .submit      = lpc214x_epsubmit,
  .cancel      = lpc214x_epcancel,
  .stall       = lpc214x_epstall,
};

static const struct usbdev_ops_s g_devops =
{
  .allocep     = lcp214x_allocep,
  .freeep      = lpc214x_freeep,
  .getframe    = lpc214x_getframe,
  .wakeup      = lpc214x_wakeup,
  .selfpowered = lpc214x_selfpowered,
  .pullup      = lpc214x_pullup,
};

/*******************************************************************************
 * Public Data
 *******************************************************************************/

/*******************************************************************************
 * Private Functions
 *******************************************************************************/

/*******************************************************************************
 * Name: lpc214x_getreg
 *
 * Description:
 *   Get the contents of an LPC214x register
 *
 *******************************************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr)
{
  static uint32 prevaddr = 0;
  static uint32 preval = 0;
  static uint32 count = 0;

  /* Read the value from the register */

  uint32 val = getreg32(addr);

  /* Is this the same value that we read from the same registe last time?  Are
   * we polling the register?  If so, suppress some of the output.
   */

  if (addr == prevaddr || val == preval)
    {
      if (count == 0xffffffff || ++count > 3)
        {
           if (count == 4)
             {
               lldbg("...\n");
             }
          return val;
        }
    }

  /* No this is a new address or value */

  else
    {
       /* Did we print "..." for the previous value? */

       if (count > 3)
         {
           /* Yes.. then show how many times the value repeated */

           lldbg("[repeats %d more times]\n", count-3);
         }

       /* Save the new address, value, and count */

       prevaddr = addr;
       preval   = val;
       count    = 1;
    }

  /* Show the register value read */

  lldbg("%08x->%08x\n", addr, val);
  return val;
}
#endif

/*******************************************************************************
 * Name: lpc214x_putreg
 *
 * Description:
 *   Set the contents of an LPC214x register to a value
 *
 *******************************************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void lpc214x_putreg(uint32 val, uint32 addr)
{
  /* Show the register value being written */

  lldbg("%08x<-%08x\n", addr, val);

  /* Write the value */

  putreg32(val, addr);
}
#endif

/*******************************************************************************
 * Name: lpc214x_usbcmd
 *
 * Description:
 *   Transmit commands to the USB engine
 *
 *******************************************************************************/

static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data)
{
  irqstate_t flags;
  uint32 tmp = 0;

  /* Disable interrupt and clear CDFULL and CCEMPTY interrupt status */

  flags = irqsave();
  lpc214x_putreg(USBDEV_DEVINT_CDFULL|USBDEV_DEVINT_CCEMTY, LPC214X_USBDEV_DEVINTCLR);
  /* Load command + WR in command code register */
  lpc214x_putreg(((cmd & 0xff) << 16) + CMD_USB_CMDWR, LPC214X_USBDEV_CMDCODE);
  /* Wait until the command register is empty (CCEMPTY != 0, command is accepted) */
  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);

  /* Clear command register empty (CCEMPTY) interrupt */

  lpc214x_putreg(USBDEV_DEVINT_CCEMTY, LPC214X_USBDEV_DEVINTCLR);
  /* Determine next phase of the command */
      /* Write operations (1 byte of data) */
    case CMD_USB_DEV_SETADDRESS:
    case CMD_USB_DEV_CONFIG:
    case CMD_USB_DEV_SETMODE:
    case CMD_USB_DEV_SETSTATUS:
      {
        /* Send data + WR and wait for CCEMPTY */

        lpc214x_putreg((data << 16) + CMD_USB_DATAWR, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);
      }
      /* 16 bit read operations */

    case CMD_USB_DEV_READFRAMENO:
    case CMD_USB_DEV_READTESTREG:
      {
        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Clear CDFULL and read LS data */

        lpc214x_putreg(USBDEV_DEVINT_CDFULL, LPC214X_USBDEV_DEVINTCLR);
        tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);

        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Read MS data */

        tmp |= lpc214x_getreg(LPC214X_USBDEV_CMDDATA) << 8;
      }
     /* 8-bit read operations */

    case CMD_USB_DEV_GETSTATUS:
    case CMD_USB_DEV_GETERRORCODE:
    case CMD_USB_DEV_READERRORSTATUS:
    case CMD_USB_EP_CLRBUFFER:
      {
        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Read data */

        tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
      }
patacongo's avatar
patacongo committed
      /* No data transfer */

    case CMD_USB_EP_VALIDATEBUFFER:
      break;

    default:
      switch (cmd & 0x1e0)
        {
        case CMD_USB_EP_SELECT:
        case CMD_USB_EP_SELECTCLEAR:
          {
            /* Send command code + RD and wait for CDFULL */

            lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
            while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

            /* Read data */

            tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
          }
          break;

        case CMD_USB_EP_SETSTATUS:
          {
            /* Send data + RD and wait for CCEMPTY */

            lpc214x_putreg((data << 16) + CMD_USB_DATAWR, LPC214X_USBDEV_CMDCODE);
            while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);
          }
          break;
        default:
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCMD), 0);
          break;
        }
      break;
    }

  /* Restore the interrupt flags */

  irqrestore(flags);
  return tmp;
}

/*******************************************************************************
 * Name: lpc214x_rqdequeue
 *
 * Description:
 *   Remove a request from an endpoint request queue
 *
 *******************************************************************************/

static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep)
{
  FAR struct lpc214x_req_s *ret = privep->head;

  if (ret)
    {
      privep->head = ret->flink;
      if (!privep->head)
        {
          privep->tail = NULL;
        }

      ret->flink = NULL;
    }

  return ret;
}

/*******************************************************************************
 * Name: lpc214x_rqenqueue
 *
 * Description:
 *   Add a request from an endpoint request queue
 *
 *******************************************************************************/

static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
                              FAR struct lpc214x_req_s *req)
{
  req->flink = NULL;
  if (!privep->head)
    {
      privep->head = req;
      privep->tail = req;
    }
  else
    {
      privep->tail->flink = req;
      privep->tail        = req;
    }
}

/*******************************************************************************
 * Name: lpc214x_epwrite
 *
 * Description:
 *   Endpoint write (IN)
 *
 *******************************************************************************/

static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes)
{
patacongo's avatar
patacongo committed
  uint32 value;
  boolean aligned = (((uint32)data & 3) == 0);

patacongo's avatar
patacongo committed
  /* Set the write enable bit for this physical EP address. Bits 2-5 are
   * the logical endpoint number (0-15)
   */
  lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | LPC214X_USBCTRL_WREN,
                 LPC214X_USBDEV_CTRL);

  /* Set the transmit packet length (nbytes must be less than 2048) */

patacongo's avatar
patacongo committed
  lpc214x_putreg(nbytes, LPC214X_USBDEV_TXPLEN);

  /* Transfer the packet data */

  do
    {
      /* Zero length packets are a special case */

      if (nbytes)
        {
patacongo's avatar
patacongo committed
          if (aligned)
            {
              value = *(uint32*)data;
            }
          else
            {
              value =  (uint32)data[0]        | ((uint32)data[1] << 8) |
                      ((uint32)data[2] << 16) | ((uint32)data[3] << 24);
            }

          lpc214x_putreg(value, LPC214X_USBDEV_TXDATA);
          data += 4;
        }
      else
        {
          /* Zero length packet */

          lpc214x_putreg(0, LPC214X_USBDEV_TXDATA);
  while ((lpc214x_getreg(LPC214X_USBDEV_CTRL) & LPC214X_USBCTRL_WREN) != 0);
  lpc214x_putreg(0, LPC214X_USBDEV_CTRL);
  (void)lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
  (void)lpc214x_usbcmd(CMD_USB_EP_VALIDATEBUFFER, 0);
}

/*******************************************************************************
 * Name: lpc214x_epread
 *
 * Description:
 *   Endpoint read (OUT)
 *
 *******************************************************************************/

static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes)
{
  uint32 pktlen;
  uint32 result;
  uint32 value;
patacongo's avatar
patacongo committed
  ubyte  aligned = 0;

  /* If data is NULL, then we are being asked to read but discard the data.
   * For most cases, the resulting buffer will be aligned and we will be
   * able to do faster 32-bit transfers.
   */

  if (data)
    {
       if (((uint32)data & 3) == 0)
        {
          aligned = 1;
        }
       else
        {
          aligned = 2;
        }
    }
patacongo's avatar
patacongo committed
  /* Set the read enable bit for this physical EP address.  Bits 2-5 are
   * the logical endpoint number (0-15).
   */
  lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | LPC214X_USBCTRL_RDEN,
                 LPC214X_USBDEV_CTRL);

  /* Wait for packet buffer ready for reading */

  while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_PKTRDY) == 0);
patacongo's avatar
patacongo committed
  /* Get the number of bytes of data to be read */
  pktlen = lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_PKTLENGTH;

  /* Read data from input buffer while read data is valid (DV) */

  while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_DV) != 0)
      value = lpc214x_getreg(LPC214X_USBDEV_RXDATA);
patacongo's avatar
patacongo committed
      if (aligned == 1)
        {
          *(uint32*)data = value;
          data += 4;
        }
      else if (aligned == 2)
patacongo's avatar
patacongo committed
          *data++ = (ubyte)value;
          *data++ = (ubyte)(value >> 8);
          *data++ = (ubyte)(value >> 16);
          *data++ = (ubyte)(value >> 24);
  lpc214x_putreg(0, LPC214X_USBDEV_CTRL);
  (void)lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
  result = lpc214x_usbcmd(CMD_USB_EP_CLRBUFFER, 0);

  /* The packet overrun bit in the clear buffer response is applicable only
   * on EP0 transfers.  If set it means that the recevied packet was overwritten
   * by a later setup packet.
   */

  if (epphy == LPC214X_EP0_OUT && (result & CMD_USB_CLRBUFFER_PO) != 0)
    {
      /* Pass this information in bit 31 */

      pktlen |= LPC214X_READOVERRUN_BIT;
    }
  return pktlen;
}

/*******************************************************************************
 * Name: lpc214x_abortrequest
 *
 * Description:
 *   Discard a request
 *
 *******************************************************************************/

static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
                                        struct lpc214x_req_s *privreq,
                                        sint16 result)
{
  usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_REQABORTED), (uint16)privep->epphy);

  /* Save the result in the request structure */

  privreq->req.result = result;

  /* Callback to the request completion handler */

  privreq->req.callback(&privep->ep, &privreq->req);
}

/*******************************************************************************
 * Name: lpc214x_reqcomplete
 *
 * Description:
 *   Handle termination of the request at the head of the endpoint request queue.
 *
 *******************************************************************************/

static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result)
{
  struct lpc214x_req_s *privreq;
  int stalled = privep->stalled;
  irqstate_t flags;

  /* Remove the completed request at the head of the endpoint request list */
  privreq = lpc214x_rqdequeue(privep);
  irqrestore(flags);

  if (privreq)
    {
      /* If endpoint 0, temporarily reflect the state of protocol stalled
       * in the callback.
       */

      if (privep->epphy == LPC214X_EP0_IN)
          privep->stalled = privep->dev->stalled;
        }

      /* Save the result in the request structure */

      privreq->req.result = result;

      /* Callback to the request completion handler */

      privreq->flink = NULL;
      privreq->req.callback(&privep->ep, &privreq->req);

      /* Restore the stalled indication */

      privep->stalled = stalled;
    }
}

/*******************************************************************************
 * Name: lpc214x_wrrequest
 *
 * Description:
 *   Send from the next queued write request
 *
 *******************************************************************************/