cassini.c
来自「linux 内核源代码」· C语言 代码 · 共 2,335 行 · 第 1/5 页
C
2,335 行
/* cassini.c: Sun Microsystems Cassini(+) ethernet driver. * * Copyright (C) 2004 Sun Microsystems Inc. * Copyright (C) 2003 Adrian Sun (asun@darksunrising.com) * * 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, or (at your option) any later version. * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * This driver uses the sungem driver (c) David Miller * (davem@redhat.com) as its basis. * * The cassini chip has a number of features that distinguish it from * the gem chip: * 4 transmit descriptor rings that are used for either QoS (VLAN) or * load balancing (non-VLAN mode) * batching of multiple packets * multiple CPU dispatching * page-based RX descriptor engine with separate completion rings * Gigabit support (GMII and PCS interface) * MIF link up/down detection works * * RX is handled by page sized buffers that are attached as fragments to * the skb. here's what's done: * -- driver allocates pages at a time and keeps reference counts * on them. * -- the upper protocol layers assume that the header is in the skb * itself. as a result, cassini will copy a small amount (64 bytes) * to make them happy. * -- driver appends the rest of the data pages as frags to skbuffs * and increments the reference count * -- on page reclamation, the driver swaps the page with a spare page. * if that page is still in use, it frees its reference to that page, * and allocates a new page for use. otherwise, it just recycles the * the page. * * NOTE: cassini can parse the header. however, it's not worth it * as long as the network stack requires a header copy. * * TX has 4 queues. currently these queues are used in a round-robin * fashion for load balancing. They can also be used for QoS. for that * to work, however, QoS information needs to be exposed down to the driver * level so that subqueues get targetted to particular transmit rings. * alternatively, the queues can be configured via use of the all-purpose * ioctl. * * RX DATA: the rx completion ring has all the info, but the rx desc * ring has all of the data. RX can conceivably come in under multiple * interrupts, but the INT# assignment needs to be set up properly by * the BIOS and conveyed to the driver. PCI BIOSes don't know how to do * that. also, the two descriptor rings are designed to distinguish between * encrypted and non-encrypted packets, but we use them for buffering * instead. * * by default, the selective clear mask is set up to process rx packets. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/compiler.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/list.h>#include <linux/dma-mapping.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/ethtool.h>#include <linux/crc32.h>#include <linux/random.h>#include <linux/mii.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/mutex.h>#include <net/checksum.h>#include <asm/atomic.h>#include <asm/system.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#define cas_page_map(x) kmap_atomic((x), KM_SKB_DATA_SOFTIRQ)#define cas_page_unmap(x) kunmap_atomic((x), KM_SKB_DATA_SOFTIRQ)#define CAS_NCPUS num_online_cpus()#if defined(CONFIG_CASSINI_NAPI) && defined(HAVE_NETDEV_POLL)#define USE_NAPI#define cas_skb_release(x) netif_receive_skb(x)#else#define cas_skb_release(x) netif_rx(x)#endif/* select which firmware to use */#define USE_HP_WORKAROUND#define HP_WORKAROUND_DEFAULT /* select which firmware to use as default */#define CAS_HP_ALT_FIRMWARE cas_prog_null /* alternate firmware */#include "cassini.h"#define USE_TX_COMPWB /* use completion writeback registers */#define USE_CSMA_CD_PROTO /* standard CSMA/CD */#define USE_RX_BLANK /* hw interrupt mitigation */#undef USE_ENTROPY_DEV /* don't test for entropy device *//* NOTE: these aren't useable unless PCI interrupts can be assigned. * also, we need to make cp->lock finer-grained. */#undef USE_PCI_INTB#undef USE_PCI_INTC#undef USE_PCI_INTD#undef USE_QOS#undef USE_VPD_DEBUG /* debug vpd information if defined *//* rx processing options */#define USE_PAGE_ORDER /* specify to allocate large rx pages */#define RX_DONT_BATCH 0 /* if 1, don't batch flows */#define RX_COPY_ALWAYS 0 /* if 0, use frags */#define RX_COPY_MIN 64 /* copy a little to make upper layers happy */#undef RX_COUNT_BUFFERS /* define to calculate RX buffer stats */#define DRV_MODULE_NAME "cassini"#define PFX DRV_MODULE_NAME ": "#define DRV_MODULE_VERSION "1.5"#define DRV_MODULE_RELDATE "4 Jan 2008"#define CAS_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK | \ NETIF_MSG_TIMER | \ NETIF_MSG_IFDOWN | \ NETIF_MSG_IFUP | \ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR)/* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */#define CAS_TX_TIMEOUT (HZ)#define CAS_LINK_TIMEOUT (22*HZ/10)#define CAS_LINK_FAST_TIMEOUT (1)/* timeout values for state changing. these specify the number * of 10us delays to be used before giving up. */#define STOP_TRIES_PHY 1000#define STOP_TRIES 5000/* specify a minimum frame size to deal with some fifo issues * max mtu == 2 * page size - ethernet header - 64 - swivel = * 2 * page_size - 0x50 */#define CAS_MIN_FRAME 97#define CAS_1000MB_MIN_FRAME 255#define CAS_MIN_MTU 60#define CAS_MAX_MTU min(((cp->page_size << 1) - 0x50), 9000)#if 1/* * Eliminate these and use separate atomic counters for each, to * avoid a race condition. */#else#define CAS_RESET_MTU 1#define CAS_RESET_ALL 2#define CAS_RESET_SPARE 3#endifstatic char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";static int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */static int link_mode;MODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)");MODULE_DESCRIPTION("Sun Cassini(+) ethernet driver");MODULE_LICENSE("GPL");module_param(cassini_debug, int, 0);MODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value");module_param(link_mode, int, 0);MODULE_PARM_DESC(link_mode, "default link mode");/* * Work around for a PCS bug in which the link goes down due to the chip * being confused and never showing a link status of "up." */#define DEFAULT_LINKDOWN_TIMEOUT 5/* * Value in seconds, for user input. */static int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT;module_param(linkdown_timeout, int, 0);MODULE_PARM_DESC(linkdown_timeout,"min reset interval in sec. for PCS linkdown issue; disabled if not positive");/* * value in 'ticks' (units used by jiffies). Set when we init the * module because 'HZ' in actually a function call on some flavors of * Linux. This will default to DEFAULT_LINKDOWN_TIMEOUT * HZ. */static int link_transition_timeout;static u16 link_modes[] __devinitdata = { BMCR_ANENABLE, /* 0 : autoneg */ 0, /* 1 : 10bt half duplex */ BMCR_SPEED100, /* 2 : 100bt half duplex */ BMCR_FULLDPLX, /* 3 : 10bt full duplex */ BMCR_SPEED100|BMCR_FULLDPLX, /* 4 : 100bt full duplex */ CAS_BMCR_SPEED1000|BMCR_FULLDPLX /* 5 : 1000bt full duplex */};static struct pci_device_id cas_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { 0, }};MODULE_DEVICE_TABLE(pci, cas_pci_tbl);static void cas_set_link_modes(struct cas *cp);static inline void cas_lock_tx(struct cas *cp){ int i; for (i = 0; i < N_TX_RINGS; i++) spin_lock(&cp->tx_lock[i]);}static inline void cas_lock_all(struct cas *cp){ spin_lock_irq(&cp->lock); cas_lock_tx(cp);}/* WTZ: QA was finding deadlock problems with the previous * versions after long test runs with multiple cards per machine. * See if replacing cas_lock_all with safer versions helps. The * symptoms QA is reporting match those we'd expect if interrupts * aren't being properly restored, and we fixed a previous deadlock * with similar symptoms by using save/restore versions in other * places. */#define cas_lock_all_save(cp, flags) \do { \ struct cas *xxxcp = (cp); \ spin_lock_irqsave(&xxxcp->lock, flags); \ cas_lock_tx(xxxcp); \} while (0)static inline void cas_unlock_tx(struct cas *cp){ int i; for (i = N_TX_RINGS; i > 0; i--) spin_unlock(&cp->tx_lock[i - 1]);}static inline void cas_unlock_all(struct cas *cp){ cas_unlock_tx(cp); spin_unlock_irq(&cp->lock);}#define cas_unlock_all_restore(cp, flags) \do { \ struct cas *xxxcp = (cp); \ cas_unlock_tx(xxxcp); \ spin_unlock_irqrestore(&xxxcp->lock, flags); \} while (0)static void cas_disable_irq(struct cas *cp, const int ring){ /* Make sure we won't get any more interrupts */ if (ring == 0) { writel(0xFFFFFFFF, cp->regs + REG_INTR_MASK); return; } /* disable completion interrupts and selectively mask */ if (cp->cas_flags & CAS_FLAG_REG_PLUS) { switch (ring) {#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)#ifdef USE_PCI_INTB case 1:#endif#ifdef USE_PCI_INTC case 2:#endif#ifdef USE_PCI_INTD case 3:#endif writel(INTRN_MASK_CLEAR_ALL | INTRN_MASK_RX_EN, cp->regs + REG_PLUS_INTRN_MASK(ring)); break;#endif default: writel(INTRN_MASK_CLEAR_ALL, cp->regs + REG_PLUS_INTRN_MASK(ring)); break; } }}static inline void cas_mask_intr(struct cas *cp){ int i; for (i = 0; i < N_RX_COMP_RINGS; i++) cas_disable_irq(cp, i);}static void cas_enable_irq(struct cas *cp, const int ring){ if (ring == 0) { /* all but TX_DONE */ writel(INTR_TX_DONE, cp->regs + REG_INTR_MASK); return; } if (cp->cas_flags & CAS_FLAG_REG_PLUS) { switch (ring) {#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)#ifdef USE_PCI_INTB case 1:#endif#ifdef USE_PCI_INTC case 2:#endif#ifdef USE_PCI_INTD case 3:#endif writel(INTRN_MASK_RX_EN, cp->regs + REG_PLUS_INTRN_MASK(ring)); break;#endif default: break; } }}static inline void cas_unmask_intr(struct cas *cp){ int i; for (i = 0; i < N_RX_COMP_RINGS; i++) cas_enable_irq(cp, i);}static inline void cas_entropy_gather(struct cas *cp){#ifdef USE_ENTROPY_DEV if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0) return; batch_entropy_store(readl(cp->regs + REG_ENTROPY_IV), readl(cp->regs + REG_ENTROPY_IV), sizeof(uint64_t)*8);#endif}static inline void cas_entropy_reset(struct cas *cp){#ifdef USE_ENTROPY_DEV if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0) return; writel(BIM_LOCAL_DEV_PAD | BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_EXT, cp->regs + REG_BIM_LOCAL_DEV_EN); writeb(ENTROPY_RESET_STC_MODE, cp->regs + REG_ENTROPY_RESET); writeb(0x55, cp->regs + REG_ENTROPY_RAND_REG); /* if we read back 0x0, we don't have an entropy device */ if (readb(cp->regs + REG_ENTROPY_RAND_REG) == 0) cp->cas_flags &= ~CAS_FLAG_ENTROPY_DEV;#endif}/* access to the phy. the following assumes that we've initialized the MIF to * be in frame rather than bit-bang mode */static u16 cas_phy_read(struct cas *cp, int reg){ u32 cmd; int limit = STOP_TRIES_PHY; cmd = MIF_FRAME_ST | MIF_FRAME_OP_READ; cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr); cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg); cmd |= MIF_FRAME_TURN_AROUND_MSB; writel(cmd, cp->regs + REG_MIF_FRAME); /* poll for completion */ while (limit-- > 0) { udelay(10); cmd = readl(cp->regs + REG_MIF_FRAME); if (cmd & MIF_FRAME_TURN_AROUND_LSB) return (cmd & MIF_FRAME_DATA_MASK); } return 0xFFFF; /* -1 */}static int cas_phy_write(struct cas *cp, int reg, u16 val){ int limit = STOP_TRIES_PHY; u32 cmd; cmd = MIF_FRAME_ST | MIF_FRAME_OP_WRITE; cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr); cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg); cmd |= MIF_FRAME_TURN_AROUND_MSB; cmd |= val & MIF_FRAME_DATA_MASK; writel(cmd, cp->regs + REG_MIF_FRAME); /* poll for completion */ while (limit-- > 0) { udelay(10); cmd = readl(cp->regs + REG_MIF_FRAME); if (cmd & MIF_FRAME_TURN_AROUND_LSB) return 0; } return -1;}static void cas_phy_powerup(struct cas *cp){ u16 ctl = cas_phy_read(cp, MII_BMCR); if ((ctl & BMCR_PDOWN) == 0) return; ctl &= ~BMCR_PDOWN; cas_phy_write(cp, MII_BMCR, ctl);}static void cas_phy_powerdown(struct cas *cp){ u16 ctl = cas_phy_read(cp, MII_BMCR); if (ctl & BMCR_PDOWN) return; ctl |= BMCR_PDOWN;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?