/* niu.c: Neptune ethernet driver. * * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net) */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "niu.h" #define DRV_MODULE_NAME "niu" #define DRV_MODULE_VERSION "1.1" #define DRV_MODULE_RELDATE "Apr 22, 2010" static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 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 DEFINE_PCI_DEVICE_TABLE(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 uninitialized_var(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 uninitialized_var(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 uninitialized_var(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 = -EINVAL; err = mii_read(np, np->phy_addr, MII_BMSR); if (err < 0) goto out; bmsr = err; if (bmsr & BMSR_LSTATUS) { u16 adv, lpa; err = mii_read(np, np->phy_addr, MII_ADVERTISE); if (err < 0) goto out; adv = err; err = mii_read(np, np->phy_addr, MII_LPA); if (err < 0) goto out; lpa = err; err = mii_read(np, np->phy_addr, MII_ESTATUS); if (err < 0) goto out; 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(unsigned long __opaque) { struct niu *np = (struct niu *) __opaque; 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; break; } 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, 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) val &= ~FFLP_CFG_1_TCAM_D