Skip to content
Snippets Groups Projects
lpc17_ethernet.c 72.4 KiB
Newer Older
              if (priv->lp_dev.d_len > 0)
                {
                  lpc17_response(priv);
                }
            }
          else
            {
              /* Unrecognized... drop it. */
              EMAC_STAT(priv, rx_dropped);
            }
      /* Bump up the consumer index and resample the producer index (which
       * might also have gotten bumped up by the hardware).
       */
      if (++considx >= CONFIG_NET_NRXDESC)
       {
         /* Wrap back to index zero */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
        }

      lpc17_putreg(considx, LPC17_ETH_RXCONSIDX);
      prodidx = lpc17_getreg(LPC17_ETH_RXPRODIDX) & ETH_RXPRODIDX_MASK;
    }
}

/****************************************************************************
 * Function: lpc17_txdone
 *
 * Description:
 *   An interrupt was received indicating that the last TX packet(s) is done
 *
 * Parameters:
 *   priv  - Reference to the driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
patacongo's avatar
patacongo committed
 *   Global interrupts are disabled by interrupt handling logic.
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
static void lpc17_txdone(struct lpc17_driver_s *priv)
patacongo's avatar
patacongo committed
  /* Cancel the pending Tx timeout */

  wd_cancel(priv->lp_txtimeout);

patacongo's avatar
patacongo committed
  /* Disable further Tx interrupts.  Tx interrupts may be re-enabled again
   * depending upon the result of the poll.
   */
patacongo's avatar
patacongo committed
  priv->lp_inten &= ~ETH_TXINTS;
  lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);

  /* Verify that the hardware is ready to send another packet.  Since a Tx
   * just completed, this must be the case.
   */

  DEBUGASSERT(lpc17_txdesc(priv) == OK);

  /* Check if there is a pending Tx transfer that was scheduled by Rx handling
   * while the Tx logic was busy.  If so, processing that pending Tx now.
   */

  if (priv->lp_txpending)
    {
      /* Clear the pending condition, send the packet, and restore Rx interrupts */

      priv->lp_txpending = false;
      EMAC_STAT(priv, tx_unpend);

      lpc17_transmit(priv);

      priv->lp_inten    |= ETH_RXINTS;
      lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
    }

  /* Otherwise poll uIP for new XMIT data */

  else
    {
      (void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
    }
}

/****************************************************************************
 * Function: lpc17_interrupt
 *
 * Description:
 *   Hardware interrupt handler
 *
 * Parameters:
 *   irq     - Number of the IRQ that generated the interrupt
 *   context - Interrupt register state save info (architecture-specific)
 *
 * Returned Value:
 *   OK on success
 *
 * Assumptions:
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
static int lpc17_interrupt(int irq, void *context)
patacongo's avatar
patacongo committed
  register struct lpc17_driver_s *priv;
  uint32_t status;
patacongo's avatar
patacongo committed
#if CONFIG_LPC17_NINTERFACES > 1
# error "A mechanism to associate and interface with an IRQ is needed"
#else
  priv = &g_ethdrvr[0];
#endif

  /* Get the interrupt status (zero means no interrupts pending). */

  status = lpc17_getreg(LPC17_ETH_INTST);
  if (status != 0)
    {
patacongo's avatar
patacongo committed
      /* Clear all pending interrupts */

      lpc17_putreg(status, LPC17_ETH_INTCLR);
      
      /* Handle each pending interrupt **************************************/
      /* Check for Wake-Up on Lan *******************************************/
#ifdef CONFIG_NET_WOL
      if ((status & ETH_INT_WKUP) != 0)
          EMAC_STAT(priv, wol);
#         warning "Missing logic"
      else
#endif
      /* Fatal Errors *******************************************************/
      /* RX OVERRUN -- Fatal overrun error in the receive queue. The fatal
       * interrupt should be resolved by a Rx soft-reset. The bit is not
       * set when there is a nonfatal overrun error.
       *
       * TX UNDERRUN -- Interrupt set on a fatal underrun error in the
       * transmit queue. The fatal interrupt should be resolved by a Tx
       * soft-reset. The bit is not set when there is a nonfatal underrun
       * error.
       */
      if ((status & (ETH_INT_RXOVR|ETH_INT_TXUNR)) != 0)
          if ((status & ETH_INT_RXOVR) != 0)
            {
patacongo's avatar
patacongo committed
              nlldbg("RX Overrun. status: %08x\n", status);
              EMAC_STAT(priv, rx_ovrerrors);
          if ((status & ETH_INT_TXUNR) != 0)
            {
patacongo's avatar
patacongo committed
              nlldbg("TX Underrun. status: %08x\n", status);
              EMAC_STAT(priv, tx_underrun);
            }
           /* ifup() will reset the EMAC and bring it back up */
           (void)lpc17_ifup(&priv->lp_dev);
      else
        {      
          /* Check for receive events ***************************************/
          /* RX ERROR -- Triggered on receive errors: AlignmentError,
patacongo's avatar
patacongo committed
           * RangeError, LengthError, SymbolError, CRCError or NoDescriptor
           * or Overrun.  NOTE:  (1) We will still need to call lpc17_rxdone
           * on RX errors to bump the considx over the bad packet.  (2) The
           * DMA engine reports bogus length errors, making this a pretty
           * useless check anyway.
          if ((status & ETH_INT_RXERR) != 0)
            {
patacongo's avatar
patacongo committed
              nlldbg("RX Error. status: %08x\n", status);
              EMAC_STAT(priv, rx_errors);
            }
patacongo's avatar
patacongo committed
          /* RX FINISHED -- Triggered when all receive descriptors have
           * been processed i.e. on the transition to the situation
           * where ProduceIndex == ConsumeIndex.
           */
patacongo's avatar
patacongo committed
          if ((status & ETH_INT_RXFIN) != 0)
            {
              EMAC_STAT(priv, rx_finished);
              DEBUGASSERT(lpc17_getreg(LPC17_ETH_RXPRODIDX) == lpc17_getreg(LPC17_ETH_RXCONSIDX));
            }
patacongo's avatar
patacongo committed
          /* RX DONE -- Triggered when a receive descriptor has been
           * processed while the Interrupt bit in the Control field of
           * the descriptor was set.
           */
patacongo's avatar
patacongo committed
          if ((status & ETH_INT_RXDONE) != 0)
            {
              EMAC_STAT(priv, rx_done);
patacongo's avatar
patacongo committed
              /* We have received at least one new incoming packet. */
patacongo's avatar
patacongo committed
              lpc17_rxdone(priv);
            }
 
          /* Check for Tx events ********************************************/
          /* TX ERROR -- Triggered on transmit errors: LateCollision,
           * ExcessiveCollision and ExcessiveDefer, NoDescriptor or Underrun.
patacongo's avatar
patacongo committed
           * NOTE: We will still need to call lpc17_txdone() in order to
           * clean up after the failed transmit.
          if ((status & ETH_INT_TXERR) != 0)
            {
patacongo's avatar
patacongo committed
              nlldbg("TX Error. status: %08x\n", status);
              EMAC_STAT(priv, tx_errors);
            }

patacongo's avatar
patacongo committed
          /* TX FINISHED -- Triggered when all transmit descriptors have
           * been processed i.e. on the transition to the situation
           * where ProduceIndex == ConsumeIndex.
           */
patacongo's avatar
patacongo committed
          if ((status & ETH_INT_TXFIN) != 0)
            {
              EMAC_STAT(priv, tx_finished);
            }
patacongo's avatar
patacongo committed
          /* TX DONE -- Triggered when a descriptor has been transmitted
           * while the Interrupt bit in the Control field of the
           * descriptor was set.
           */

          if ((status & ETH_INT_TXDONE) != 0)
            {
              EMAC_STAT(priv, tx_done);
patacongo's avatar
patacongo committed
              /* A packet transmission just completed */
patacongo's avatar
patacongo committed
              lpc17_txdone(priv);
patacongo's avatar
patacongo committed
  /* Clear the pending interrupt */
patacongo's avatar
patacongo committed
#if 0 /* Apparently not necessary */
# if CONFIG_LPC17_NINTERFACES > 1
  lpc17_clrpend(priv->irq);
patacongo's avatar
patacongo committed
# else
  lpc17_clrpend(LPC17_IRQ_ETH);
patacongo's avatar
patacongo committed
# endif
patacongo's avatar
patacongo committed

  return OK;
}

/****************************************************************************
 * Function: lpc17_txtimeout
 *
 * Description:
 *   Our TX watchdog timed out.  Called from the timer interrupt handler.
 *   The last TX never completed.  Reset the hardware and start again.
 *
 * Parameters:
 *   argc - The number of available arguments
 *   arg  - The first argument
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
patacongo's avatar
patacongo committed
 *   Global interrupts are disabled by the watchdog logic.
 *
 ****************************************************************************/

static void lpc17_txtimeout(int argc, uint32_t arg, ...)
{
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;

  /* Increment statistics and dump debug info */

patacongo's avatar
patacongo committed
  EMAC_STAT(priv, tx_timeouts);
  if (priv->lp_ifup)
    {
      /* Then reset the hardware. ifup() will reset the interface, then bring
       * it back up.
       */

      (void)lpc17_ifup(&priv->lp_dev);
      /* Then poll uIP for new XMIT data */
      (void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
    }
}

/****************************************************************************
 * Function: lpc17_polltimer
 *
 * Description:
 *   Periodic timer handler.  Called from the timer interrupt handler.
 *
 * Parameters:
 *   argc - The number of available arguments
 *   arg  - The first argument
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
patacongo's avatar
patacongo committed
 *   Global interrupts are disabled by the watchdog logic.
 *
 ****************************************************************************/

static void lpc17_polltimer(int argc, uint32_t arg, ...)
{
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;

  /* Check if there is room in the send another TX packet.  We cannot perform
   * the TX poll if he are unable to accept another packet for transmission.
   */

  if (lpc17_txdesc(priv) == OK)
    {
      /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
       * might be bug here.  Does this mean if there is a transmit in progress,
       * we will missing TCP time state updates?
       */
      (void)uip_timer(&priv->lp_dev, lpc17_uiptxpoll, LPC17_POLLHSEC);
    }

  /* Setup the watchdog poll timer again */

  (void)wd_start(priv->lp_txpoll, LPC17_WDDELAY, lpc17_polltimer, 1, arg);
}

/****************************************************************************
 * Function: lpc17_ifup
 *
 * Description:
 *   NuttX Callback: Bring up the Ethernet interface when an IP address is
 *   provided 
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

static int lpc17_ifup(struct uip_driver_s *dev)
{
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
patacongo's avatar
patacongo committed
  uint32_t regval;
patacongo's avatar
patacongo committed
  int ret;

  ndbg("Bringing up: %d.%d.%d.%d\n",
       dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
       (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
patacongo's avatar
patacongo committed
  /* Reset the Ethernet controller (again) */

  lpc17_ethreset(priv);

  /* Initialize the PHY and wait for the link to be established */

  ret = lpc17_phyinit(priv);
  if (ret != 0)
    {
      ndbg("lpc17_phyinit failed: %d\n", ret);
      return ret;
    }
patacongo's avatar
patacongo committed
  /* Configure the MAC station address */

patacongo's avatar
patacongo committed
  regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[5] << 8 |
           (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[4];
patacongo's avatar
patacongo committed
  lpc17_putreg(regval, LPC17_ETH_SA0);

  regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[3] << 8 |
           (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[2];
  lpc17_putreg(regval, LPC17_ETH_SA1);

patacongo's avatar
patacongo committed
  regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[1] << 8 |
           (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[0];
patacongo's avatar
patacongo committed
  lpc17_putreg(regval, LPC17_ETH_SA2);

patacongo's avatar
patacongo committed
  /* Initialize Ethernet interface for the PHY setup */

  lpc17_macmode(priv->lp_mode);

  /* Initialize EMAC DMA memory -- descriptors, status, packet buffers, etc. */

  lpc17_txdescinit(priv);
  lpc17_rxdescinit(priv);
  /* Configure to pass all received frames */

  regval = lpc17_getreg(LPC17_ETH_MAC1);
  regval |= ETH_MAC1_PARF;
  lpc17_putreg(regval, LPC17_ETH_MAC1);

  /* Set up RX filter and configure to accept broadcast addresses, multicast
   * addresses, and perfect station address matches.  We should also accept
   * perfect matches and, most likely, broadcast (for example, for ARP requests).
   * Other RX filter options will only be enabled if so selected.  NOTE: There
   * is a selection CONFIG_NET_BROADCAST, but this enables receipt of UDP
   * broadcast packets inside of the stack.
  regval = ETH_RXFLCTRL_PERFEN | ETH_RXFLCTRL_BCASTEN;
  RXFILTERCTRL |= (ETH_RXFLCTRL_MCASTEN | ETH_RXFLCTRL_UCASTEN);
#endif
  RXFILTERCTRL |= (ETH_RXFLCTRL_MCASTHASHEN | ETH_RXFLCTRL_UCASTHASHEN);
#endif
  lpc17_putreg(regval, LPC17_ETH_RXFLCTRL);

  /* Clear any pending interrupts (shouldn't be any) */

  lpc17_putreg(0xffffffff, LPC17_ETH_INTCLR);

  /* Configure interrupts.  The Ethernet interrupt was attached during one-time
   * initialization, so we only need to set the interrupt priority, configure
   * interrupts, and enable them.
  /* Set the interrupt to the highest priority */

#ifdef CONFIG_ARCH_IRQPRIO
patacongo's avatar
patacongo committed
#if CONFIG_LPC17_NINTERFACES > 1
  (void)up_prioritize_irq(priv->irq, CONFIG_NET_PRIORITY);
  (void)up_prioritize_irq(LPC17_IRQ_ETH, CONFIG_NET_PRIORITY);
#endif
#endif

  /* Enable Ethernet interrupts.  The way we do this depends on whether or
   * not Wakeup on Lan (WoL) has been configured.
   */

  /* Configure WoL: Clear all receive filter WoLs and enable the perfect
   * match WoL interrupt.  We will wait until the Wake-up to finish
   * bringing things up.
   */

  lpc17_putreg(0xffffffff, LPC17_ETH_RXFLWOLCLR);
  lpc17_putreg(ETH_RXFLCTRL_RXFILEN, LPC17_ETH_RXRLCTRL);
patacongo's avatar
patacongo committed

  priv->lp_inten = ETH_INT_WKUP;
  lpc17_putreg(ETH_INT_WKUP, LPC17_ETH_INTEN);
#else
patacongo's avatar
patacongo committed
  /* Otherwise, enable all Rx interrupts.  Tx interrupts, SOFTINT and WoL are
   * excluded.  Tx interrupts will not be enabled until there is data to be
   * sent.
   */
patacongo's avatar
patacongo committed
  priv->lp_inten = ETH_RXINTS;
  lpc17_putreg(ETH_RXINTS, LPC17_ETH_INTEN);
patacongo's avatar
patacongo committed
  /* Enable Rx. "Enabling of the receive function is located in two places.
   * The receive DMA manager needs to be enabled and the receive data path
   * of the MAC needs to be enabled. To prevent overflow in the receive
   * DMA engine the receive DMA engine should be enabled by setting the
   * RxEnable bit in the Command register before enabling the receive data
   * path in the MAC by setting the RECEIVE ENABLE bit in the MAC1 register."
   */

  regval  = lpc17_getreg(LPC17_ETH_CMD);
  regval |= ETH_CMD_RXEN;
  lpc17_putreg(regval, LPC17_ETH_CMD);

  regval  = lpc17_getreg(LPC17_ETH_MAC1);
  regval |= ETH_MAC1_RE;
  lpc17_putreg(regval, LPC17_ETH_MAC1);

  /* Enable Tx */

  regval  = lpc17_getreg(LPC17_ETH_CMD);
  regval |= ETH_CMD_TXEN;
  lpc17_putreg(regval, LPC17_ETH_CMD);

  /* Set and activate a timer process */

  (void)wd_start(priv->lp_txpoll, LPC17_WDDELAY, lpc17_polltimer, 1,
                (uint32_t)priv);
  /* Finally, make the interface up and enable the Ethernet interrupt at
   * the interrupt controller
   */
patacongo's avatar
patacongo committed
  priv->lp_ifup = true;
patacongo's avatar
patacongo committed
#if CONFIG_LPC17_NINTERFACES > 1
  up_enable_irq(priv->irq);
#else
  up_enable_irq(LPC17_IRQ_ETH);
  return OK;
}

/****************************************************************************
 * Function: lpc17_ifdown
 *
 * Description:
 *   NuttX Callback: Stop the interface.
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

static int lpc17_ifdown(struct uip_driver_s *dev)
{
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
  irqstate_t flags;

  /* Disable the Ethernet interrupt */

  flags = irqsave();
  up_disable_irq(LPC17_IRQ_ETH);

  /* Cancel the TX poll timer and TX timeout timers */

  wd_cancel(priv->lp_txpoll);
  wd_cancel(priv->lp_txtimeout);

patacongo's avatar
patacongo committed
  /* Reset the device and mark it as down. */
patacongo's avatar
patacongo committed
  lpc17_ethreset(priv);
patacongo's avatar
patacongo committed
  priv->lp_ifup = false;
  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Function: lpc17_txavail
 *
 * Description:
 *   Driver callback invoked when new TX data is available.  This is a 
 *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
 *   latency.
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Called in normal user mode
 *
 ****************************************************************************/

static int lpc17_txavail(struct uip_driver_s *dev)
{
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
  irqstate_t flags;

patacongo's avatar
patacongo committed
  /* Disable interrupts because this function may be called from interrupt
   * level processing.
   */

  flags = irqsave();

  /* Ignore the notification if the interface is not yet up */

patacongo's avatar
patacongo committed
  if (priv->lp_ifup)
    {
      /* Check if there is room in the hardware to hold another outgoing packet. */

      if (lpc17_txdesc(priv) == OK)
        {
          /* If so, then poll uIP for new XMIT data */
          (void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
        }
    }

  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Function: lpc17_addmac
 *
 * Description:
 *   NuttX Callback: Add the specified MAC address to the hardware multicast
 *   address filtering
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *   mac  - The MAC address to be added 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef CONFIG_NET_IGMP
patacongo's avatar
patacongo committed
static int lpc17_addmac(struct uip_driver_s *dev, const uint8_t *mac)
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;

  /* Add the MAC address to the hardware multicast routing table */

#warning "Not implemented"
  return OK;
}
#endif

/****************************************************************************
 * Function: lpc17_rmmac
 *
 * Description:
 *   NuttX Callback: Remove the specified MAC address from the hardware multicast
 *   address filtering
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *   mac  - The MAC address to be removed 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef CONFIG_NET_IGMP
patacongo's avatar
patacongo committed
static int lpc17_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;

  /* Add the MAC address to the hardware multicast routing table */

#warning "Not implemented"
patacongo's avatar
patacongo committed
/*******************************************************************************
 * Name: lpc17_showpins
 *
 * Description:
 *   Dump GPIO registers
 *
 * Parameters:
 *   None 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 *******************************************************************************/

patacongo's avatar
patacongo committed
#if defined(CONFIG_NET_REGDEBUG) && defined(CONFIG_DEBUG_GPIO)
patacongo's avatar
patacongo committed
static void lpc17_showpins(void)
{
patacongo's avatar
patacongo committed
  lpc17_dumpgpio(GPIO_PORT1|GPIO_PIN0, "P1[1-15]");
  lpc17_dumpgpio(GPIO_PORT1|GPIO_PIN16, "P1[16-31]");
patacongo's avatar
patacongo committed
}
#endif

/*******************************************************************************
 * Name: lpc17_showmii
 *
 * Description:
 *   Dump PHY MII registers
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 *******************************************************************************/

#if defined(CONFIG_NET_REGDEBUG) && defined(LPC17_HAVE_PHY)
patacongo's avatar
patacongo committed
static void lpc17_showmii(uint8_t phyaddr, const char *msg)
{
patacongo's avatar
patacongo committed
  dbg("PHY " LPC17_PHYNAME ": %s\n", msg);
  dbg("  MCR:       %04x\n", lpc17_phyread(phyaddr, MII_MCR));
  dbg("  MSR:       %04x\n", lpc17_phyread(phyaddr, MII_MSR));
  dbg("  ADVERTISE: %04x\n", lpc17_phyread(phyaddr, MII_ADVERTISE));
  dbg("  LPA:       %04x\n", lpc17_phyread(phyaddr, MII_LPA));
  dbg("  EXPANSION: %04x\n", lpc17_phyread(phyaddr, MII_EXPANSION));
patacongo's avatar
patacongo committed
#ifdef CONFIG_PHY_KS8721
  dbg("  10BTCR:    %04x\n", lpc17_phyread(phyaddr, MII_KS8721_10BTCR));
patacongo's avatar
patacongo committed
#endif
}
#endif

/****************************************************************************
 * Function: lpc17_phywrite
 *
 * Description:
 *   Write a value to an MII PHY register
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *   regaddr - The address of the PHY register to be written
 *   phydata - The data to write to the PHY register 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
#ifdef LPC17_HAVE_PHY
static void lpc17_phywrite(uint8_t phyaddr, uint8_t regaddr, uint16_t phydata)
{
  uint32_t regval;

  /* Set PHY address and PHY register address */

  regval = ((uint32_t)phyaddr << ETH_MADR_PHYADDR_SHIFT) |
           ((uint32_t)regaddr << ETH_MADR_REGADDR_SHIFT);
  lpc17_putreg(regval, LPC17_ETH_MADR);

  /* Set up to write */

  lpc17_putreg(ETH_MCMD_WRITE, LPC17_ETH_MCMD);

  /* Write the register data to the PHY */

  lpc17_putreg((uint32_t)phydata, LPC17_ETH_MWTD);

  /* Wait for the PHY command to complete */

  while ((lpc17_getreg(LPC17_ETH_MIND) & ETH_MIND_BUSY) != 0);
}
patacongo's avatar
patacongo committed
#endif

/****************************************************************************
patacongo's avatar
patacongo committed
 * Function: lpc17_phyread
 *
 * Description:
 *   Read a value from an MII PHY register
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *   regaddr - The address of the PHY register to be written
 *
 * Returned Value:
 *   Data read from the PHY register
 *
 * Assumptions:
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
#ifdef LPC17_HAVE_PHY
static uint16_t lpc17_phyread(uint8_t phyaddr, uint8_t regaddr)
{
  uint32_t regval;

  lpc17_putreg(0, LPC17_ETH_MCMD);

  /* Set PHY address and PHY register address */

  regval = ((uint32_t)phyaddr << ETH_MADR_PHYADDR_SHIFT) |
           ((uint32_t)regaddr << ETH_MADR_REGADDR_SHIFT);
  lpc17_putreg(regval, LPC17_ETH_MADR);

  /* Set up to read */

  lpc17_putreg(ETH_MCMD_READ, LPC17_ETH_MCMD);

  /* Wait for the PHY command to complete */

  while ((lpc17_getreg(LPC17_ETH_MIND) & (ETH_MIND_BUSY|ETH_MIND_NVALID)) != 0);
  lpc17_putreg(0, LPC17_ETH_MCMD);

  /* Return the PHY register data */

patacongo's avatar
patacongo committed
  return (uint16_t)(lpc17_getreg(LPC17_ETH_MRDD) & ETH_MRDD_MASK);
}
#endif

/****************************************************************************
 * Function: lpc17_phyreset
 *
 * Description:
 *   Reset the PHY
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef LPC17_HAVE_PHY
static inline int lpc17_phyreset(uint8_t phyaddr)
{
  int32_t timeout;
  uint16_t phyreg;

  /* Reset the PHY.  Needs a minimal 50uS delay after reset. */

  lpc17_phywrite(phyaddr, MII_MCR, MII_MCR_RESET);

  /* Wait for a minimum of 50uS no matter what */

  up_udelay(50);

  /* The MCR reset bit is self-clearing.  Wait for it to be clear indicating
   * that the reset is complete.
patacongo's avatar
patacongo committed
   */

  for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
    {
      phyreg = lpc17_phyread(phyaddr, MII_MCR);
      if ((phyreg & MII_MCR_RESET) == 0)
        {
          return OK;
        }
    }

patacongo's avatar
patacongo committed
  ndbg("Reset failed. MCR: %04x\n", phyreg);
patacongo's avatar
patacongo committed
  return -ETIMEDOUT;
}
#endif

/****************************************************************************
 * Function: lpc17_phyautoneg
 *
 * Description:
 *   Enable auto-negotiation.
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   The adverisement regiser has already been configured.
 *
 ****************************************************************************/

#if defined(LPC17_HAVE_PHY) && defined(CONFIG_PHY_AUTONEG)
static inline int lpc17_phyautoneg(uint8_t phyaddr)
{
  int32_t timeout;
  uint16_t phyreg;

  /* Start auto-negotiation */

  lpc17_phywrite(phyaddr, MII_MCR, MII_MCR_ANENABLE | MII_MCR_ANRESTART);

  /* Wait for autonegotiation to complete */

  for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
    {
      /* Check if auto-negotiation has completed */

      phyreg = lpc17_phyread(phyaddr, MII_MSR);
      if ((phyreg & (MII_MSR_LINKSTATUS | MII_MSR_ANEGCOMPLETE)) == 
          (MII_MSR_LINKSTATUS | MII_MSR_ANEGCOMPLETE))
        {
          /* Yes.. return success */

          return OK;
        }
    }

patacongo's avatar
patacongo committed
  ndbg("Auto-negotiation failed. MSR: %04x\n", phyreg);
patacongo's avatar
patacongo committed
  return -ETIMEDOUT;
}
#endif

/****************************************************************************
patacongo's avatar
patacongo committed
 * Function: lpc17_phymode
patacongo's avatar
patacongo committed
 *
 * Description:
patacongo's avatar
patacongo committed
 *   Set the PHY to operate at a selected speed/duplex mode.
patacongo's avatar
patacongo committed
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
patacongo's avatar
patacongo committed
 *   mode - speed/duplex mode
patacongo's avatar
patacongo committed
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef LPC17_HAVE_PHY
patacongo's avatar
patacongo committed
static int lpc17_phymode(uint8_t phyaddr, uint8_t mode)
patacongo's avatar
patacongo committed
{
  int32_t timeout;
  uint16_t phyreg;

  /* Disable auto-negotiation and set fixed Speed and Duplex settings */

  phyreg = 0;
patacongo's avatar
patacongo committed
  if ((mode & LPC17_SPEED_MASK) ==  LPC17_SPEED_100)
patacongo's avatar
patacongo committed
    {
      phyreg = MII_MCR_SPEED100;
    }

patacongo's avatar
patacongo committed
  if ((mode & LPC17_DUPLEX_MASK) == LPC17_DUPLEX_FULL)
patacongo's avatar
patacongo committed
    {
      phyreg |= MII_MCR_FULLDPLX;
    }

  lpc17_phywrite(phyaddr, MII_MCR, phyreg);

  /* Then wait for the link to be established */

  for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
    {
      phyreg = lpc17_phyread(phyaddr, MII_MSR);
      if (phyreg & MII_MSR_LINKSTATUS)
        {
          /* Yes.. return success */

          return OK;
        }
    }

patacongo's avatar
patacongo committed
  ndbg("Link failed. MSR: %04x\n", phyreg);
patacongo's avatar
patacongo committed
  return -ETIMEDOUT;
}
#endif

/****************************************************************************
 * Function: lpc17_phyinit
 *
 * Description:
 *   Initialize the PHY
 *
 * Parameters:
 *   priv - Pointer to EMAC device driver structure 
 *
 * Returned Value:
 *   None directly.  As a side-effect, it will initialize priv->lp_phyaddr
 *   and priv->lp_phymode.
patacongo's avatar
patacongo committed
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef LPC17_HAVE_PHY
static inline int lpc17_phyinit(struct lpc17_driver_s *priv)
{
  unsigned int phyaddr;
  uint16_t phyreg;
  uint32_t regval;
  int ret;

  /* MII configuration: host clocked divided per board.h, no suppress
   * preamble, no scan increment.
patacongo's avatar
patacongo committed
   */

  lpc17_putreg(ETH_MCFG_CLKSEL_DIV, LPC17_ETH_MCFG);
  lpc17_putreg(0, LPC17_ETH_MCMD);

  /* Enter RMII mode and select 100 MBPS support */

  lpc17_putreg(ETH_CMD_RMII, LPC17_ETH_CMD);
  lpc17_putreg(ETH_SUPP_SPEED, LPC17_ETH_SUPP);

  /* Find PHY Address.  Because the controller has a pull-up and the
   * PHY has pull-down resistors on RXD lines some times the PHY
patacongo's avatar
patacongo committed
   * latches different at different addresses.
   */

  for (phyaddr = 1; phyaddr < 32; phyaddr++)
    {
       /* Check if we can see the selected device ID at this
        * PHY address.