Skip to content
Snippets Groups Projects
lpc17_ethernet.c 41.6 KiB
Newer Older

  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.
   */

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

  nlldbg("Reset failed. MCR: %04x\n", phyreg);
  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;
        }
    }

  nlldbg("Auto-negotiation failed. MSR: %04x\n", phyreg);
  return -ETIMEDOUT;
}
#endif

/****************************************************************************
 * Function: lpc17_phyfixed
 *
 * Description:
 *   Set fixed PHY configuration.
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef LPC17_HAVE_PHY
static int lpc17_phyfixed(uint8_t phyaddr, bool speed100, bool fullduplex)
{
  int32_t timeout;
  uint16_t phyreg;

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

  phyreg = 0;
  if (speed100)
    {
      phyreg = MII_MCR_SPEED100;
    }

  if (fullduplex)
    {
      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;
        }
    }

  nlldbg("Link failed. MSR: %04x\n", phyreg);
  return -ETIMEDOUT;
}
#endif

/****************************************************************************
 * Function: lpc17_phyfulldplex
 *
 * Description:
patacongo's avatar
patacongo committed
 *   Tweak a few things for full/half duplex.
patacongo's avatar
patacongo committed
 *
 * Parameters:
 *   phyaddr - The device address where the PHY was discovered
patacongo's avatar
patacongo committed
 *   fullduplex - TRUE: full duplex
patacongo's avatar
patacongo committed
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef LPC17_HAVE_PHY
patacongo's avatar
patacongo committed
static void lpc17_phyduplex(uint8_t phyaddr, bool fullduplex)
patacongo's avatar
patacongo committed
{
  uint32_t regval;

patacongo's avatar
patacongo committed
  if (fullduplex)
    {
      /* Set the inter-packet gap */
patacongo's avatar
patacongo committed
 
patacongo's avatar
patacongo committed
      lpc17_putreg(21, LPC17_ETH_IPGT);
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
      /* Set MAC to operate in full duplex mode */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
      regval = lpc17_getreg(LPC17_ETH_MAC2);
      regval |= ETH_MAC2_FD;
      lpc17_putreg(regval, LPC17_ETH_MAC2);
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
      /* Select full duplex operation for ethernet controller */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
      regval = lpc17_getreg(LPC17_ETH_CMD);
      regval |= ETH_CMD_FD;
      lpc17_putreg(regval, LPC17_ETH_CMD);
    }
  else
    {
      /* Set the inter-packet gap */
 
      lpc17_putreg(18, LPC17_ETH_IPGT);

      /* Set MAC to operate in half duplex mode */

      regval = lpc17_getreg(LPC17_ETH_MAC2);
      regval &= ~ETH_MAC2_FD;
      lpc17_putreg(regval, LPC17_ETH_MAC2);

      /* Select half duplex operation for ethernet controller */

      regval = lpc17_getreg(LPC17_ETH_CMD);
      regval &= ~ETH_CMD_FD;
      lpc17_putreg(regval, LPC17_ETH_CMD);
    }
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Function: lpc17_phyinit
 *
 * Description:
 *   Initialize the PHY
 *
 * Parameters:
 *   priv - Pointer to EMAC device driver structure 
 *
 * Returned Value:
 *   None
 *
 * 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
   * preambl,e no scan increment.
   */

  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 controller has a pull-up and the
   * PHY have pull-down resistors on RXD lines some times the PHY
   * latches different at different addresses.
   */

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

       phyreg = (unsigned int)lpc17_phyread(phyaddr, MII_PHYID1);
       if (phyreg == LPC17_PHYID1)
        {
          phyreg = lpc17_phyread(phyaddr, MII_PHYID2);
          if (phyreg  == LPC17_PHYID2)
            {
              break;
            }
        }
    }
  nlldbg("phyaddr: %d\n", phyaddr);

  /* Check if the PHY device address was found */

  if (phyaddr > 31)
    {
      /* Failed to find PHY at any location */

      return -ENODEV;
    }

  /* Save the discovered PHY device address */

  priv->lp_phyaddr = phyaddr;

  /* Reset the PHY */

  ret = lpc17_phyreset(phyaddr);
  if (ret < 0)
    {
      return ret;
    }
  lpc17_showmii(phyaddr, "After reset");

  /* Check for preamble suppression support */

  phyreg = lpc17_phyread(phyaddr, MII_MSR);
  if ((phyreg & MII_MSR_MFRAMESUPPRESS) != 0)
    {
      /* The PHY supports preamble suppression */

      regval  = lpc17_getreg(LPC17_ETH_MCFG);
      regval |= ETH_MCFG_SUPPRE;
      lpc17_putreg(regval, LPC17_ETH_MCFG);
    }

  /* Are we configured to do auto-negotiation? */

#ifdef CONFIG_PHY_AUTONEG
  /* Setup the Auto-negotiation advertisement: 100 or 10, and HD or FD */

  lpc17_phywrite(phyaddr, MII_ADVERTISE, 
                 (MII_ADVERTISE_100BASETXFULL | MII_ADVERTISE_100BASETXHALF |
                  MII_ADVERTISE_10BASETXFULL  | MII_ADVERTISE_10BASETXHALF  |
                  MII_ADVERTISE_CSMA));
patacongo's avatar
patacongo committed

  /* Then perform the auto-negotiation */

patacongo's avatar
patacongo committed
  ret = lpc17_phyautoneg(phyaddr);
  if (ret < 0)
    {
      return ret;
    }
#else
  /* Set up the fixed PHY configuration */

patacongo's avatar
patacongo committed
#ifdef CONFIG_PHY_SPEED100
  priv->lp_speed100 = true;
#else
  priv->lp_speed100 = false;
#endif

#ifdef CONFIG_PHY_FDUPLEX
  priv->lp_fullduplex = true;
#else
  priv->lp_fullduplex = false;
#endif

  ret = lpc17_phyfixed(phyaddr, priv->lp_speed100, priv->lp_fullduplex);
patacongo's avatar
patacongo committed
  if (ret < 0)
    {
      return ret;
    }
#endif

  /* The link is established */

  lpc17_showmii(phyaddr, "After link established");

  /* Check configuration */

#ifdef CONFIG_PHY_KS8721
  phyreg = lpc17_phyread(phyaddr, MII_KS8721_10BTCR);

  switch (phyreg & KS8721_10BTCR_MODE_MASK)
    {
      case KS8721_10BTCR_MODE_10BTHD:  /* 10BASE-T half duplex */
patacongo's avatar
patacongo committed
        priv->lp_fullduplex = false;
        priv->lp_speed100 = false;
patacongo's avatar
patacongo committed
        lpc17_putreg(0, LPC17_ETH_SUPP);
        break;
      case KS8721_10BTCR_MODE_100BTHD: /* 100BASE-T half duplex */
patacongo's avatar
patacongo committed
        priv->lp_fullduplex = false;
        priv->lp_speed100 = true;
patacongo's avatar
patacongo committed
        break;
      case KS8721_10BTCR_MODE_10BTFD: /* 10BASE-T full duplex */
patacongo's avatar
patacongo committed
        priv->lp_fullduplex = true;
        priv->lp_speed100 = false;
patacongo's avatar
patacongo committed
        lpc17_putreg(0, LPC17_ETH_SUPP);
        break;
      case KS8721_10BTCR_MODE_100BTFD: /* 100BASE-T full duplex */
patacongo's avatar
patacongo committed
        priv->lp_fullduplex = true;
        priv->lp_speed100 = true;
patacongo's avatar
patacongo committed
        break;
      default:
        lldbg("Unrecognized mode: %04x\n", phyreg);
        return -ENODEV;
    }
#endif

patacongo's avatar
patacongo committed
  nlldbg("%dBase-T %s duplex\n",
         priv->lp_speed100 ? 10 : 100,
         priv->lp_fullduplex ? "full" : "half");

  /* Configure the MAC for this speed */

  lpc17_phyduplex(phyaddr, priv->lp_fullduplex);
  lpc17_phyfixed(phyaddr, priv->lp_speed100, priv->lp_fullduplex);
  lpc17_showmii(phyaddr, "At completion");

patacongo's avatar
patacongo committed
  return OK;
}
#endif

/****************************************************************************
 * Function: lpc17_ethreset
 *
 * Description:
 *   Configure and reset the Ethernet module, leaving it in a disabled state.
 *
 * Parameters:
 *   priv   - Reference to the driver state structure
 *
 * Returned Value:
 *   OK on success; a negated errno on failure
 *
 * Assumptions:
 *
 ****************************************************************************/

static void lpc17_ethreset(struct lpc17_driver_s *priv)
{
  irqstate_t flags;
  uint32_t regval;

#if LPC17_NETHCONTROLLERS > 1
#  error "If multiple interfaces are supported, this function would have to be redesigned"
#endif

  /* Make sure that clocking is enabled for the Ethernet (and PHY) peripherals */

  flags   = irqsave();

  /* Put the Ethernet controller into the reset state */

  /* Wait just a bit.  This is a much longer delay than necessary */

  up_mdelay(2);

  /* Then take the Ethernet controller out of the reset state */

  /* Disable all Ethernet controller interrupts */

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

  irqrestore(flags);
/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Function: lpc17_ethinitialize
 *
 * Description:
 *   Initialize one Ethernet controller and driver structure.
 *   intf - Selects the interface to be initialized.
 *
 * Returned Value:
 *   OK on success; Negated errno on failure.
 *
 * Assumptions:
 *
 ****************************************************************************/

#if LPC17_NETHCONTROLLERS > 1
int lpc17_ethinitialize(int intf)
#else
static inline int lpc17_ethinitialize(int intf)
#endif
patacongo's avatar
patacongo committed
  struct lpc17_driver_s *priv;
patacongo's avatar
patacongo committed
  int ret;
patacongo's avatar
patacongo committed
  DEBUGASSERT(inf < LPC17_NETHCONTROLLERS);
  priv = &g_ethdrvr[intf];

  /* Turn on the ethernet MAC clock */

  regval  = lpc17_getreg(LPC17_SYSCON_PCONP);
  regval |= SYSCON_PCONP_PCENET;
  lpc17_putreg(regval, LPC17_SYSCON_PCONP);

  /* Configure all GPIO pins needed by ENET */

  for (i = 0; i < GPIO_NENET_PINS; i++)
    {
      (void)lpc17_configgpio(g_enetpins[i]);
    }
  lpc17_showpins();

  /* Initialize the driver structure */

  memset(g_ethdrvr, 0, CONFIG_LPC17_NINTERFACES*sizeof(struct lpc17_driver_s));
patacongo's avatar
patacongo committed
  priv->lp_dev.d_ifup    = lpc17_ifup;    /* I/F down callback */
  priv->lp_dev.d_ifdown  = lpc17_ifdown;  /* I/F up (new IP address) callback */
  priv->lp_dev.d_txavail = lpc17_txavail; /* New TX data callback */
#ifdef CONFIG_NET_IGMP
patacongo's avatar
patacongo committed
  priv->lp_dev.d_addmac  = lpc17_addmac;  /* Add multicast MAC address */
  priv->lp_dev.d_rmmac   = lpc17_rmmac;   /* Remove multicast MAC address */
#endif
  priv->lp_dev.d_private = (void*)priv;   /* Used to recover private state from dev */

#if LM3S_NETHCONTROLLERS > 1
# error "A mechanism to associate base address an IRQ with an interface is needed"
  priv->lp_base          = ??;            /* Ethernet controller base address */
  priv->lp_irq           = ??;            /* Ethernet controller IRQ number */
#endif

  /* Create a watchdog for timing polling for and timing of transmisstions */

patacongo's avatar
patacongo committed
  priv->lp_txpoll       = wd_create();    /* Create periodic poll timer */
  priv->lp_txtimeout    = wd_create();    /* Create TX timeout timer */

  /* Perform minimal, one-time initialization -- just reset the controller and
   * leave it disabled.  The Ethernet controller will be reset and properly
   * re-initialized each time lpc17_ifup() is called.
   */

  lpc17_ethreset(priv);
  lpc17_ifdown(&priv->lp_dev);

  /* Attach the IRQ to the driver */

#if LM3S_NETHCONTROLLERS > 1
  ret = irq_attach(priv->irq, lpc17_interrupt);
#else
  ret = irq_attach(LPC17_IRQ_ETH, lpc17_interrupt);
#endif
  if (ret != 0)
    {
      /* We could not attach the ISR to the the interrupt */
patacongo's avatar
patacongo committed
      return -EAGAIN;
    }

  /* Register the device with the OS so that socket IOCTLs can be performed */

  (void)netdev_register(&priv->lp_dev);
/****************************************************************************
 * Name: up_netinitialize
 *
 * Description:
 *   Initialize the first network interface.  If there are more than one
 *   interface in the chip, then board-specific logic will have to provide
 *   this function to determine which, if any, Ethernet controllers should
 *   be initialized.
 *
 ****************************************************************************/

#if LPC17_NETHCONTROLLERS == 1
void up_netinitialize(void)
{
  (void)lpc17_ethinitialize(0);
}
#endif
#endif /* LPC17_NETHCONTROLLERS > 0 */
#endif /* CONFIG_NET && CONFIG_LPC17_ETHERNET */