📄 niu.c
字号:
/* niu.c: Neptune ethernet driver. * * Copyright (C) 2007 David S. Miller (davem@davemloft.net) */#include <linux/module.h>#include <linux/init.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_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/io.h>#ifdef CONFIG_SPARC64#include <linux/of_device.h>#endif#include "niu.h"#define DRV_MODULE_NAME "niu"#define PFX DRV_MODULE_NAME ": "#define DRV_MODULE_VERSION "0.6"#define DRV_MODULE_RELDATE "January 5, 2008"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 DMA_44BIT_MASK#define DMA_44BIT_MASK 0x00000fffffffffffULL#endif#ifndef readqstatic u64 readq(void __iomem *reg){ return (((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg));}static void writeq(u64 val, void __iomem *reg){ writel(val & 0xffffffff, reg); writel(val >> 32, reg + 0x4UL);}#endifstatic 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 niudbg(TYPE, f, a...) \do { if ((np)->msg_enable & NETIF_MSG_##TYPE) \ printk(KERN_DEBUG PFX f, ## a); \} while (0)#define niuinfo(TYPE, f, a...) \do { if ((np)->msg_enable & NETIF_MSG_##TYPE) \ printk(KERN_INFO PFX f, ## a); \} while (0)#define niuwarn(TYPE, f, a...) \do { if ((np)->msg_enable & NETIF_MSG_##TYPE) \ printk(KERN_WARNING PFX f, ## a); \} while (0)#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 __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) dev_err(np->device, PFX "%s: bits (%llx) of register %s " "would not clear, val[%llx]\n", np->dev->name, (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) dev_err(np->device, PFX "%s: bits (%llx) of register %s " "would not clear, val[%llx]\n", np->dev->name, (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) dev_err(np->device, PFX "%s: bits (%llx) of register %s " "would not clear, val[%llx]\n", np->dev->name, (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(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 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) { dev_err(np->device, PFX "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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -