sky2.c
来自「linux 内核源代码」· C语言 代码 · 共 2,361 行 · 第 1/5 页
C
2,361 行
/* * New driver for Marvell Yukon 2 chipset. * Based on earlier sk98lin, and skge driver. * * This driver intentionally does not support all the features * of the original driver such as link fail-over and link management because * those should be done at higher levels. * * Copyright (C) 2005 Stephen Hemminger <shemminger@osdl.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/crc32.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/dma-mapping.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/pci.h>#include <linux/ip.h>#include <net/ip.h>#include <linux/tcp.h>#include <linux/in.h>#include <linux/delay.h>#include <linux/workqueue.h>#include <linux/if_vlan.h>#include <linux/prefetch.h>#include <linux/debugfs.h>#include <linux/mii.h>#include <asm/irq.h>#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)#define SKY2_VLAN_TAG_USED 1#endif#include "sky2.h"#define DRV_NAME "sky2"#define DRV_VERSION "1.20"#define PFX DRV_NAME " "/* * The Yukon II chipset takes 64 bit command blocks (called list elements) * that are organized into three (receive, transmit, status) different rings * similar to Tigon3. */#define RX_LE_SIZE 1024#define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le))#define RX_MAX_PENDING (RX_LE_SIZE/6 - 2)#define RX_DEF_PENDING RX_MAX_PENDING#define RX_SKB_ALIGN 8#define TX_RING_SIZE 512#define TX_DEF_PENDING (TX_RING_SIZE - 1)#define TX_MIN_PENDING 64#define MAX_SKB_TX_LE (4 + (sizeof(dma_addr_t)/sizeof(u32))*MAX_SKB_FRAGS)#define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */#define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le))#define TX_WATCHDOG (5 * HZ)#define NAPI_WEIGHT 64#define PHY_RETRIES 1000#define SKY2_EEPROM_MAGIC 0x9955aabb#define RING_NEXT(x,s) (((x)+1) & ((s)-1))static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;static int debug = -1; /* defaults above */module_param(debug, int, 0);MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");static int copybreak __read_mostly = 128;module_param(copybreak, int, 0);MODULE_PARM_DESC(copybreak, "Receive copy threshold");static int disable_msi = 0;module_param(disable_msi, int, 0);MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) }, /* DGE-550SX */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B02) }, /* DGE-560SX */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B03) }, /* DGE-550T */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4340) }, /* 88E8021 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4341) }, /* 88E8022 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4342) }, /* 88E8061 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4343) }, /* 88E8062 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4344) }, /* 88E8021 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4345) }, /* 88E8022 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4346) }, /* 88E8061 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4347) }, /* 88E8062 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) }, /* 88E8035 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) }, /* 88E8036 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, /* 88E8038 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) }, /* 88E8039 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4354) }, /* 88E8040 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4356) }, /* 88EC033 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4357) }, /* 88E8042 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x435A) }, /* 88E8048 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) }, /* 88E8052 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, /* 88E8050 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, /* 88E8053 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, /* 88E8055 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) }, /* 88E8056 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4365) }, /* 88E8070 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) }, /* 88EC036 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) }, /* 88EC032 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) }, /* 88EC034 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4369) }, /* 88EC042 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436A) }, /* 88E8058 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436B) }, /* 88E8071 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436C) }, /* 88E8072 */ { 0 }};MODULE_DEVICE_TABLE(pci, sky2_id_table);/* Avoid conditionals by using array */static const unsigned txqaddr[] = { Q_XA1, Q_XA2 };static const unsigned rxqaddr[] = { Q_R1, Q_R2 };static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 };/* This driver supports yukon2 chipset only */static const char *yukon2_name[] = { "XL", /* 0xb3 */ "EC Ultra", /* 0xb4 */ "Extreme", /* 0xb5 */ "EC", /* 0xb6 */ "FE", /* 0xb7 */ "FE+", /* 0xb8 */};static void sky2_set_multicast(struct net_device *dev);/* Access to PHY via serial interconnect */static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val){ int i; gma_write16(hw, port, GM_SMI_DATA, val); gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg)); for (i = 0; i < PHY_RETRIES; i++) { u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL); if (ctrl == 0xffff) goto io_error; if (!(ctrl & GM_SMI_CT_BUSY)) return 0; udelay(10); } dev_warn(&hw->pdev->dev,"%s: phy write timeout\n", hw->dev[port]->name); return -ETIMEDOUT;io_error: dev_err(&hw->pdev->dev, "%s: phy I/O error\n", hw->dev[port]->name); return -EIO;}static int __gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg, u16 *val){ int i; gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD); for (i = 0; i < PHY_RETRIES; i++) { u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL); if (ctrl == 0xffff) goto io_error; if (ctrl & GM_SMI_CT_RD_VAL) { *val = gma_read16(hw, port, GM_SMI_DATA); return 0; } udelay(10); } dev_warn(&hw->pdev->dev, "%s: phy read timeout\n", hw->dev[port]->name); return -ETIMEDOUT;io_error: dev_err(&hw->pdev->dev, "%s: phy I/O error\n", hw->dev[port]->name); return -EIO;}static inline u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg){ u16 v; __gm_phy_read(hw, port, reg, &v); return v;}static void sky2_power_on(struct sky2_hw *hw){ /* switch power to VCC (WA for VAUX problem) */ sky2_write8(hw, B0_POWER_CTRL, PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); /* disable Core Clock Division, */ sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) /* enable bits are inverted */ sky2_write8(hw, B2_Y2_CLK_GATE, Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); else sky2_write8(hw, B2_Y2_CLK_GATE, 0); if (hw->flags & SKY2_HW_ADV_POWER_CTL) { u32 reg; sky2_pci_write32(hw, PCI_DEV_REG3, 0); reg = sky2_pci_read32(hw, PCI_DEV_REG4); /* set all bits to 0 except bits 15..12 and 8 */ reg &= P_ASPM_CONTROL_MSK; sky2_pci_write32(hw, PCI_DEV_REG4, reg); reg = sky2_pci_read32(hw, PCI_DEV_REG5); /* set all bits to 0 except bits 28 & 27 */ reg &= P_CTL_TIM_VMAIN_AV_MSK; sky2_pci_write32(hw, PCI_DEV_REG5, reg); sky2_pci_write32(hw, PCI_CFG_REG_1, 0); /* Enable workaround for dev 4.107 on Yukon-Ultra & Extreme */ reg = sky2_read32(hw, B2_GP_IO); reg |= GLB_GPIO_STAT_RACE_DIS; sky2_write32(hw, B2_GP_IO, reg); sky2_read32(hw, B2_GP_IO); }}static void sky2_power_aux(struct sky2_hw *hw){ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) sky2_write8(hw, B2_Y2_CLK_GATE, 0); else /* enable bits are inverted */ sky2_write8(hw, B2_Y2_CLK_GATE, Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); /* switch power to VAUX */ if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) sky2_write8(hw, B0_POWER_CTRL, (PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF));}static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port){ u16 reg; /* disable all GMAC IRQ's */ sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0); gma_write16(hw, port, GM_MC_ADDR_H1, 0); /* clear MC hash */ gma_write16(hw, port, GM_MC_ADDR_H2, 0); gma_write16(hw, port, GM_MC_ADDR_H3, 0); gma_write16(hw, port, GM_MC_ADDR_H4, 0); reg = gma_read16(hw, port, GM_RX_CTRL); reg |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA; gma_write16(hw, port, GM_RX_CTRL, reg);}/* flow control to advertise bits */static const u16 copper_fc_adv[] = { [FC_NONE] = 0, [FC_TX] = PHY_M_AN_ASP, [FC_RX] = PHY_M_AN_PC, [FC_BOTH] = PHY_M_AN_PC | PHY_M_AN_ASP,};/* flow control to advertise bits when using 1000BaseX */static const u16 fiber_fc_adv[] = { [FC_NONE] = PHY_M_P_NO_PAUSE_X, [FC_TX] = PHY_M_P_ASYM_MD_X, [FC_RX] = PHY_M_P_SYM_MD_X, [FC_BOTH] = PHY_M_P_BOTH_MD_X,};/* flow control to GMA disable bits */static const u16 gm_fc_disable[] = { [FC_NONE] = GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS, [FC_TX] = GM_GPCR_FC_RX_DIS, [FC_RX] = GM_GPCR_FC_TX_DIS, [FC_BOTH] = 0,};static void sky2_phy_init(struct sky2_hw *hw, unsigned port){ struct sky2_port *sky2 = netdev_priv(hw->dev[port]); u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg; if (sky2->autoneg == AUTONEG_ENABLE && !(hw->flags & SKY2_HW_NEWER_PHY)) { u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK | PHY_M_EC_MAC_S_MSK); ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ); /* on PHY 88E1040 Rev.D0 (and newer) downshift control changed */ if (hw->chip_id == CHIP_ID_YUKON_EC) /* set downshift counter to 3x and enable downshift */ ectrl |= PHY_M_EC_DSC_2(2) | PHY_M_EC_DOWN_S_ENA; else /* set master & slave downshift counter to 1x */ ectrl |= PHY_M_EC_M_DSC(0) | PHY_M_EC_S_DSC(1); gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl); } ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); if (sky2_is_copper(hw)) { if (!(hw->flags & SKY2_HW_GIGABIT)) { /* enable automatic crossover */ ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1; if (hw->chip_id == CHIP_ID_YUKON_FE_P && hw->chip_rev == CHIP_REV_YU_FE2_A0) { u16 spec; /* Enable Class A driver for FE+ A0 */ spec = gm_phy_read(hw, port, PHY_MARV_FE_SPEC_2); spec |= PHY_M_FESC_SEL_CL_A; gm_phy_write(hw, port, PHY_MARV_FE_SPEC_2, spec); } } else { /* disable energy detect */ ctrl &= ~PHY_M_PC_EN_DET_MSK; /* enable automatic crossover */ ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); /* downshift on PHY 88E1112 and 88E1149 is changed */ if (sky2->autoneg == AUTONEG_ENABLE && (hw->flags & SKY2_HW_NEWER_PHY)) { /* set downshift counter to 3x and enable downshift */ ctrl &= ~PHY_M_PC_DSC_MSK; ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA; } } } else { /* workaround for deviation #4.88 (CRC errors) */ /* disable Automatic Crossover */ ctrl &= ~PHY_M_PC_MDIX_MSK; } gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); /* special setup for PHY 88E1112 Fiber */ if (hw->chip_id == CHIP_ID_YUKON_XL && (hw->flags & SKY2_HW_FIBRE_PHY)) { pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2); ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); ctrl &= ~PHY_M_MAC_MD_MSK; ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX); gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); if (hw->pmd_type == 'P') { /* select page 1 to access Fiber registers */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1); /* for SFP-module set SIGDET polarity to low */ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); ctrl |= PHY_M_FIB_SIGD_POL; gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); } gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } ctrl = PHY_CT_RESET; ct1000 = 0; adv = PHY_AN_CSMA; reg = 0; if (sky2->autoneg == AUTONEG_ENABLE) { if (sky2_is_copper(hw)) { if (sky2->advertising & ADVERTISED_1000baseT_Full) ct1000 |= PHY_M_1000C_AFD; if (sky2->advertising & ADVERTISED_1000baseT_Half) ct1000 |= PHY_M_1000C_AHD; if (sky2->advertising & ADVERTISED_100baseT_Full) adv |= PHY_M_AN_100_FD; if (sky2->advertising & ADVERTISED_100baseT_Half) adv |= PHY_M_AN_100_HD; if (sky2->advertising & ADVERTISED_10baseT_Full) adv |= PHY_M_AN_10_FD; if (sky2->advertising & ADVERTISED_10baseT_Half) adv |= PHY_M_AN_10_HD; adv |= copper_fc_adv[sky2->flow_mode]; } else { /* special defines for FIBER (88E1040S only) */ if (sky2->advertising & ADVERTISED_1000baseT_Full) adv |= PHY_M_AN_1000X_AFD; if (sky2->advertising & ADVERTISED_1000baseT_Half) adv |= PHY_M_AN_1000X_AHD; adv |= fiber_fc_adv[sky2->flow_mode]; } /* Restart Auto-negotiation */ ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG; } else { /* forced speed/duplex settings */ ct1000 = PHY_M_1000C_MSE; /* Disable auto update for duplex flow control and speed */ reg |= GM_GPCR_AU_ALL_DIS; switch (sky2->speed) { case SPEED_1000: ctrl |= PHY_CT_SP1000; reg |= GM_GPCR_SPEED_1000; break; case SPEED_100: ctrl |= PHY_CT_SP100; reg |= GM_GPCR_SPEED_100; break; } if (sky2->duplex == DUPLEX_FULL) { reg |= GM_GPCR_DUP_FULL; ctrl |= PHY_CT_DUP_MD; } else if (sky2->speed < SPEED_1000) sky2->flow_mode = FC_NONE; reg |= gm_fc_disable[sky2->flow_mode]; /* Forward pause packets to GMAC? */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?