Skip to content
Snippets Groups Projects
lpc214x_usbdev.c 95.3 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_BADREQUEST           0x000a
#define LPC214X_TRACEERR_BADSETADDRESS        0x000b
#define LPC214X_TRACEERR_BADSETCONFIG         0x000c
#define LPC214X_TRACEERR_BADSETFEATURE        0x000d
#define LPC214X_TRACEERR_BINDFAILED           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)

/* Mapping to more traditional endpoint numbers */

#define LPC214X_EP_LOG2PHYOUT(ep)    ((ep)&0x0f)<<1))
#define LPC214X_EP_LOG2PHYIN(ep)     (LPC214X_EP_OUT(ep)|0x01)
#define LPC214X_EP_LOG2PHY(ep)       ((((ep)&0x0f)<<1)|(((ep)&0x80)>>7))

/* 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;     /* Endpoint is halted */
  ubyte                   halted:1;      /* Endpoint feature halted */
  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
{
Loading
Loading full blame...