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 + -
显示快捷键?