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