Skip to content
Snippets Groups Projects
lpc214x_usbdev.c 98.2 KiB
Newer Older
  ubyte arg = USBDEV_DEVSTATUS_SUSPEND;
  irqstate_t flags;

  usbtrace(TRACE_DEVWAKEUP, (uint16)g_usbdev.devstatus);

  flags = irqsave();
  if (DEVSTATUS_CONNECT(g_usbdev.devstatus))
    {
      arg |= USBDEV_DEVSTATUS_CONNECT;
    }

  lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, arg);
  irqrestore(flags);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_selfpowered
 *
 * Description:
 *   Sets/clears the device selfpowered feature 
 *
 *******************************************************************************/

static int lpc214x_selfpowered(struct usbdev_s *dev, boolean selfpowered)
{
  FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;

  usbtrace(TRACE_DEVSELFPOWERED, (uint16)selfpowered);

#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -ENODEV;
    }
#endif

  priv->selfpowered = selfpowered;
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_pullup
 *
 * Description:
 *   Software-controlled connect to/disconnect from USB host
 *
 *******************************************************************************/

static int lpc214x_pullup(struct usbdev_s *dev, boolean enable)
{
  usbtrace(TRACE_DEVPULLUP, (uint16)enable);

  /* The USBDEV_DEVSTATUS_CONNECT bit in the CMD_USB_DEV_SETSTATUS command
   * controls the LPC214x SoftConnect_N output pin that is used for SoftConnect.
   */

  lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, (enable ? USBDEV_DEVSTATUS_CONNECT : 0));
  return OK;
}

/*******************************************************************************
 * Public Functions
 *******************************************************************************/

/*******************************************************************************
 * Name: up_usbinitialize
 *
 * Description:
 *   Initialize USB hardware.
 *
 * Assumptions:
 * - This function is called very early in the initialization sequence
 * - PLL and GIO pin initialization is not performed here but should been in
 *   the low-level  boot logic:  PLL1 must be configured for operation at 48MHz
 *   and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect
 *   LED.
 *
 *******************************************************************************/

void up_usbinitialize(void)
{
  struct lpc214x_usbdev_s *priv = &g_usbdev;
  uint32 reg;
  int i;

  usbtrace(TRACE_DEVINIT, 0);

  /* Disable USB interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_INTST);

patacongo's avatar
patacongo committed
  /* Initialize the device state structure */

  memset(priv, 0, sizeof(struct lpc214x_usbdev_s));
  priv->usbdev.ops = &g_devops;
  priv->usbdev.ep0 = &priv->eplist[LPC214X_EP0_IN].ep;
  priv->epavail    = LPC214X_EPALLSET;
  /* Initialize the endpoint list */

  for (i = 0; i < LPC214X_NPHYSENDPOINTS; i++)
    {
      uint32 bit = 1 << i;

      /* Set endpoint operations, reference to driver structure (not
       * really necessary because there is only one controller), and
       * the physical endpoint number (which is just the index to the
       * endpoint).
       */
      priv->eplist[i].ep.ops       = &g_epops;
      priv->eplist[i].dev          = priv;

      /* The index, i, is the physical endpoint address;  Map this
       * to a logical endpoint address usable by the class driver.
       */

      priv->eplist[i].epphy        = i;
      if (LPC214X_EPPHYIN(i))
        {
          priv->eplist[i].ep.eplog = LPC214X_EPPHYIN2LOG(i);
        }
      else
        {
          priv->eplist[i].ep.eplog = LPC214X_EPPHYOUT2LOG(i);
        }

      /* The maximum packet size may depend on the type of endpoint */

      if ((LPC214X_EPCTRLSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_EP0MAXPACKET;
        }
      else if ((LPC214X_EPINTRSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_INTRMAXPACKET;
        }
      else if ((LPC214X_EPBULKSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_BULKMAXPACKET;
        }
      else /* if ((LPC214X_EPISOCSET & bit) != 0) */
        {
          priv->eplist[i].ep.maxpacket = LPC214X_ISOCMAXPACKET;
        }
    }

  /* Turn on USB power and clocking */

  reg = lpc214x_getreg(LPC214X_PCON_PCONP);
  reg |= LPC214X_PCONP_PCUSB;
  lpc214x_putreg(reg, LPC214X_PCON_PCONP);
  /* Attach USB controller interrupt handler */

  if (irq_attach(LPC214X_USB_IRQ, lpc214x_usbinterrupt) != 0)
    {
patacongo's avatar
patacongo committed
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_IRQREGISTRATION),
               (uint16)LPC214X_USB_IRQ);
  /* Enable USB inerrupts at the controller -- but do not disable
   * the ARM interrupt until the device is bound to the class
   * driver
   */
  lpc214x_putreg(USBDEV_INTST_ENUSBINTS, LPC214X_USBDEV_INTST);

  /* Disconnect device */

  lpc214x_pullup(&priv->usbdev, FALSE);

  /* Enable EP0 for OUT (host-to-device) */

  lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN|0);
  lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN|0);

  /* Reset/Re-initialize the USB hardware */

  lpc214x_usbreset(priv);

  /* Init Device state structure */

  priv->devstatus = lpc214x_usbcmd(CMD_USB_DEV_GETSTATUS, 0);
  return;

errout:
  up_usbuninitialize();
}

/*******************************************************************************
 * Name: up_usbuninitialize
 *******************************************************************************/

void up_usbuninitialize(void)
{
  struct lpc214x_usbdev_s *priv = &g_usbdev;
  uint32 reg;
  irqstate_t flags;

  usbtrace(TRACE_DEVUNINIT, 0);
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVERREGISTERED), 0);
      usbdev_unregister(priv->driver);
    }

  /* Disconnect device */

  flags = irqsave();
  lpc214x_pullup(&priv->usbdev, FALSE);
  priv->usbdev.speed = USB_SPEED_UNKNOWN;
  lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);

  /* Disable and detach IRQs */

  up_disable_irq(LPC214X_USB_IRQ);
  irq_detach(LPC214X_USB_IRQ);

  /* Turn off USB power and clocking */

  reg = lpc214x_getreg(LPC214X_PCON_PCONP);
  reg &= ~LPC214X_PCONP_PCUSB;
  lpc214x_putreg(reg, LPC214X_PCON_PCONP);
  irqrestore(flags);
}

/*******************************************************************************
 * Name: usbdev_register
 *
 * Description:
 *   Register a USB device class driver. The class driver's bind() method will be
 *   called to bind it to a USB device driver.
 *
 *******************************************************************************/

int usbdev_register(struct usbdevclass_driver_s *driver)
{
  int ret;

  usbtrace(TRACE_DEVREGISTER, 0);
patacongo's avatar
patacongo committed

#ifdef CONFIG_DEBUG
patacongo's avatar
patacongo committed
  if (!driver || !driver->ops->bind || !driver->ops->unbind ||
      !driver->ops->disconnect || !driver->ops->setup)
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
patacongo's avatar
patacongo committed
      return -EINVAL;
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVER), 0);
      return -EBUSY;
    }
#endif

  /* First hook up the driver */

  g_usbdev.driver = driver;

  /* Then bind the class driver */

  ret = CLASS_BIND(driver, &g_usbdev.usbdev);
  if (ret)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BINDFAILED), (uint16)-ret);
      g_usbdev.driver = NULL;
    }
  else
    {
      /* Enable USB controller interrupts */

      up_enable_irq(LPC214X_USB_IRQ);
    }
  return ret;
}

/*******************************************************************************
 * Name: usbdev_unregister
 *
 * Description:
 *   Un-register usbdev class driver.If the USB device is connected to a USB host,
 *   it will first disconnect().  The driver is also requested to unbind() and clean
 *   up any device state, before this procedure finally returns.
 *
 *******************************************************************************/

int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
  usbtrace(TRACE_DEVUNREGISTER, 0);

#ifdef CONFIG_DEBUG
  if (driver != g_usbdev.driver)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Unbind the class driver */

  CLASS_UNBIND(driver, &g_usbdev.usbdev);

  /* Disable USB controller interrupts */

  up_disable_irq(LPC214X_USB_IRQ);

  /* Unhook the driver */

  g_usbdev.driver = NULL;
  return OK;
}