skge.c

来自「linux 内核源代码」· C语言 代码 · 共 2,432 行 · 第 1/5 页

C
2,432
字号
/* * New driver for Marvell Yukon chipset and SysKonnect Gigabit * Ethernet adapters. Based on earlier sk98lin, e100 and * FreeBSD if_sk drivers. * * 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) 2004, 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/in.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/pci.h>#include <linux/if_vlan.h>#include <linux/ip.h>#include <linux/delay.h>#include <linux/crc32.h>#include <linux/dma-mapping.h>#include <linux/debugfs.h>#include <linux/seq_file.h>#include <linux/mii.h>#include <asm/irq.h>#include "skge.h"#define DRV_NAME		"skge"#define DRV_VERSION		"1.13"#define PFX			DRV_NAME " "#define DEFAULT_TX_RING_SIZE	128#define DEFAULT_RX_RING_SIZE	512#define MAX_TX_RING_SIZE	1024#define TX_LOW_WATER		(MAX_SKB_FRAGS + 1)#define MAX_RX_RING_SIZE	4096#define RX_COPY_THRESHOLD	128#define RX_BUF_SIZE		1536#define PHY_RETRIES	        1000#define ETH_JUMBO_MTU		9000#define TX_WATCHDOG		(5 * HZ)#define NAPI_WEIGHT		64#define BLINK_MS		250#define LINK_HZ			HZ#define SKGE_EEPROM_MAGIC	0x9933aabbMODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);static const u32 default_msg	= NETIF_MSG_DRV| NETIF_MSG_PROBE| NETIF_MSG_LINK	  | 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 const struct pci_device_id skge_id_table[] = {	{ PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940) },	{ PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B) },	{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE) },	{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU) },	{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T) },	{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) },	/* DGE-530T */	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) },	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */	{ PCI_DEVICE(PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD) },	{ PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064) },	{ PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015 },	{ 0 }};MODULE_DEVICE_TABLE(pci, skge_id_table);static int skge_up(struct net_device *dev);static int skge_down(struct net_device *dev);static void skge_phy_reset(struct skge_port *skge);static void skge_tx_clean(struct net_device *dev);static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);static void genesis_get_stats(struct skge_port *skge, u64 *data);static void yukon_get_stats(struct skge_port *skge, u64 *data);static void yukon_init(struct skge_hw *hw, int port);static void genesis_mac_init(struct skge_hw *hw, int port);static void genesis_link_up(struct skge_port *skge);/* Avoid conditionals by using array */static const int txqaddr[] = { Q_XA1, Q_XA2 };static const int rxqaddr[] = { Q_R1, Q_R2 };static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F };static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F };static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F };static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 };static int skge_get_regs_len(struct net_device *dev){	return 0x4000;}/* * Returns copy of whole control register region * Note: skip RAM address register because accessing it will * 	 cause bus hangs! */static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs,			  void *p){	const struct skge_port *skge = netdev_priv(dev);	const void __iomem *io = skge->hw->regs;	regs->version = 1;	memset(p, 0, regs->len);	memcpy_fromio(p, io, B3_RAM_ADDR);	memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1,		      regs->len - B3_RI_WTO_R1);}/* Wake on Lan only supported on Yukon chips with rev 1 or above */static u32 wol_supported(const struct skge_hw *hw){	if (hw->chip_id == CHIP_ID_GENESIS)		return 0;	if (hw->chip_id == CHIP_ID_YUKON && hw->chip_rev == 0)		return 0;	return WAKE_MAGIC | WAKE_PHY;}static u32 pci_wake_enabled(struct pci_dev *dev){	int pm = pci_find_capability(dev, PCI_CAP_ID_PM);	u16 value;	/* If device doesn't support PM Capabilities, but request is to disable	 * wake events, it's a nop; otherwise fail */	if (!pm)		return 0;	pci_read_config_word(dev, pm + PCI_PM_PMC, &value);	value &= PCI_PM_CAP_PME_MASK;	value >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */	return value != 0;}static void skge_wol_init(struct skge_port *skge){	struct skge_hw *hw = skge->hw;	int port = skge->port;	u16 ctrl;	skge_write16(hw, B0_CTST, CS_RST_CLR);	skge_write16(hw, SK_REG(port, GMAC_LINK_CTRL), GMLC_RST_CLR);	/* Turn on Vaux */	skge_write8(hw, B0_POWER_CTRL,		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF);	/* WA code for COMA mode -- clear PHY reset */	if (hw->chip_id == CHIP_ID_YUKON_LITE &&	    hw->chip_rev >= CHIP_REV_YU_LITE_A3) {		u32 reg = skge_read32(hw, B2_GP_IO);		reg |= GP_DIR_9;		reg &= ~GP_IO_9;		skge_write32(hw, B2_GP_IO, reg);	}	skge_write32(hw, SK_REG(port, GPHY_CTRL),		     GPC_DIS_SLEEP |		     GPC_HWCFG_M_3 | GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0 |		     GPC_ANEG_1 | GPC_RST_SET);	skge_write32(hw, SK_REG(port, GPHY_CTRL),		     GPC_DIS_SLEEP |		     GPC_HWCFG_M_3 | GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0 |		     GPC_ANEG_1 | GPC_RST_CLR);	skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);	/* Force to 10/100 skge_reset will re-enable on resume	 */	gm_phy_write(hw, port, PHY_MARV_AUNE_ADV,		     PHY_AN_100FULL | PHY_AN_100HALF |		     PHY_AN_10FULL | PHY_AN_10HALF| PHY_AN_CSMA);	/* no 1000 HD/FD */	gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, 0);	gm_phy_write(hw, port, PHY_MARV_CTRL,		     PHY_CT_RESET | PHY_CT_SPS_LSB | PHY_CT_ANE |		     PHY_CT_RE_CFG | PHY_CT_DUP_MD);	/* Set GMAC to no flow control and auto update for speed/duplex */	gma_write16(hw, port, GM_GP_CTRL,		    GM_GPCR_FC_TX_DIS|GM_GPCR_TX_ENA|GM_GPCR_RX_ENA|		    GM_GPCR_DUP_FULL|GM_GPCR_FC_RX_DIS|GM_GPCR_AU_FCT_DIS);	/* Set WOL address */	memcpy_toio(hw->regs + WOL_REGS(port, WOL_MAC_ADDR),		    skge->netdev->dev_addr, ETH_ALEN);	/* Turn on appropriate WOL control bits */	skge_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), WOL_CTL_CLEAR_RESULT);	ctrl = 0;	if (skge->wol & WAKE_PHY)		ctrl |= WOL_CTL_ENA_PME_ON_LINK_CHG|WOL_CTL_ENA_LINK_CHG_UNIT;	else		ctrl |= WOL_CTL_DIS_PME_ON_LINK_CHG|WOL_CTL_DIS_LINK_CHG_UNIT;	if (skge->wol & WAKE_MAGIC)		ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT;	else		ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;;	ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT;	skge_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl);	/* block receiver */	skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);}static void skge_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol){	struct skge_port *skge = netdev_priv(dev);	wol->supported = wol_supported(skge->hw);	wol->wolopts = skge->wol;}static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol){	struct skge_port *skge = netdev_priv(dev);	struct skge_hw *hw = skge->hw;	if (wol->wolopts & ~wol_supported(hw))		return -EOPNOTSUPP;	skge->wol = wol->wolopts;	return 0;}/* Determine supported/advertised modes based on hardware. * Note: ethtool ADVERTISED_xxx == SUPPORTED_xxx */static u32 skge_supported_modes(const struct skge_hw *hw){	u32 supported;	if (hw->copper) {		supported = SUPPORTED_10baseT_Half			| SUPPORTED_10baseT_Full			| SUPPORTED_100baseT_Half			| SUPPORTED_100baseT_Full			| SUPPORTED_1000baseT_Half			| SUPPORTED_1000baseT_Full			| SUPPORTED_Autoneg| SUPPORTED_TP;		if (hw->chip_id == CHIP_ID_GENESIS)			supported &= ~(SUPPORTED_10baseT_Half					     | SUPPORTED_10baseT_Full					     | SUPPORTED_100baseT_Half					     | SUPPORTED_100baseT_Full);		else if (hw->chip_id == CHIP_ID_YUKON)			supported &= ~SUPPORTED_1000baseT_Half;	} else		supported = SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half			| SUPPORTED_FIBRE | SUPPORTED_Autoneg;	return supported;}static int skge_get_settings(struct net_device *dev,			     struct ethtool_cmd *ecmd){	struct skge_port *skge = netdev_priv(dev);	struct skge_hw *hw = skge->hw;	ecmd->transceiver = XCVR_INTERNAL;	ecmd->supported = skge_supported_modes(hw);	if (hw->copper) {		ecmd->port = PORT_TP;		ecmd->phy_address = hw->phy_addr;	} else		ecmd->port = PORT_FIBRE;	ecmd->advertising = skge->advertising;	ecmd->autoneg = skge->autoneg;	ecmd->speed = skge->speed;	ecmd->duplex = skge->duplex;	return 0;}static int skge_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd){	struct skge_port *skge = netdev_priv(dev);	const struct skge_hw *hw = skge->hw;	u32 supported = skge_supported_modes(hw);	if (ecmd->autoneg == AUTONEG_ENABLE) {		ecmd->advertising = supported;		skge->duplex = -1;		skge->speed = -1;	} else {		u32 setting;		switch (ecmd->speed) {		case SPEED_1000:			if (ecmd->duplex == DUPLEX_FULL)				setting = SUPPORTED_1000baseT_Full;			else if (ecmd->duplex == DUPLEX_HALF)				setting = SUPPORTED_1000baseT_Half;			else				return -EINVAL;			break;		case SPEED_100:			if (ecmd->duplex == DUPLEX_FULL)				setting = SUPPORTED_100baseT_Full;			else if (ecmd->duplex == DUPLEX_HALF)				setting = SUPPORTED_100baseT_Half;			else				return -EINVAL;			break;		case SPEED_10:			if (ecmd->duplex == DUPLEX_FULL)				setting = SUPPORTED_10baseT_Full;			else if (ecmd->duplex == DUPLEX_HALF)				setting = SUPPORTED_10baseT_Half;			else				return -EINVAL;			break;		default:			return -EINVAL;		}		if ((setting & supported) == 0)			return -EINVAL;		skge->speed = ecmd->speed;		skge->duplex = ecmd->duplex;	}	skge->autoneg = ecmd->autoneg;	skge->advertising = ecmd->advertising;	if (netif_running(dev))		skge_phy_reset(skge);	return (0);}static void skge_get_drvinfo(struct net_device *dev,			     struct ethtool_drvinfo *info){	struct skge_port *skge = netdev_priv(dev);	strcpy(info->driver, DRV_NAME);	strcpy(info->version, DRV_VERSION);	strcpy(info->fw_version, "N/A");	strcpy(info->bus_info, pci_name(skge->hw->pdev));}static const struct skge_stat {	char 	   name[ETH_GSTRING_LEN];	u16	   xmac_offset;	u16	   gma_offset;} skge_stats[] = {	{ "tx_bytes",		XM_TXO_OK_HI,  GM_TXO_OK_HI },	{ "rx_bytes",		XM_RXO_OK_HI,  GM_RXO_OK_HI },	{ "tx_broadcast",	XM_TXF_BC_OK,  GM_TXF_BC_OK },	{ "rx_broadcast",	XM_RXF_BC_OK,  GM_RXF_BC_OK },	{ "tx_multicast",	XM_TXF_MC_OK,  GM_TXF_MC_OK },	{ "rx_multicast",	XM_RXF_MC_OK,  GM_RXF_MC_OK },	{ "tx_unicast",		XM_TXF_UC_OK,  GM_TXF_UC_OK },	{ "rx_unicast",		XM_RXF_UC_OK,  GM_RXF_UC_OK },	{ "tx_mac_pause",	XM_TXF_MPAUSE, GM_TXF_MPAUSE },	{ "rx_mac_pause",	XM_RXF_MPAUSE, GM_RXF_MPAUSE },	{ "collisions",		XM_TXF_SNG_COL, GM_TXF_SNG_COL },	{ "multi_collisions",	XM_TXF_MUL_COL, GM_TXF_MUL_COL },	{ "aborted",		XM_TXF_ABO_COL, GM_TXF_ABO_COL },	{ "late_collision",	XM_TXF_LAT_COL, GM_TXF_LAT_COL },	{ "fifo_underrun",	XM_TXE_FIFO_UR, GM_TXE_FIFO_UR },	{ "fifo_overflow",	XM_RXE_FIFO_OV, GM_RXE_FIFO_OV },	{ "rx_toolong",		XM_RXF_LNG_ERR, GM_RXF_LNG_ERR },	{ "rx_jabber",		XM_RXF_JAB_PKT, GM_RXF_JAB_PKT },	{ "rx_runt",		XM_RXE_RUNT, 	GM_RXE_FRAG },	{ "rx_too_long",	XM_RXF_LNG_ERR, GM_RXF_LNG_ERR },	{ "rx_fcs_error",	XM_RXF_FCS_ERR, GM_RXF_FCS_ERR },};static int skge_get_sset_count(struct net_device *dev, int sset){	switch (sset) {	case ETH_SS_STATS:		return ARRAY_SIZE(skge_stats);	default:		return -EOPNOTSUPP;	}}static void skge_get_ethtool_stats(struct net_device *dev,				   struct ethtool_stats *stats, u64 *data){	struct skge_port *skge = netdev_priv(dev);	if (skge->hw->chip_id == CHIP_ID_GENESIS)		genesis_get_stats(skge, data);	else		yukon_get_stats(skge, data);}/* Use hardware MIB variables for critical path statistics and * transmit feedback not reported at interrupt. * Other errors are accounted for in interrupt handler. */static struct net_device_stats *skge_get_stats(struct net_device *dev){	struct skge_port *skge = netdev_priv(dev);	u64 data[ARRAY_SIZE(skge_stats)];	if (skge->hw->chip_id == CHIP_ID_GENESIS)		genesis_get_stats(skge, data);	else		yukon_get_stats(skge, data);	dev->stats.tx_bytes = data[0];	dev->stats.rx_bytes = data[1];	dev->stats.tx_packets = data[2] + data[4] + data[6];	dev->stats.rx_packets = data[3] + data[5] + data[7];	dev->stats.multicast = data[3] + data[5];	dev->stats.collisions = data[10];	dev->stats.tx_aborted_errors = data[12];	return &dev->stats;}static void skge_get_strings(struct net_device *dev, u32 stringset, u8 *data){	int i;	switch (stringset) {	case ETH_SS_STATS:		for (i = 0; i < ARRAY_SIZE(skge_stats); i++)			memcpy(data + i * ETH_GSTRING_LEN,			       skge_stats[i].name, ETH_GSTRING_LEN);		break;	}}static void skge_get_ring_param(struct net_device *dev,				struct ethtool_ringparam *p){	struct skge_port *skge = netdev_priv(dev);	p->rx_max_pending = MAX_RX_RING_SIZE;	p->tx_max_pending = MAX_TX_RING_SIZE;	p->rx_mini_max_pending = 0;	p->rx_jumbo_max_pending = 0;	p->rx_pending = skge->rx_ring.count;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?