Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  niu.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* niu.c: Neptune ethernet driver.
 *
 * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net)
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/mii.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/ipv6.h>
#include <linux/log2.h>
#include <linux/jiffies.h>
#include <linux/crc32.h>
#include <linux/list.h>
#include <linux/slab.h>

#include <linux/io.h>
#include <linux/of.h>

#include "niu.h"

/* This driver wants to store a link to a "next page" within the
 * page struct itself by overloading the content of the "mapping"
 * member. This is not expected by the page API, but does currently
 * work. However, the randstruct plugin gets very bothered by this
 * case because "mapping" (struct address_space) is randomized, so
 * casts to/from it trigger warnings. Hide this by way of a union,
 * to create a typed alias of "mapping", since that's how it is
 * actually being used here.
 */

union niu_page {
 struct page page;
 struct {
  unsigned long __flags; /* unused alias of "flags" */
  struct list_head __lru; /* unused alias of "lru" */
  struct page *next; /* alias of "mapping" */
 };
};
#define niu_next_page(p) container_of(p, union niu_page, page)->next

#define DRV_MODULE_NAME  "niu"
#define DRV_MODULE_VERSION "1.1"
#define DRV_MODULE_RELDATE "Apr 22, 2010"

static char version[] =
 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";

MODULE_AUTHOR("David S. Miller ");
MODULE_DESCRIPTION("NIU ethernet driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);

#ifndef readq
static u64 readq(void __iomem *reg)
{
 return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32);
}

static void writeq(u64 val, void __iomem *reg)
{
 writel(val & 0xffffffff, reg);
 writel(val >> 32, reg + 0x4UL);
}
#endif

static const struct pci_device_id niu_pci_tbl[] = {
 {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0xabcd)},
 {}
};

MODULE_DEVICE_TABLE(pci, niu_pci_tbl);

#define NIU_TX_TIMEOUT   (5 * HZ)

#define nr64(reg)  readq(np->regs + (reg))
#define nw64(reg, val)  writeq((val), np->regs + (reg))

#define nr64_mac(reg)  readq(np->mac_regs + (reg))
#define nw64_mac(reg, val) writeq((val), np->mac_regs + (reg))

#define nr64_ipp(reg)  readq(np->regs + np->ipp_off + (reg))
#define nw64_ipp(reg, val) writeq((val), np->regs + np->ipp_off + (reg))

#define nr64_pcs(reg)  readq(np->regs + np->pcs_off + (reg))
#define nw64_pcs(reg, val) writeq((val), np->regs + np->pcs_off + (reg))

#define nr64_xpcs(reg)  readq(np->regs + np->xpcs_off + (reg))
#define nw64_xpcs(reg, val) writeq((val), np->regs + np->xpcs_off + (reg))

#define NIU_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)

static int niu_debug;
static int debug = -1;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "NIU debug level");

#define niu_lock_parent(np, flags) \
 spin_lock_irqsave(&np->parent->lock, flags)
#define niu_unlock_parent(np, flags) \
 spin_unlock_irqrestore(&np->parent->lock, flags)

static int serdes_init_10g_serdes(struct niu *np);

static int __niu_wait_bits_clear_mac(struct niu *np, unsigned long reg,
         u64 bits, int limit, int delay)
{
 while (--limit >= 0) {
  u64 val = nr64_mac(reg);

  if (!(val & bits))
   break;
  udelay(delay);
 }
 if (limit < 0)
  return -ENODEV;
 return 0;
}

static int __niu_set_and_wait_clear_mac(struct niu *np, unsigned long reg,
     u64 bits, int limit, int delay,
     const char *reg_name)
{
 int err;

 nw64_mac(reg, bits);
 err = __niu_wait_bits_clear_mac(np, reg, bits, limit, delay);
 if (err)
  netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n",
      (unsigned long long)bits, reg_name,
      (unsigned long long)nr64_mac(reg));
 return err;
}

#define niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
 __niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
})

static int __niu_wait_bits_clear_ipp(struct niu *np, unsigned long reg,
         u64 bits, int limit, int delay)
{
 while (--limit >= 0) {
  u64 val = nr64_ipp(reg);

  if (!(val & bits))
   break;
  udelay(delay);
 }
 if (limit < 0)
  return -ENODEV;
 return 0;
}

static int __niu_set_and_wait_clear_ipp(struct niu *np, unsigned long reg,
     u64 bits, int limit, int delay,
     const char *reg_name)
{
 int err;
 u64 val;

 val = nr64_ipp(reg);
 val |= bits;
 nw64_ipp(reg, val);

 err = __niu_wait_bits_clear_ipp(np, reg, bits, limit, delay);
 if (err)
  netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n",
      (unsigned long long)bits, reg_name,
      (unsigned long long)nr64_ipp(reg));
 return err;
}

#define niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
 __niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
})

static int __niu_wait_bits_clear(struct niu *np, unsigned long reg,
     u64 bits, int limit, int delay)
{
 while (--limit >= 0) {
  u64 val = nr64(reg);

  if (!(val & bits))
   break;
  udelay(delay);
 }
 if (limit < 0)
  return -ENODEV;
 return 0;
}

#define niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY) \
({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
 __niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY); \
})

static int __niu_set_and_wait_clear(struct niu *np, unsigned long reg,
        u64 bits, int limit, int delay,
        const char *reg_name)
{
 int err;

 nw64(reg, bits);
 err = __niu_wait_bits_clear(np, reg, bits, limit, delay);
 if (err)
  netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n",
      (unsigned long long)bits, reg_name,
      (unsigned long long)nr64(reg));
 return err;
}

#define niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
 __niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
})

static void niu_ldg_rearm(struct niu *np, struct niu_ldg *lp, int on)
{
 u64 val = (u64) lp->timer;

 if (on)
  val |= LDG_IMGMT_ARM;

 nw64(LDG_IMGMT(lp->ldg_num), val);
}

static int niu_ldn_irq_enable(struct niu *np, int ldn, int on)
{
 unsigned long mask_reg, bits;
 u64 val;

 if (ldn < 0 || ldn > LDN_MAX)
  return -EINVAL;

 if (ldn < 64) {
  mask_reg = LD_IM0(ldn);
  bits = LD_IM0_MASK;
 } else {
  mask_reg = LD_IM1(ldn - 64);
  bits = LD_IM1_MASK;
 }

 val = nr64(mask_reg);
 if (on)
  val &= ~bits;
 else
  val |= bits;
 nw64(mask_reg, val);

 return 0;
}

static int niu_enable_ldn_in_ldg(struct niu *np, struct niu_ldg *lp, int on)
{
 struct niu_parent *parent = np->parent;
 int i;

 for (i = 0; i <= LDN_MAX; i++) {
  int err;

  if (parent->ldg_map[i] != lp->ldg_num)
   continue;

  err = niu_ldn_irq_enable(np, i, on);
  if (err)
   return err;
 }
 return 0;
}

static int niu_enable_interrupts(struct niu *np, int on)
{
 int i;

 for (i = 0; i < np->num_ldg; i++) {
  struct niu_ldg *lp = &np->ldg[i];
  int err;

  err = niu_enable_ldn_in_ldg(np, lp, on);
  if (err)
   return err;
 }
 for (i = 0; i < np->num_ldg; i++)
  niu_ldg_rearm(np, &np->ldg[i], on);

 return 0;
}

static u32 phy_encode(u32 type, int port)
{
 return type << (port * 2);
}

static u32 phy_decode(u32 val, int port)
{
 return (val >> (port * 2)) & PORT_TYPE_MASK;
}

static int mdio_wait(struct niu *np)
{
 int limit = 1000;
 u64 val;

 while (--limit > 0) {
  val = nr64(MIF_FRAME_OUTPUT);
  if ((val >> MIF_FRAME_OUTPUT_TA_SHIFT) & 0x1)
   return val & MIF_FRAME_OUTPUT_DATA;

  udelay(10);
 }

 return -ENODEV;
}

static int mdio_read(struct niu *np, int port, int dev, int reg)
{
 int err;

 nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
 err = mdio_wait(np);
 if (err < 0)
  return err;

 nw64(MIF_FRAME_OUTPUT, MDIO_READ_OP(port, dev));
 return mdio_wait(np);
}

static int mdio_write(struct niu *np, int port, int dev, int reg, int data)
{
 int err;

 nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
 err = mdio_wait(np);
 if (err < 0)
  return err;

 nw64(MIF_FRAME_OUTPUT, MDIO_WRITE_OP(port, dev, data));
 err = mdio_wait(np);
 if (err < 0)
  return err;

 return 0;
}

static int mii_read(struct niu *np, int port, int reg)
{
 nw64(MIF_FRAME_OUTPUT, MII_READ_OP(port, reg));
 return mdio_wait(np);
}

static int mii_write(struct niu *np, int port, int reg, int data)
{
 int err;

 nw64(MIF_FRAME_OUTPUT, MII_WRITE_OP(port, reg, data));
 err = mdio_wait(np);
 if (err < 0)
  return err;

 return 0;
}

static int esr2_set_tx_cfg(struct niu *np, unsigned long channel, u32 val)
{
 int err;

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_TX_CFG_L(channel),
    val & 0xffff);
 if (!err)
  err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
     ESR2_TI_PLL_TX_CFG_H(channel),
     val >> 16);
 return err;
}

static int esr2_set_rx_cfg(struct niu *np, unsigned long channel, u32 val)
{
 int err;

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_RX_CFG_L(channel),
    val & 0xffff);
 if (!err)
  err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
     ESR2_TI_PLL_RX_CFG_H(channel),
     val >> 16);
 return err;
}

/* Mode is always 10G fiber.  */
static int serdes_init_niu_10g_fiber(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 u32 tx_cfg, rx_cfg;
 unsigned long i;

 tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV);
 rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT |
    PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH |
    PLL_RX_CFG_EQ_LP_ADAPTIVE);

 if (lp->loopback_mode == LOOPBACK_PHY) {
  u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS;

  mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
      ESR2_TI_PLL_TEST_CFG_L, test_cfg);

  tx_cfg |= PLL_TX_CFG_ENTEST;
  rx_cfg |= PLL_RX_CFG_ENTEST;
 }

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  int err = esr2_set_tx_cfg(np, i, tx_cfg);
  if (err)
   return err;
 }

 for (i = 0; i < 4; i++) {
  int err = esr2_set_rx_cfg(np, i, rx_cfg);
  if (err)
   return err;
 }

 return 0;
}

static int serdes_init_niu_1g_serdes(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 u16 pll_cfg, pll_sts;
 int max_retry = 100;
 u64 sig, mask, val;
 u32 tx_cfg, rx_cfg;
 unsigned long i;
 int err;

 tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV |
    PLL_TX_CFG_RATE_HALF);
 rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT |
    PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH |
    PLL_RX_CFG_RATE_HALF);

 if (np->port == 0)
  rx_cfg |= PLL_RX_CFG_EQ_LP_ADAPTIVE;

 if (lp->loopback_mode == LOOPBACK_PHY) {
  u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS;

  mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
      ESR2_TI_PLL_TEST_CFG_L, test_cfg);

  tx_cfg |= PLL_TX_CFG_ENTEST;
  rx_cfg |= PLL_RX_CFG_ENTEST;
 }

 /* Initialize PLL for 1G */
 pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_8X);

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_CFG_L, pll_cfg);
 if (err) {
  netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_CFG_L failed\n",
      np->port, __func__);
  return err;
 }

 pll_sts = PLL_CFG_ENPLL;

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_STS_L, pll_sts);
 if (err) {
  netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_STS_L failed\n",
      np->port, __func__);
  return err;
 }

 udelay(200);

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  err = esr2_set_tx_cfg(np, i, tx_cfg);
  if (err)
   return err;
 }

 for (i = 0; i < 4; i++) {
  err = esr2_set_rx_cfg(np, i, rx_cfg);
  if (err)
   return err;
 }

 switch (np->port) {
 case 0:
  val = (ESR_INT_SRDY0_P0 | ESR_INT_DET0_P0);
  mask = val;
  break;

 case 1:
  val = (ESR_INT_SRDY0_P1 | ESR_INT_DET0_P1);
  mask = val;
  break;

 default:
  return -EINVAL;
 }

 while (max_retry--) {
  sig = nr64(ESR_INT_SIGNALS);
  if ((sig & mask) == val)
   break;

  mdelay(500);
 }

 if ((sig & mask) != val) {
  netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n",
      np->port, (int)(sig & mask), (int)val);
  return -ENODEV;
 }

 return 0;
}

static int serdes_init_niu_10g_serdes(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 u32 tx_cfg, rx_cfg, pll_cfg, pll_sts;
 int max_retry = 100;
 u64 sig, mask, val;
 unsigned long i;
 int err;

 tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV);
 rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT |
    PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH |
    PLL_RX_CFG_EQ_LP_ADAPTIVE);

 if (lp->loopback_mode == LOOPBACK_PHY) {
  u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS;

  mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
      ESR2_TI_PLL_TEST_CFG_L, test_cfg);

  tx_cfg |= PLL_TX_CFG_ENTEST;
  rx_cfg |= PLL_RX_CFG_ENTEST;
 }

 /* Initialize PLL for 10G */
 pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_10X);

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_CFG_L, pll_cfg & 0xffff);
 if (err) {
  netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_CFG_L failed\n",
      np->port, __func__);
  return err;
 }

 pll_sts = PLL_CFG_ENPLL;

 err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
    ESR2_TI_PLL_STS_L, pll_sts & 0xffff);
 if (err) {
  netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_STS_L failed\n",
      np->port, __func__);
  return err;
 }

 udelay(200);

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  err = esr2_set_tx_cfg(np, i, tx_cfg);
  if (err)
   return err;
 }

 for (i = 0; i < 4; i++) {
  err = esr2_set_rx_cfg(np, i, rx_cfg);
  if (err)
   return err;
 }

 /* check if serdes is ready */

 switch (np->port) {
 case 0:
  mask = ESR_INT_SIGNALS_P0_BITS;
  val = (ESR_INT_SRDY0_P0 |
         ESR_INT_DET0_P0 |
         ESR_INT_XSRDY_P0 |
         ESR_INT_XDP_P0_CH3 |
         ESR_INT_XDP_P0_CH2 |
         ESR_INT_XDP_P0_CH1 |
         ESR_INT_XDP_P0_CH0);
  break;

 case 1:
  mask = ESR_INT_SIGNALS_P1_BITS;
  val = (ESR_INT_SRDY0_P1 |
         ESR_INT_DET0_P1 |
         ESR_INT_XSRDY_P1 |
         ESR_INT_XDP_P1_CH3 |
         ESR_INT_XDP_P1_CH2 |
         ESR_INT_XDP_P1_CH1 |
         ESR_INT_XDP_P1_CH0);
  break;

 default:
  return -EINVAL;
 }

 while (max_retry--) {
  sig = nr64(ESR_INT_SIGNALS);
  if ((sig & mask) == val)
   break;

  mdelay(500);
 }

 if ((sig & mask) != val) {
  pr_info("NIU Port %u signal bits [%08x] are not [%08x] for 10G...trying 1G\n",
   np->port, (int)(sig & mask), (int)val);

  /* 10G failed, try initializing at 1G */
  err = serdes_init_niu_1g_serdes(np);
  if (!err) {
   np->flags &= ~NIU_FLAGS_10G;
   np->mac_xcvr = MAC_XCVR_PCS;
  }  else {
   netdev_err(np->dev, "Port %u 10G/1G SERDES Link Failed\n",
       np->port);
   return -ENODEV;
  }
 }
 return 0;
}

static int esr_read_rxtx_ctrl(struct niu *np, unsigned long chan, u32 *val)
{
 int err;

 err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, ESR_RXTX_CTRL_L(chan));
 if (err >= 0) {
  *val = (err & 0xffff);
  err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_CTRL_H(chan));
  if (err >= 0)
   *val |= ((err & 0xffff) << 16);
  err = 0;
 }
 return err;
}

static int esr_read_glue0(struct niu *np, unsigned long chan, u32 *val)
{
 int err;

 err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
   ESR_GLUE_CTRL0_L(chan));
 if (err >= 0) {
  *val = (err & 0xffff);
  err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_GLUE_CTRL0_H(chan));
  if (err >= 0) {
   *val |= ((err & 0xffff) << 16);
   err = 0;
  }
 }
 return err;
}

static int esr_read_reset(struct niu *np, u32 *val)
{
 int err;

 err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
   ESR_RXTX_RESET_CTRL_L);
 if (err >= 0) {
  *val = (err & 0xffff);
  err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_RESET_CTRL_H);
  if (err >= 0) {
   *val |= ((err & 0xffff) << 16);
   err = 0;
  }
 }
 return err;
}

static int esr_write_rxtx_ctrl(struct niu *np, unsigned long chan, u32 val)
{
 int err;

 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_CTRL_L(chan), val & 0xffff);
 if (!err)
  err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
     ESR_RXTX_CTRL_H(chan), (val >> 16));
 return err;
}

static int esr_write_glue0(struct niu *np, unsigned long chan, u32 val)
{
 int err;

 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
   ESR_GLUE_CTRL0_L(chan), val & 0xffff);
 if (!err)
  err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
     ESR_GLUE_CTRL0_H(chan), (val >> 16));
 return err;
}

static int esr_reset(struct niu *np)
{
 u32 reset;
 int err;

 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_RESET_CTRL_L, 0x0000);
 if (err)
  return err;
 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_RESET_CTRL_H, 0xffff);
 if (err)
  return err;
 udelay(200);

 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_RESET_CTRL_L, 0xffff);
 if (err)
  return err;
 udelay(200);

 err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
    ESR_RXTX_RESET_CTRL_H, 0x0000);
 if (err)
  return err;
 udelay(200);

 err = esr_read_reset(np, &reset);
 if (err)
  return err;
 if (reset != 0) {
  netdev_err(np->dev, "Port %u ESR_RESET did not clear [%08x]\n",
      np->port, reset);
  return -ENODEV;
 }

 return 0;
}

static int serdes_init_10g(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 unsigned long ctrl_reg, test_cfg_reg, i;
 u64 ctrl_val, test_cfg_val, sig, mask, val;
 int err;

 switch (np->port) {
 case 0:
  ctrl_reg = ENET_SERDES_0_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_0_TEST_CFG;
  break;
 case 1:
  ctrl_reg = ENET_SERDES_1_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_1_TEST_CFG;
  break;

 default:
  return -EINVAL;
 }
 ctrl_val = (ENET_SERDES_CTRL_SDET_0 |
      ENET_SERDES_CTRL_SDET_1 |
      ENET_SERDES_CTRL_SDET_2 |
      ENET_SERDES_CTRL_SDET_3 |
      (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT));
 test_cfg_val = 0;

 if (lp->loopback_mode == LOOPBACK_PHY) {
  test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_0_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_1_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_2_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_3_SHIFT));
 }

 nw64(ctrl_reg, ctrl_val);
 nw64(test_cfg_reg, test_cfg_val);

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  u32 rxtx_ctrl, glue0;

  err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl);
  if (err)
   return err;
  err = esr_read_glue0(np, i, &glue0);
  if (err)
   return err;

  rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO);
  rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH |
         (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT));

  glue0 &= ~(ESR_GLUE_CTRL0_SRATE |
      ESR_GLUE_CTRL0_THCNT |
      ESR_GLUE_CTRL0_BLTIME);
  glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB |
     (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) |
     (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) |
     (BLTIME_300_CYCLES <<
      ESR_GLUE_CTRL0_BLTIME_SHIFT));

  err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl);
  if (err)
   return err;
  err = esr_write_glue0(np, i, glue0);
  if (err)
   return err;
 }

 err = esr_reset(np);
 if (err)
  return err;

 sig = nr64(ESR_INT_SIGNALS);
 switch (np->port) {
 case 0:
  mask = ESR_INT_SIGNALS_P0_BITS;
  val = (ESR_INT_SRDY0_P0 |
         ESR_INT_DET0_P0 |
         ESR_INT_XSRDY_P0 |
         ESR_INT_XDP_P0_CH3 |
         ESR_INT_XDP_P0_CH2 |
         ESR_INT_XDP_P0_CH1 |
         ESR_INT_XDP_P0_CH0);
  break;

 case 1:
  mask = ESR_INT_SIGNALS_P1_BITS;
  val = (ESR_INT_SRDY0_P1 |
         ESR_INT_DET0_P1 |
         ESR_INT_XSRDY_P1 |
         ESR_INT_XDP_P1_CH3 |
         ESR_INT_XDP_P1_CH2 |
         ESR_INT_XDP_P1_CH1 |
         ESR_INT_XDP_P1_CH0);
  break;

 default:
  return -EINVAL;
 }

 if ((sig & mask) != val) {
  if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
   np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
   return 0;
  }
  netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n",
      np->port, (int)(sig & mask), (int)val);
  return -ENODEV;
 }
 if (np->flags & NIU_FLAGS_HOTPLUG_PHY)
  np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT;
 return 0;
}

static int serdes_init_1g(struct niu *np)
{
 u64 val;

 val = nr64(ENET_SERDES_1_PLL_CFG);
 val &= ~ENET_SERDES_PLL_FBDIV2;
 switch (np->port) {
 case 0:
  val |= ENET_SERDES_PLL_HRATE0;
  break;
 case 1:
  val |= ENET_SERDES_PLL_HRATE1;
  break;
 case 2:
  val |= ENET_SERDES_PLL_HRATE2;
  break;
 case 3:
  val |= ENET_SERDES_PLL_HRATE3;
  break;
 default:
  return -EINVAL;
 }
 nw64(ENET_SERDES_1_PLL_CFG, val);

 return 0;
}

static int serdes_init_1g_serdes(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 unsigned long ctrl_reg, test_cfg_reg, pll_cfg, i;
 u64 ctrl_val, test_cfg_val, sig, mask, val;
 int err;
 u64 reset_val, val_rd;

 val = ENET_SERDES_PLL_HRATE0 | ENET_SERDES_PLL_HRATE1 |
  ENET_SERDES_PLL_HRATE2 | ENET_SERDES_PLL_HRATE3 |
  ENET_SERDES_PLL_FBDIV0;
 switch (np->port) {
 case 0:
  reset_val =  ENET_SERDES_RESET_0;
  ctrl_reg = ENET_SERDES_0_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_0_TEST_CFG;
  pll_cfg = ENET_SERDES_0_PLL_CFG;
  break;
 case 1:
  reset_val =  ENET_SERDES_RESET_1;
  ctrl_reg = ENET_SERDES_1_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_1_TEST_CFG;
  pll_cfg = ENET_SERDES_1_PLL_CFG;
  break;

 default:
  return -EINVAL;
 }
 ctrl_val = (ENET_SERDES_CTRL_SDET_0 |
      ENET_SERDES_CTRL_SDET_1 |
      ENET_SERDES_CTRL_SDET_2 |
      ENET_SERDES_CTRL_SDET_3 |
      (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT));
 test_cfg_val = 0;

 if (lp->loopback_mode == LOOPBACK_PHY) {
  test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_0_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_1_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_2_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_3_SHIFT));
 }

 nw64(ENET_SERDES_RESET, reset_val);
 mdelay(20);
 val_rd = nr64(ENET_SERDES_RESET);
 val_rd &= ~reset_val;
 nw64(pll_cfg, val);
 nw64(ctrl_reg, ctrl_val);
 nw64(test_cfg_reg, test_cfg_val);
 nw64(ENET_SERDES_RESET, val_rd);
 mdelay(2000);

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  u32 rxtx_ctrl, glue0;

  err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl);
  if (err)
   return err;
  err = esr_read_glue0(np, i, &glue0);
  if (err)
   return err;

  rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO);
  rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH |
         (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT));

  glue0 &= ~(ESR_GLUE_CTRL0_SRATE |
      ESR_GLUE_CTRL0_THCNT |
      ESR_GLUE_CTRL0_BLTIME);
  glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB |
     (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) |
     (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) |
     (BLTIME_300_CYCLES <<
      ESR_GLUE_CTRL0_BLTIME_SHIFT));

  err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl);
  if (err)
   return err;
  err = esr_write_glue0(np, i, glue0);
  if (err)
   return err;
 }


 sig = nr64(ESR_INT_SIGNALS);
 switch (np->port) {
 case 0:
  val = (ESR_INT_SRDY0_P0 | ESR_INT_DET0_P0);
  mask = val;
  break;

 case 1:
  val = (ESR_INT_SRDY0_P1 | ESR_INT_DET0_P1);
  mask = val;
  break;

 default:
  return -EINVAL;
 }

 if ((sig & mask) != val) {
  netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n",
      np->port, (int)(sig & mask), (int)val);
  return -ENODEV;
 }

 return 0;
}

static int link_status_1g_serdes(struct niu *np, int *link_up_p)
{
 struct niu_link_config *lp = &np->link_config;
 int link_up;
 u64 val;
 u16 current_speed;
 unsigned long flags;
 u8 current_duplex;

 link_up = 0;
 current_speed = SPEED_INVALID;
 current_duplex = DUPLEX_INVALID;

 spin_lock_irqsave(&np->lock, flags);

 val = nr64_pcs(PCS_MII_STAT);

 if (val & PCS_MII_STAT_LINK_STATUS) {
  link_up = 1;
  current_speed = SPEED_1000;
  current_duplex = DUPLEX_FULL;
 }

 lp->active_speed = current_speed;
 lp->active_duplex = current_duplex;
 spin_unlock_irqrestore(&np->lock, flags);

 *link_up_p = link_up;
 return 0;
}

static int link_status_10g_serdes(struct niu *np, int *link_up_p)
{
 unsigned long flags;
 struct niu_link_config *lp = &np->link_config;
 int link_up = 0;
 int link_ok = 1;
 u64 val, val2;
 u16 current_speed;
 u8 current_duplex;

 if (!(np->flags & NIU_FLAGS_10G))
  return link_status_1g_serdes(np, link_up_p);

 current_speed = SPEED_INVALID;
 current_duplex = DUPLEX_INVALID;
 spin_lock_irqsave(&np->lock, flags);

 val = nr64_xpcs(XPCS_STATUS(0));
 val2 = nr64_mac(XMAC_INTER2);
 if (val2 & 0x01000000)
  link_ok = 0;

 if ((val & 0x1000ULL) && link_ok) {
  link_up = 1;
  current_speed = SPEED_10000;
  current_duplex = DUPLEX_FULL;
 }
 lp->active_speed = current_speed;
 lp->active_duplex = current_duplex;
 spin_unlock_irqrestore(&np->lock, flags);
 *link_up_p = link_up;
 return 0;
}

static int link_status_mii(struct niu *np, int *link_up_p)
{
 struct niu_link_config *lp = &np->link_config;
 int err;
 int bmsr, advert, ctrl1000, stat1000, lpa, bmcr, estatus;
 int supported, advertising, active_speed, active_duplex;

 err = mii_read(np, np->phy_addr, MII_BMCR);
 if (unlikely(err < 0))
  return err;
 bmcr = err;

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (unlikely(err < 0))
  return err;
 bmsr = err;

 err = mii_read(np, np->phy_addr, MII_ADVERTISE);
 if (unlikely(err < 0))
  return err;
 advert = err;

 err = mii_read(np, np->phy_addr, MII_LPA);
 if (unlikely(err < 0))
  return err;
 lpa = err;

 if (likely(bmsr & BMSR_ESTATEN)) {
  err = mii_read(np, np->phy_addr, MII_ESTATUS);
  if (unlikely(err < 0))
   return err;
  estatus = err;

  err = mii_read(np, np->phy_addr, MII_CTRL1000);
  if (unlikely(err < 0))
   return err;
  ctrl1000 = err;

  err = mii_read(np, np->phy_addr, MII_STAT1000);
  if (unlikely(err < 0))
   return err;
  stat1000 = err;
 } else
  estatus = ctrl1000 = stat1000 = 0;

 supported = 0;
 if (bmsr & BMSR_ANEGCAPABLE)
  supported |= SUPPORTED_Autoneg;
 if (bmsr & BMSR_10HALF)
  supported |= SUPPORTED_10baseT_Half;
 if (bmsr & BMSR_10FULL)
  supported |= SUPPORTED_10baseT_Full;
 if (bmsr & BMSR_100HALF)
  supported |= SUPPORTED_100baseT_Half;
 if (bmsr & BMSR_100FULL)
  supported |= SUPPORTED_100baseT_Full;
 if (estatus & ESTATUS_1000_THALF)
  supported |= SUPPORTED_1000baseT_Half;
 if (estatus & ESTATUS_1000_TFULL)
  supported |= SUPPORTED_1000baseT_Full;
 lp->supported = supported;

 advertising = mii_adv_to_ethtool_adv_t(advert);
 advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);

 if (bmcr & BMCR_ANENABLE) {
  int neg, neg1000;

  lp->active_autoneg = 1;
  advertising |= ADVERTISED_Autoneg;

  neg = advert & lpa;
  neg1000 = (ctrl1000 << 2) & stat1000;

  if (neg1000 & (LPA_1000FULL | LPA_1000HALF))
   active_speed = SPEED_1000;
  else if (neg & LPA_100)
   active_speed = SPEED_100;
  else if (neg & (LPA_10HALF | LPA_10FULL))
   active_speed = SPEED_10;
  else
   active_speed = SPEED_INVALID;

  if ((neg1000 & LPA_1000FULL) || (neg & LPA_DUPLEX))
   active_duplex = DUPLEX_FULL;
  else if (active_speed != SPEED_INVALID)
   active_duplex = DUPLEX_HALF;
  else
   active_duplex = DUPLEX_INVALID;
 } else {
  lp->active_autoneg = 0;

  if ((bmcr & BMCR_SPEED1000) && !(bmcr & BMCR_SPEED100))
   active_speed = SPEED_1000;
  else if (bmcr & BMCR_SPEED100)
   active_speed = SPEED_100;
  else
   active_speed = SPEED_10;

  if (bmcr & BMCR_FULLDPLX)
   active_duplex = DUPLEX_FULL;
  else
   active_duplex = DUPLEX_HALF;
 }

 lp->active_advertising = advertising;
 lp->active_speed = active_speed;
 lp->active_duplex = active_duplex;
 *link_up_p = !!(bmsr & BMSR_LSTATUS);

 return 0;
}

static int link_status_1g_rgmii(struct niu *np, int *link_up_p)
{
 struct niu_link_config *lp = &np->link_config;
 u16 current_speed, bmsr;
 unsigned long flags;
 u8 current_duplex;
 int err, link_up;

 link_up = 0;
 current_speed = SPEED_INVALID;
 current_duplex = DUPLEX_INVALID;

 spin_lock_irqsave(&np->lock, flags);

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (err < 0)
  goto out;

 bmsr = err;
 if (bmsr & BMSR_LSTATUS) {
  link_up = 1;
  current_speed = SPEED_1000;
  current_duplex = DUPLEX_FULL;
 }
 lp->active_speed = current_speed;
 lp->active_duplex = current_duplex;
 err = 0;

out:
 spin_unlock_irqrestore(&np->lock, flags);

 *link_up_p = link_up;
 return err;
}

static int link_status_1g(struct niu *np, int *link_up_p)
{
 struct niu_link_config *lp = &np->link_config;
 unsigned long flags;
 int err;

 spin_lock_irqsave(&np->lock, flags);

 err = link_status_mii(np, link_up_p);
 lp->supported |= SUPPORTED_TP;
 lp->active_advertising |= ADVERTISED_TP;

 spin_unlock_irqrestore(&np->lock, flags);
 return err;
}

static int bcm8704_reset(struct niu *np)
{
 int err, limit;

 err = mdio_read(np, np->phy_addr,
   BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
 if (err < 0 || err == 0xffff)
  return err;
 err |= BMCR_RESET;
 err = mdio_write(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
    MII_BMCR, err);
 if (err)
  return err;

 limit = 1000;
 while (--limit >= 0) {
  err = mdio_read(np, np->phy_addr,
    BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
  if (err < 0)
   return err;
  if (!(err & BMCR_RESET))
   break;
 }
 if (limit < 0) {
  netdev_err(np->dev, "Port %u PHY will not reset (bmcr=%04x)\n",
      np->port, (err & 0xffff));
  return -ENODEV;
 }
 return 0;
}

/* When written, certain PHY registers need to be read back twice
 * in order for the bits to settle properly.
 */

static int bcm8704_user_dev3_readback(struct niu *np, int reg)
{
 int err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
 if (err < 0)
  return err;
 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
 if (err < 0)
  return err;
 return 0;
}

static int bcm8706_init_user_dev3(struct niu *np)
{
 int err;


 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_OPT_DIGITAL_CTRL);
 if (err < 0)
  return err;
 err &= ~USER_ODIG_CTRL_GPIOS;
 err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT);
 err |=  USER_ODIG_CTRL_RESV2;
 err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
    BCM8704_USER_OPT_DIGITAL_CTRL, err);
 if (err)
  return err;

 mdelay(1000);

 return 0;
}

static int bcm8704_init_user_dev3(struct niu *np)
{
 int err;

 err = mdio_write(np, np->phy_addr,
    BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL,
    (USER_CONTROL_OPTXRST_LVL |
     USER_CONTROL_OPBIASFLT_LVL |
     USER_CONTROL_OBTMPFLT_LVL |
     USER_CONTROL_OPPRFLT_LVL |
     USER_CONTROL_OPTXFLT_LVL |
     USER_CONTROL_OPRXLOS_LVL |
     USER_CONTROL_OPRXFLT_LVL |
     USER_CONTROL_OPTXON_LVL |
     (0x3f << USER_CONTROL_RES1_SHIFT)));
 if (err)
  return err;

 err = mdio_write(np, np->phy_addr,
    BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL,
    (USER_PMD_TX_CTL_XFP_CLKEN |
     (1 << USER_PMD_TX_CTL_TX_DAC_TXD_SH) |
     (2 << USER_PMD_TX_CTL_TX_DAC_TXCK_SH) |
     USER_PMD_TX_CTL_TSCK_LPWREN));
 if (err)
  return err;

 err = bcm8704_user_dev3_readback(np, BCM8704_USER_CONTROL);
 if (err)
  return err;
 err = bcm8704_user_dev3_readback(np, BCM8704_USER_PMD_TX_CONTROL);
 if (err)
  return err;

 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_OPT_DIGITAL_CTRL);
 if (err < 0)
  return err;
 err &= ~USER_ODIG_CTRL_GPIOS;
 err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT);
 err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
    BCM8704_USER_OPT_DIGITAL_CTRL, err);
 if (err)
  return err;

 mdelay(1000);

 return 0;
}

static int mrvl88x2011_act_led(struct niu *np, int val)
{
 int err;

 err  = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
  MRVL88X2011_LED_8_TO_11_CTL);
 if (err < 0)
  return err;

 err &= ~MRVL88X2011_LED(MRVL88X2011_LED_ACT,MRVL88X2011_LED_CTL_MASK);
 err |=  MRVL88X2011_LED(MRVL88X2011_LED_ACT,val);

 return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
     MRVL88X2011_LED_8_TO_11_CTL, err);
}

static int mrvl88x2011_led_blink_rate(struct niu *np, int rate)
{
 int err;

 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
   MRVL88X2011_LED_BLINK_CTL);
 if (err >= 0) {
  err &= ~MRVL88X2011_LED_BLKRATE_MASK;
  err |= (rate << 4);

  err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
     MRVL88X2011_LED_BLINK_CTL, err);
 }

 return err;
}

static int xcvr_init_10g_mrvl88x2011(struct niu *np)
{
 int err;

 /* Set LED functions */
 err = mrvl88x2011_led_blink_rate(np, MRVL88X2011_LED_BLKRATE_134MS);
 if (err)
  return err;

 /* led activity */
 err = mrvl88x2011_act_led(np, MRVL88X2011_LED_CTL_OFF);
 if (err)
  return err;

 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
   MRVL88X2011_GENERAL_CTL);
 if (err < 0)
  return err;

 err |= MRVL88X2011_ENA_XFPREFCLK;

 err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
    MRVL88X2011_GENERAL_CTL, err);
 if (err < 0)
  return err;

 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
   MRVL88X2011_PMA_PMD_CTL_1);
 if (err < 0)
  return err;

 if (np->link_config.loopback_mode == LOOPBACK_MAC)
  err |= MRVL88X2011_LOOPBACK;
 else
  err &= ~MRVL88X2011_LOOPBACK;

 err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
    MRVL88X2011_PMA_PMD_CTL_1, err);
 if (err < 0)
  return err;

 /* Enable PMD  */
 return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
     MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX);
}


static int xcvr_diag_bcm870x(struct niu *np)
{
 u16 analog_stat0, tx_alarm_status;
 int err = 0;

#if 1
 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
   MII_STAT1000);
 if (err < 0)
  return err;
 pr_info("Port %u PMA_PMD(MII_STAT1000) [%04x]\n", np->port, err);

 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 0x20);
 if (err < 0)
  return err;
 pr_info("Port %u USER_DEV3(0x20) [%04x]\n", np->port, err);

 err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
   MII_NWAYTEST);
 if (err < 0)
  return err;
 pr_info("Port %u PHYXS(MII_NWAYTEST) [%04x]\n", np->port, err);
#endif

 /* XXX dig this out it might not be so useful XXX */
 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_ANALOG_STATUS0);
 if (err < 0)
  return err;
 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_ANALOG_STATUS0);
 if (err < 0)
  return err;
 analog_stat0 = err;

 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_TX_ALARM_STATUS);
 if (err < 0)
  return err;
 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
   BCM8704_USER_TX_ALARM_STATUS);
 if (err < 0)
  return err;
 tx_alarm_status = err;

 if (analog_stat0 != 0x03fc) {
  if ((analog_stat0 == 0x43bc) && (tx_alarm_status != 0)) {
   pr_info("Port %u cable not connected or bad cable\n",
    np->port);
  } else if (analog_stat0 == 0x639c) {
   pr_info("Port %u optical module is bad or missing\n",
    np->port);
  }
 }

 return 0;
}

static int xcvr_10g_set_lb_bcm870x(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 int err;

 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
   MII_BMCR);
 if (err < 0)
  return err;

 err &= ~BMCR_LOOPBACK;

 if (lp->loopback_mode == LOOPBACK_MAC)
  err |= BMCR_LOOPBACK;

 err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
    MII_BMCR, err);
 if (err)
  return err;

 return 0;
}

static int xcvr_init_10g_bcm8706(struct niu *np)
{
 int err = 0;
 u64 val;

 if ((np->flags & NIU_FLAGS_HOTPLUG_PHY) &&
     (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) == 0)
   return err;

 val = nr64_mac(XMAC_CONFIG);
 val &= ~XMAC_CONFIG_LED_POLARITY;
 val |= XMAC_CONFIG_FORCE_LED_ON;
 nw64_mac(XMAC_CONFIG, val);

 val = nr64(MIF_CONFIG);
 val |= MIF_CONFIG_INDIRECT_MODE;
 nw64(MIF_CONFIG, val);

 err = bcm8704_reset(np);
 if (err)
  return err;

 err = xcvr_10g_set_lb_bcm870x(np);
 if (err)
  return err;

 err = bcm8706_init_user_dev3(np);
 if (err)
  return err;

 err = xcvr_diag_bcm870x(np);
 if (err)
  return err;

 return 0;
}

static int xcvr_init_10g_bcm8704(struct niu *np)
{
 int err;

 err = bcm8704_reset(np);
 if (err)
  return err;

 err = bcm8704_init_user_dev3(np);
 if (err)
  return err;

 err = xcvr_10g_set_lb_bcm870x(np);
 if (err)
  return err;

 err =  xcvr_diag_bcm870x(np);
 if (err)
  return err;

 return 0;
}

static int xcvr_init_10g(struct niu *np)
{
 int phy_id, err;
 u64 val;

 val = nr64_mac(XMAC_CONFIG);
 val &= ~XMAC_CONFIG_LED_POLARITY;
 val |= XMAC_CONFIG_FORCE_LED_ON;
 nw64_mac(XMAC_CONFIG, val);

 /* XXX shared resource, lock parent XXX */
 val = nr64(MIF_CONFIG);
 val |= MIF_CONFIG_INDIRECT_MODE;
 nw64(MIF_CONFIG, val);

 phy_id = phy_decode(np->parent->port_phy, np->port);
 phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port];

 /* handle different phy types */
 switch (phy_id & NIU_PHY_ID_MASK) {
 case NIU_PHY_ID_MRVL88X2011:
  err = xcvr_init_10g_mrvl88x2011(np);
  break;

 default/* bcom 8704 */
  err = xcvr_init_10g_bcm8704(np);
  break;
 }

 return err;
}

static int mii_reset(struct niu *np)
{
 int limit, err;

 err = mii_write(np, np->phy_addr, MII_BMCR, BMCR_RESET);
 if (err)
  return err;

 limit = 1000;
 while (--limit >= 0) {
  udelay(500);
  err = mii_read(np, np->phy_addr, MII_BMCR);
  if (err < 0)
   return err;
  if (!(err & BMCR_RESET))
   break;
 }
 if (limit < 0) {
  netdev_err(np->dev, "Port %u MII would not reset, bmcr[%04x]\n",
      np->port, err);
  return -ENODEV;
 }

 return 0;
}

static int xcvr_init_1g_rgmii(struct niu *np)
{
 int err;
 u64 val;
 u16 bmcr, bmsr, estat;

 val = nr64(MIF_CONFIG);
 val &= ~MIF_CONFIG_INDIRECT_MODE;
 nw64(MIF_CONFIG, val);

 err = mii_reset(np);
 if (err)
  return err;

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (err < 0)
  return err;
 bmsr = err;

 estat = 0;
 if (bmsr & BMSR_ESTATEN) {
  err = mii_read(np, np->phy_addr, MII_ESTATUS);
  if (err < 0)
   return err;
  estat = err;
 }

 bmcr = 0;
 err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
 if (err)
  return err;

 if (bmsr & BMSR_ESTATEN) {
  u16 ctrl1000 = 0;

  if (estat & ESTATUS_1000_TFULL)
   ctrl1000 |= ADVERTISE_1000FULL;
  err = mii_write(np, np->phy_addr, MII_CTRL1000, ctrl1000);
  if (err)
   return err;
 }

 bmcr = (BMCR_SPEED1000 | BMCR_FULLDPLX);

 err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
 if (err)
  return err;

 err = mii_read(np, np->phy_addr, MII_BMCR);
 if (err < 0)
  return err;
 bmcr = mii_read(np, np->phy_addr, MII_BMCR);

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (err < 0)
  return err;

 return 0;
}

static int mii_init_common(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 u16 bmcr, bmsr, adv, estat;
 int err;

 err = mii_reset(np);
 if (err)
  return err;

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (err < 0)
  return err;
 bmsr = err;

 estat = 0;
 if (bmsr & BMSR_ESTATEN) {
  err = mii_read(np, np->phy_addr, MII_ESTATUS);
  if (err < 0)
   return err;
  estat = err;
 }

 bmcr = 0;
 err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
 if (err)
  return err;

 if (lp->loopback_mode == LOOPBACK_MAC) {
  bmcr |= BMCR_LOOPBACK;
  if (lp->active_speed == SPEED_1000)
   bmcr |= BMCR_SPEED1000;
  if (lp->active_duplex == DUPLEX_FULL)
   bmcr |= BMCR_FULLDPLX;
 }

 if (lp->loopback_mode == LOOPBACK_PHY) {
  u16 aux;

  aux = (BCM5464R_AUX_CTL_EXT_LB |
         BCM5464R_AUX_CTL_WRITE_1);
  err = mii_write(np, np->phy_addr, BCM5464R_AUX_CTL, aux);
  if (err)
   return err;
 }

 if (lp->autoneg) {
  u16 ctrl1000;

  adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP;
  if ((bmsr & BMSR_10HALF) &&
   (lp->advertising & ADVERTISED_10baseT_Half))
   adv |= ADVERTISE_10HALF;
  if ((bmsr & BMSR_10FULL) &&
   (lp->advertising & ADVERTISED_10baseT_Full))
   adv |= ADVERTISE_10FULL;
  if ((bmsr & BMSR_100HALF) &&
   (lp->advertising & ADVERTISED_100baseT_Half))
   adv |= ADVERTISE_100HALF;
  if ((bmsr & BMSR_100FULL) &&
   (lp->advertising & ADVERTISED_100baseT_Full))
   adv |= ADVERTISE_100FULL;
  err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv);
  if (err)
   return err;

  if (likely(bmsr & BMSR_ESTATEN)) {
   ctrl1000 = 0;
   if ((estat & ESTATUS_1000_THALF) &&
    (lp->advertising & ADVERTISED_1000baseT_Half))
    ctrl1000 |= ADVERTISE_1000HALF;
   if ((estat & ESTATUS_1000_TFULL) &&
    (lp->advertising & ADVERTISED_1000baseT_Full))
    ctrl1000 |= ADVERTISE_1000FULL;
   err = mii_write(np, np->phy_addr,
     MII_CTRL1000, ctrl1000);
   if (err)
    return err;
  }

  bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
 } else {
  /* !lp->autoneg */
  int fulldpx;

  if (lp->duplex == DUPLEX_FULL) {
   bmcr |= BMCR_FULLDPLX;
   fulldpx = 1;
  } else if (lp->duplex == DUPLEX_HALF)
   fulldpx = 0;
  else
   return -EINVAL;

  if (lp->speed == SPEED_1000) {
   /* if X-full requested while not supported, or
   X-half requested while not supported... */

   if ((fulldpx && !(estat & ESTATUS_1000_TFULL)) ||
    (!fulldpx && !(estat & ESTATUS_1000_THALF)))
    return -EINVAL;
   bmcr |= BMCR_SPEED1000;
  } else if (lp->speed == SPEED_100) {
   if ((fulldpx && !(bmsr & BMSR_100FULL)) ||
    (!fulldpx && !(bmsr & BMSR_100HALF)))
    return -EINVAL;
   bmcr |= BMCR_SPEED100;
  } else if (lp->speed == SPEED_10) {
   if ((fulldpx && !(bmsr & BMSR_10FULL)) ||
    (!fulldpx && !(bmsr & BMSR_10HALF)))
    return -EINVAL;
  } else
   return -EINVAL;
 }

 err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
 if (err)
  return err;

#if 0
 err = mii_read(np, np->phy_addr, MII_BMCR);
 if (err < 0)
  return err;
 bmcr = err;

 err = mii_read(np, np->phy_addr, MII_BMSR);
 if (err < 0)
  return err;
 bmsr = err;

 pr_info("Port %u after MII init bmcr[%04x] bmsr[%04x]\n",
  np->port, bmcr, bmsr);
#endif

 return 0;
}

static int xcvr_init_1g(struct niu *np)
{
 u64 val;

 /* XXX shared resource, lock parent XXX */
 val = nr64(MIF_CONFIG);
 val &= ~MIF_CONFIG_INDIRECT_MODE;
 nw64(MIF_CONFIG, val);

 return mii_init_common(np);
}

static int niu_xcvr_init(struct niu *np)
{
 const struct niu_phy_ops *ops = np->phy_ops;
 int err;

 err = 0;
 if (ops->xcvr_init)
  err = ops->xcvr_init(np);

 return err;
}

static int niu_serdes_init(struct niu *np)
{
 const struct niu_phy_ops *ops = np->phy_ops;
 int err;

 err = 0;
 if (ops->serdes_init)
  err = ops->serdes_init(np);

 return err;
}

static void niu_init_xif(struct niu *);
static void niu_handle_led(struct niu *, int status);

static int niu_link_status_common(struct niu *np, int link_up)
{
 struct niu_link_config *lp = &np->link_config;
 struct net_device *dev = np->dev;
 unsigned long flags;

 if (!netif_carrier_ok(dev) && link_up) {
  netif_info(np, link, dev, "Link is up at %s, %s duplex\n",
      lp->active_speed == SPEED_10000 ? "10Gb/sec" :
      lp->active_speed == SPEED_1000 ? "1Gb/sec" :
      lp->active_speed == SPEED_100 ? "100Mbit/sec" :
      "10Mbit/sec",
      lp->active_duplex == DUPLEX_FULL ? "full" : "half");

  spin_lock_irqsave(&np->lock, flags);
  niu_init_xif(np);
  niu_handle_led(np, 1);
  spin_unlock_irqrestore(&np->lock, flags);

  netif_carrier_on(dev);
 } else if (netif_carrier_ok(dev) && !link_up) {
  netif_warn(np, link, dev, "Link is down\n");
  spin_lock_irqsave(&np->lock, flags);
  niu_handle_led(np, 0);
  spin_unlock_irqrestore(&np->lock, flags);
  netif_carrier_off(dev);
 }

 return 0;
}

static int link_status_10g_mrvl(struct niu *np, int *link_up_p)
{
 int err, link_up, pma_status, pcs_status;

 link_up = 0;

 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
   MRVL88X2011_10G_PMD_STATUS_2);
 if (err < 0)
  goto out;

 /* Check PMA/PMD Register: 1.0001.2 == 1 */
 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
   MRVL88X2011_PMA_PMD_STATUS_1);
 if (err < 0)
  goto out;

 pma_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0);

        /* Check PMC Register : 3.0001.2 == 1: read twice */
 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
   MRVL88X2011_PMA_PMD_STATUS_1);
 if (err < 0)
  goto out;

 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
   MRVL88X2011_PMA_PMD_STATUS_1);
 if (err < 0)
  goto out;

 pcs_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0);

        /* Check XGXS Register : 4.0018.[0-3,12] */
 err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV4_ADDR,
   MRVL88X2011_10G_XGXS_LANE_STAT);
 if (err < 0)
  goto out;

 if (err == (PHYXS_XGXS_LANE_STAT_ALINGED | PHYXS_XGXS_LANE_STAT_LANE3 |
      PHYXS_XGXS_LANE_STAT_LANE2 | PHYXS_XGXS_LANE_STAT_LANE1 |
      PHYXS_XGXS_LANE_STAT_LANE0 | PHYXS_XGXS_LANE_STAT_MAGIC |
      0x800))
  link_up = (pma_status && pcs_status) ? 1 : 0;

 np->link_config.active_speed = SPEED_10000;
 np->link_config.active_duplex = DUPLEX_FULL;
 err = 0;
out:
 mrvl88x2011_act_led(np, (link_up ?
     MRVL88X2011_LED_CTL_PCS_ACT :
     MRVL88X2011_LED_CTL_OFF));

 *link_up_p = link_up;
 return err;
}

static int link_status_10g_bcm8706(struct niu *np, int *link_up_p)
{
 int err, link_up;
 link_up = 0;

 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
   BCM8704_PMD_RCV_SIGDET);
 if (err < 0 || err == 0xffff)
  goto out;
 if (!(err & PMD_RCV_SIGDET_GLOBAL)) {
  err = 0;
  goto out;
 }

 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
   BCM8704_PCS_10G_R_STATUS);
 if (err < 0)
  goto out;

 if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) {
  err = 0;
  goto out;
 }

 err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
   BCM8704_PHYXS_XGXS_LANE_STAT);
 if (err < 0)
  goto out;
 if (err != (PHYXS_XGXS_LANE_STAT_ALINGED |
      PHYXS_XGXS_LANE_STAT_MAGIC |
      PHYXS_XGXS_LANE_STAT_PATTEST |
      PHYXS_XGXS_LANE_STAT_LANE3 |
      PHYXS_XGXS_LANE_STAT_LANE2 |
      PHYXS_XGXS_LANE_STAT_LANE1 |
      PHYXS_XGXS_LANE_STAT_LANE0)) {
  err = 0;
  np->link_config.active_speed = SPEED_INVALID;
  np->link_config.active_duplex = DUPLEX_INVALID;
  goto out;
 }

 link_up = 1;
 np->link_config.active_speed = SPEED_10000;
 np->link_config.active_duplex = DUPLEX_FULL;
 err = 0;

out:
 *link_up_p = link_up;
 return err;
}

static int link_status_10g_bcom(struct niu *np, int *link_up_p)
{
 int err, link_up;

 link_up = 0;

 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
   BCM8704_PMD_RCV_SIGDET);
 if (err < 0)
  goto out;
 if (!(err & PMD_RCV_SIGDET_GLOBAL)) {
  err = 0;
  goto out;
 }

 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
   BCM8704_PCS_10G_R_STATUS);
 if (err < 0)
  goto out;
 if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) {
  err = 0;
  goto out;
 }

 err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
   BCM8704_PHYXS_XGXS_LANE_STAT);
 if (err < 0)
  goto out;

 if (err != (PHYXS_XGXS_LANE_STAT_ALINGED |
      PHYXS_XGXS_LANE_STAT_MAGIC |
      PHYXS_XGXS_LANE_STAT_LANE3 |
      PHYXS_XGXS_LANE_STAT_LANE2 |
      PHYXS_XGXS_LANE_STAT_LANE1 |
      PHYXS_XGXS_LANE_STAT_LANE0)) {
  err = 0;
  goto out;
 }

 link_up = 1;
 np->link_config.active_speed = SPEED_10000;
 np->link_config.active_duplex = DUPLEX_FULL;
 err = 0;

out:
 *link_up_p = link_up;
 return err;
}

static int link_status_10g(struct niu *np, int *link_up_p)
{
 unsigned long flags;
 int err = -EINVAL;

 spin_lock_irqsave(&np->lock, flags);

 if (np->link_config.loopback_mode == LOOPBACK_DISABLED) {
  int phy_id;

  phy_id = phy_decode(np->parent->port_phy, np->port);
  phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port];

  /* handle different phy types */
  switch (phy_id & NIU_PHY_ID_MASK) {
  case NIU_PHY_ID_MRVL88X2011:
   err = link_status_10g_mrvl(np, link_up_p);
   break;

  default/* bcom 8704 */
   err = link_status_10g_bcom(np, link_up_p);
   break;
  }
 }

 spin_unlock_irqrestore(&np->lock, flags);

 return err;
}

static int niu_10g_phy_present(struct niu *np)
{
 u64 sig, mask, val;

 sig = nr64(ESR_INT_SIGNALS);
 switch (np->port) {
 case 0:
  mask = ESR_INT_SIGNALS_P0_BITS;
  val = (ESR_INT_SRDY0_P0 |
         ESR_INT_DET0_P0 |
         ESR_INT_XSRDY_P0 |
         ESR_INT_XDP_P0_CH3 |
         ESR_INT_XDP_P0_CH2 |
         ESR_INT_XDP_P0_CH1 |
         ESR_INT_XDP_P0_CH0);
  break;

 case 1:
  mask = ESR_INT_SIGNALS_P1_BITS;
  val = (ESR_INT_SRDY0_P1 |
         ESR_INT_DET0_P1 |
         ESR_INT_XSRDY_P1 |
         ESR_INT_XDP_P1_CH3 |
         ESR_INT_XDP_P1_CH2 |
         ESR_INT_XDP_P1_CH1 |
         ESR_INT_XDP_P1_CH0);
  break;

 default:
  return 0;
 }

 if ((sig & mask) != val)
  return 0;
 return 1;
}

static int link_status_10g_hotplug(struct niu *np, int *link_up_p)
{
 unsigned long flags;
 int err = 0;
 int phy_present;
 int phy_present_prev;

 spin_lock_irqsave(&np->lock, flags);

 if (np->link_config.loopback_mode == LOOPBACK_DISABLED) {
  phy_present_prev = (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) ?
   1 : 0;
  phy_present = niu_10g_phy_present(np);
  if (phy_present != phy_present_prev) {
   /* state change */
   if (phy_present) {
    /* A NEM was just plugged in */
    np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT;
    if (np->phy_ops->xcvr_init)
     err = np->phy_ops->xcvr_init(np);
    if (err) {
     err = mdio_read(np, np->phy_addr,
      BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
     if (err == 0xffff) {
      /* No mdio, back-to-back XAUI */
      goto out;
     }
     /* debounce */
     np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
    }
   } else {
    np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
    *link_up_p = 0;
    netif_warn(np, link, np->dev,
        "Hotplug PHY Removed\n");
   }
  }
out:
  if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) {
   err = link_status_10g_bcm8706(np, link_up_p);
   if (err == 0xffff) {
    /* No mdio, back-to-back XAUI: it is C10NEM */
    *link_up_p = 1;
    np->link_config.active_speed = SPEED_10000;
    np->link_config.active_duplex = DUPLEX_FULL;
   }
  }
 }

 spin_unlock_irqrestore(&np->lock, flags);

 return 0;
}

static int niu_link_status(struct niu *np, int *link_up_p)
{
 const struct niu_phy_ops *ops = np->phy_ops;
 int err;

 err = 0;
 if (ops->link_status)
  err = ops->link_status(np, link_up_p);

 return err;
}

static void niu_timer(struct timer_list *t)
{
 struct niu *np = timer_container_of(np, t, timer);
 unsigned long off;
 int err, link_up;

 err = niu_link_status(np, &link_up);
 if (!err)
  niu_link_status_common(np, link_up);

 if (netif_carrier_ok(np->dev))
  off = 5 * HZ;
 else
  off = 1 * HZ;
 np->timer.expires = jiffies + off;

 add_timer(&np->timer);
}

static const struct niu_phy_ops phy_ops_10g_serdes = {
 .serdes_init  = serdes_init_10g_serdes,
 .link_status  = link_status_10g_serdes,
};

static const struct niu_phy_ops phy_ops_10g_serdes_niu = {
 .serdes_init  = serdes_init_niu_10g_serdes,
 .link_status  = link_status_10g_serdes,
};

static const struct niu_phy_ops phy_ops_1g_serdes_niu = {
 .serdes_init  = serdes_init_niu_1g_serdes,
 .link_status  = link_status_1g_serdes,
};

static const struct niu_phy_ops phy_ops_1g_rgmii = {
 .xcvr_init  = xcvr_init_1g_rgmii,
 .link_status  = link_status_1g_rgmii,
};

static const struct niu_phy_ops phy_ops_10g_fiber_niu = {
 .serdes_init  = serdes_init_niu_10g_fiber,
 .xcvr_init  = xcvr_init_10g,
 .link_status  = link_status_10g,
};

static const struct niu_phy_ops phy_ops_10g_fiber = {
 .serdes_init  = serdes_init_10g,
 .xcvr_init  = xcvr_init_10g,
 .link_status  = link_status_10g,
};

static const struct niu_phy_ops phy_ops_10g_fiber_hotplug = {
 .serdes_init  = serdes_init_10g,
 .xcvr_init  = xcvr_init_10g_bcm8706,
 .link_status  = link_status_10g_hotplug,
};

static const struct niu_phy_ops phy_ops_niu_10g_hotplug = {
 .serdes_init  = serdes_init_niu_10g_fiber,
 .xcvr_init  = xcvr_init_10g_bcm8706,
 .link_status  = link_status_10g_hotplug,
};

static const struct niu_phy_ops phy_ops_10g_copper = {
 .serdes_init  = serdes_init_10g,
 .link_status  = link_status_10g, /* XXX */
};

static const struct niu_phy_ops phy_ops_1g_fiber = {
 .serdes_init  = serdes_init_1g,
 .xcvr_init  = xcvr_init_1g,
 .link_status  = link_status_1g,
};

static const struct niu_phy_ops phy_ops_1g_copper = {
 .xcvr_init  = xcvr_init_1g,
 .link_status  = link_status_1g,
};

struct niu_phy_template {
 const struct niu_phy_ops *ops;
 u32    phy_addr_base;
};

static const struct niu_phy_template phy_template_niu_10g_fiber = {
 .ops  = &phy_ops_10g_fiber_niu,
 .phy_addr_base = 16,
};

static const struct niu_phy_template phy_template_niu_10g_serdes = {
 .ops  = &phy_ops_10g_serdes_niu,
 .phy_addr_base = 0,
};

static const struct niu_phy_template phy_template_niu_1g_serdes = {
 .ops  = &phy_ops_1g_serdes_niu,
 .phy_addr_base = 0,
};

static const struct niu_phy_template phy_template_10g_fiber = {
 .ops  = &phy_ops_10g_fiber,
 .phy_addr_base = 8,
};

static const struct niu_phy_template phy_template_10g_fiber_hotplug = {
 .ops  = &phy_ops_10g_fiber_hotplug,
 .phy_addr_base = 8,
};

static const struct niu_phy_template phy_template_niu_10g_hotplug = {
 .ops  = &phy_ops_niu_10g_hotplug,
 .phy_addr_base = 8,
};

static const struct niu_phy_template phy_template_10g_copper = {
 .ops  = &phy_ops_10g_copper,
 .phy_addr_base = 10,
};

static const struct niu_phy_template phy_template_1g_fiber = {
 .ops  = &phy_ops_1g_fiber,
 .phy_addr_base = 0,
};

static const struct niu_phy_template phy_template_1g_copper = {
 .ops  = &phy_ops_1g_copper,
 .phy_addr_base = 0,
};

static const struct niu_phy_template phy_template_1g_rgmii = {
 .ops  = &phy_ops_1g_rgmii,
 .phy_addr_base = 0,
};

static const struct niu_phy_template phy_template_10g_serdes = {
 .ops  = &phy_ops_10g_serdes,
 .phy_addr_base = 0,
};

static int niu_atca_port_num[4] = {
 0, 0,  11, 10
};

static int serdes_init_10g_serdes(struct niu *np)
{
 struct niu_link_config *lp = &np->link_config;
 unsigned long ctrl_reg, test_cfg_reg, pll_cfg, i;
 u64 ctrl_val, test_cfg_val, sig, mask, val;

 switch (np->port) {
 case 0:
  ctrl_reg = ENET_SERDES_0_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_0_TEST_CFG;
  pll_cfg = ENET_SERDES_0_PLL_CFG;
  break;
 case 1:
  ctrl_reg = ENET_SERDES_1_CTRL_CFG;
  test_cfg_reg = ENET_SERDES_1_TEST_CFG;
  pll_cfg = ENET_SERDES_1_PLL_CFG;
  break;

 default:
  return -EINVAL;
 }
 ctrl_val = (ENET_SERDES_CTRL_SDET_0 |
      ENET_SERDES_CTRL_SDET_1 |
      ENET_SERDES_CTRL_SDET_2 |
      ENET_SERDES_CTRL_SDET_3 |
      (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) |
      (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) |
      (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT));
 test_cfg_val = 0;

 if (lp->loopback_mode == LOOPBACK_PHY) {
  test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_0_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_1_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_2_SHIFT) |
     (ENET_TEST_MD_PAD_LOOPBACK <<
      ENET_SERDES_TEST_MD_3_SHIFT));
 }

 esr_reset(np);
 nw64(pll_cfg, ENET_SERDES_PLL_FBDIV2);
 nw64(ctrl_reg, ctrl_val);
 nw64(test_cfg_reg, test_cfg_val);

 /* Initialize all 4 lanes of the SERDES.  */
 for (i = 0; i < 4; i++) {
  u32 rxtx_ctrl, glue0;
  int err;

  err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl);
  if (err)
   return err;
  err = esr_read_glue0(np, i, &glue0);
  if (err)
   return err;

  rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO);
  rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH |
         (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT));

  glue0 &= ~(ESR_GLUE_CTRL0_SRATE |
      ESR_GLUE_CTRL0_THCNT |
      ESR_GLUE_CTRL0_BLTIME);
  glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB |
     (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) |
     (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) |
     (BLTIME_300_CYCLES <<
      ESR_GLUE_CTRL0_BLTIME_SHIFT));

  err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl);
  if (err)
   return err;
  err = esr_write_glue0(np, i, glue0);
  if (err)
   return err;
 }


 sig = nr64(ESR_INT_SIGNALS);
 switch (np->port) {
 case 0:
  mask = ESR_INT_SIGNALS_P0_BITS;
  val = (ESR_INT_SRDY0_P0 |
         ESR_INT_DET0_P0 |
         ESR_INT_XSRDY_P0 |
         ESR_INT_XDP_P0_CH3 |
         ESR_INT_XDP_P0_CH2 |
         ESR_INT_XDP_P0_CH1 |
         ESR_INT_XDP_P0_CH0);
  break;

 case 1:
  mask = ESR_INT_SIGNALS_P1_BITS;
  val = (ESR_INT_SRDY0_P1 |
         ESR_INT_DET0_P1 |
         ESR_INT_XSRDY_P1 |
         ESR_INT_XDP_P1_CH3 |
         ESR_INT_XDP_P1_CH2 |
         ESR_INT_XDP_P1_CH1 |
         ESR_INT_XDP_P1_CH0);
  break;

 default:
  return -EINVAL;
 }

 if ((sig & mask) != val) {
  int err;
  err = serdes_init_1g_serdes(np);
  if (!err) {
   np->flags &= ~NIU_FLAGS_10G;
   np->mac_xcvr = MAC_XCVR_PCS;
  }  else {
   netdev_err(np->dev, "Port %u 10G/1G SERDES Link Failed\n",
       np->port);
   return -ENODEV;
  }
 }

 return 0;
}

static int niu_determine_phy_disposition(struct niu *np)
{
 struct niu_parent *parent = np->parent;
 u8 plat_type = parent->plat_type;
 const struct niu_phy_template *tp;
 u32 phy_addr_off = 0;

 if (plat_type == PLAT_TYPE_NIU) {
  switch (np->flags &
   (NIU_FLAGS_10G |
    NIU_FLAGS_FIBER |
    NIU_FLAGS_XCVR_SERDES)) {
  case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES:
   /* 10G Serdes */
   tp = &phy_template_niu_10g_serdes;
   break;
  case NIU_FLAGS_XCVR_SERDES:
   /* 1G Serdes */
   tp = &phy_template_niu_1g_serdes;
   break;
  case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
   /* 10G Fiber */
  default:
   if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
    tp = &phy_template_niu_10g_hotplug;
    if (np->port == 0)
     phy_addr_off = 8;
    if (np->port == 1)
     phy_addr_off = 12;
   } else {
    tp = &phy_template_niu_10g_fiber;
    phy_addr_off += np->port;
   }
   break;
  }
 } else {
  switch (np->flags &
   (NIU_FLAGS_10G |
    NIU_FLAGS_FIBER |
    NIU_FLAGS_XCVR_SERDES)) {
  case 0:
   /* 1G copper */
   tp = &phy_template_1g_copper;
   if (plat_type == PLAT_TYPE_VF_P0)
    phy_addr_off = 10;
   else if (plat_type == PLAT_TYPE_VF_P1)
    phy_addr_off = 26;

   phy_addr_off += (np->port ^ 0x3);
   break;

  case NIU_FLAGS_10G:
   /* 10G copper */
   tp = &phy_template_10g_copper;
   break;

  case NIU_FLAGS_FIBER:
   /* 1G fiber */
   tp = &phy_template_1g_fiber;
   break;

  case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
   /* 10G fiber */
   tp = &phy_template_10g_fiber;
   if (plat_type == PLAT_TYPE_VF_P0 ||
       plat_type == PLAT_TYPE_VF_P1)
    phy_addr_off = 8;
   phy_addr_off += np->port;
   if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
    tp = &phy_template_10g_fiber_hotplug;
    if (np->port == 0)
     phy_addr_off = 8;
    if (np->port == 1)
     phy_addr_off = 12;
   }
   break;

  case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES:
  case NIU_FLAGS_XCVR_SERDES | NIU_FLAGS_FIBER:
  case NIU_FLAGS_XCVR_SERDES:
   switch(np->port) {
   case 0:
   case 1:
    tp = &phy_template_10g_serdes;
    break;
   case 2:
   case 3:
    tp = &phy_template_1g_rgmii;
    break;
   default:
    return -EINVAL;
   }
   phy_addr_off = niu_atca_port_num[np->port];
   break;

  default:
   return -EINVAL;
  }
 }

 np->phy_ops = tp->ops;
 np->phy_addr = tp->phy_addr_base + phy_addr_off;

 return 0;
}

static int niu_init_link(struct niu *np)
{
 struct niu_parent *parent = np->parent;
 int err, ignore;

 if (parent->plat_type == PLAT_TYPE_NIU) {
  err = niu_xcvr_init(np);
  if (err)
   return err;
  msleep(200);
 }
 err = niu_serdes_init(np);
 if (err && !(np->flags & NIU_FLAGS_HOTPLUG_PHY))
  return err;
 msleep(200);
 err = niu_xcvr_init(np);
 if (!err || (np->flags & NIU_FLAGS_HOTPLUG_PHY))
  niu_link_status(np, &ignore);
 return 0;
}

static void niu_set_primary_mac(struct niu *np, const unsigned char *addr)
{
 u16 reg0 = addr[4] << 8 | addr[5];
 u16 reg1 = addr[2] << 8 | addr[3];
 u16 reg2 = addr[0] << 8 | addr[1];

 if (np->flags & NIU_FLAGS_XMAC) {
  nw64_mac(XMAC_ADDR0, reg0);
  nw64_mac(XMAC_ADDR1, reg1);
  nw64_mac(XMAC_ADDR2, reg2);
 } else {
  nw64_mac(BMAC_ADDR0, reg0);
  nw64_mac(BMAC_ADDR1, reg1);
  nw64_mac(BMAC_ADDR2, reg2);
 }
}

static int niu_num_alt_addr(struct niu *np)
{
 if (np->flags & NIU_FLAGS_XMAC)
  return XMAC_NUM_ALT_ADDR;
 else
  return BMAC_NUM_ALT_ADDR;
}

static int niu_set_alt_mac(struct niu *np, int index, unsigned char *addr)
{
 u16 reg0 = addr[4] << 8 | addr[5];
 u16 reg1 = addr[2] << 8 | addr[3];
 u16 reg2 = addr[0] << 8 | addr[1];

 if (index >= niu_num_alt_addr(np))
  return -EINVAL;

 if (np->flags & NIU_FLAGS_XMAC) {
  nw64_mac(XMAC_ALT_ADDR0(index), reg0);
  nw64_mac(XMAC_ALT_ADDR1(index), reg1);
  nw64_mac(XMAC_ALT_ADDR2(index), reg2);
 } else {
  nw64_mac(BMAC_ALT_ADDR0(index), reg0);
  nw64_mac(BMAC_ALT_ADDR1(index), reg1);
  nw64_mac(BMAC_ALT_ADDR2(index), reg2);
 }

 return 0;
}

static int niu_enable_alt_mac(struct niu *np, int index, int on)
{
 unsigned long reg;
 u64 val, mask;

 if (index >= niu_num_alt_addr(np))
  return -EINVAL;

 if (np->flags & NIU_FLAGS_XMAC) {
  reg = XMAC_ADDR_CMPEN;
  mask = 1 << index;
 } else {
  reg = BMAC_ADDR_CMPEN;
  mask = 1 << (index + 1);
 }

 val = nr64_mac(reg);
 if (on)
  val |= mask;
 else
  val &= ~mask;
 nw64_mac(reg, val);

 return 0;
}

static void __set_rdc_table_num_hw(struct niu *np, unsigned long reg,
       int num, int mac_pref)
{
 u64 val = nr64_mac(reg);
 val &= ~(HOST_INFO_MACRDCTBLN | HOST_INFO_MPR);
 val |= num;
 if (mac_pref)
  val |= HOST_INFO_MPR;
 nw64_mac(reg, val);
}

static int __set_rdc_table_num(struct niu *np,
          int xmac_index, int bmac_index,
          int rdc_table_num, int mac_pref)
{
 unsigned long reg;

 if (rdc_table_num & ~HOST_INFO_MACRDCTBLN)
  return -EINVAL;
 if (np->flags & NIU_FLAGS_XMAC)
  reg = XMAC_HOST_INFO(xmac_index);
 else
  reg = BMAC_HOST_INFO(bmac_index);
 __set_rdc_table_num_hw(np, reg, rdc_table_num, mac_pref);
 return 0;
}

static int niu_set_primary_mac_rdc_table(struct niu *np, int table_num,
      int mac_pref)
{
 return __set_rdc_table_num(np, 17, 0, table_num, mac_pref);
}

static int niu_set_multicast_mac_rdc_table(struct niu *np, int table_num,
        int mac_pref)
{
 return __set_rdc_table_num(np, 16, 8, table_num, mac_pref);
}

static int niu_set_alt_mac_rdc_table(struct niu *np, int idx,
         int table_num, int mac_pref)
{
 if (idx >= niu_num_alt_addr(np))
  return -EINVAL;
 return __set_rdc_table_num(np, idx, idx + 1, table_num, mac_pref);
}

static u64 vlan_entry_set_parity(u64 reg_val)
{
 u64 port01_mask;
 u64 port23_mask;

 port01_mask = 0x00ff;
 port23_mask = 0xff00;

 if (hweight64(reg_val & port01_mask) & 1)
  reg_val |= ENET_VLAN_TBL_PARITY0;
 else
  reg_val &= ~ENET_VLAN_TBL_PARITY0;

 if (hweight64(reg_val & port23_mask) & 1)
  reg_val |= ENET_VLAN_TBL_PARITY1;
 else
  reg_val &= ~ENET_VLAN_TBL_PARITY1;

 return reg_val;
}

static void vlan_tbl_write(struct niu *np, unsigned long index,
      int port, int vpr, int rdc_table)
{
 u64 reg_val = nr64(ENET_VLAN_TBL(index));

 reg_val &= ~((ENET_VLAN_TBL_VPR |
        ENET_VLAN_TBL_VLANRDCTBLN) <<
       ENET_VLAN_TBL_SHIFT(port));
 if (vpr)
  reg_val |= (ENET_VLAN_TBL_VPR <<
       ENET_VLAN_TBL_SHIFT(port));
 reg_val |= (rdc_table << ENET_VLAN_TBL_SHIFT(port));

 reg_val = vlan_entry_set_parity(reg_val);

 nw64(ENET_VLAN_TBL(index), reg_val);
}

static void vlan_tbl_clear(struct niu *np)
{
 int i;

 for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++)
  nw64(ENET_VLAN_TBL(i), 0);
}

static int tcam_wait_bit(struct niu *np, u64 bit)
{
 int limit = 1000;

 while (--limit > 0) {
  if (nr64(TCAM_CTL) & bit)
   break;
  udelay(1);
 }
 if (limit <= 0)
  return -ENODEV;

 return 0;
}

static int tcam_flush(struct niu *np, int index)
{
 nw64(TCAM_KEY_0, 0x00);
 nw64(TCAM_KEY_MASK_0, 0xff);
 nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));

 return tcam_wait_bit(np, TCAM_CTL_STAT);
}

#if 0
static int tcam_read(struct niu *np, int index,
       u64 *key, u64 *mask)
{
 int err;

 nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_READ | index));
 err = tcam_wait_bit(np, TCAM_CTL_STAT);
 if (!err) {
  key[0] = nr64(TCAM_KEY_0);
  key[1] = nr64(TCAM_KEY_1);
  key[2] = nr64(TCAM_KEY_2);
  key[3] = nr64(TCAM_KEY_3);
  mask[0] = nr64(TCAM_KEY_MASK_0);
  mask[1] = nr64(TCAM_KEY_MASK_1);
  mask[2] = nr64(TCAM_KEY_MASK_2);
  mask[3] = nr64(TCAM_KEY_MASK_3);
 }
 return err;
}
#endif

static int tcam_write(struct niu *np, int index,
        u64 *key, u64 *mask)
{
 nw64(TCAM_KEY_0, key[0]);
 nw64(TCAM_KEY_1, key[1]);
 nw64(TCAM_KEY_2, key[2]);
 nw64(TCAM_KEY_3, key[3]);
 nw64(TCAM_KEY_MASK_0, mask[0]);
 nw64(TCAM_KEY_MASK_1, mask[1]);
 nw64(TCAM_KEY_MASK_2, mask[2]);
 nw64(TCAM_KEY_MASK_3, mask[3]);
 nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));

 return tcam_wait_bit(np, TCAM_CTL_STAT);
}

#if 0
static int tcam_assoc_read(struct niu *np, int index, u64 *data)
{
 int err;

 nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_READ | index));
 err = tcam_wait_bit(np, TCAM_CTL_STAT);
 if (!err)
  *data = nr64(TCAM_KEY_1);

 return err;
}
#endif

static int tcam_assoc_write(struct niu *np, int index, u64 assoc_data)
{
 nw64(TCAM_KEY_1, assoc_data);
 nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_WRITE | index));

 return tcam_wait_bit(np, TCAM_CTL_STAT);
}

static void tcam_enable(struct niu *np, int on)
{
 u64 val = nr64(FFLP_CFG_1);

 if (on)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge