📄 epic100.c
字号:
/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. *//* Written 1997-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. All other rights reserved. This driver is for the SMC83c170/175 "EPIC" series, as used on the SMC EtherPower II 9432 PCI adapter, and several CardBus cards. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 Support and updates available at http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html*/static const char *version ="epic100.c:v1.03 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";/* A few user-configurable values. *//* Keep the ring sizes a power of two for efficiency. Making the Tx ring too large decreases the effectiveness of channel bonding and packet priority. There are no ill effects from too-large receive rings. */#define TX_RING_SIZE 16#define RX_RING_SIZE 32/* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1518 effectively disables this feature. */static int rx_copybreak = 200;/* Maximum events (Rx packets, etc.) to handle at each interrupt. */static int max_interrupt_work = 10;/* Operational parameters that usually are not changed. *//* Time in jiffies before concluding the transmitter is hung. */#define TX_TIMEOUT ((2000*HZ)/1000)#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*//* Bytes transferred to chip before transmission starts. */#define TX_FIFO_THRESH 256 /* Rounded down to 4 byte units. */#define RX_FIFO_THRESH 1 /* 0-3, 0==32, 64,96, or 3==128 bytes */#include <linux/config.h>#include <linux/version.h> /* Evil, but neccessary */#ifdef MODULE#ifdef MODVERSIONS#include <linux/modversions.h>#endif#include <linux/module.h>#else#define MOD_INC_USE_COUNT#define MOD_DEC_USE_COUNT#endif#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/malloc.h>#include <linux/interrupt.h>#include <linux/pci.h>#if LINUX_VERSION_CODE >= 0x20155#define PCI_SUPPORT_VER2#else#include <linux/bios32.h>#endif#include <linux/delay.h>#include <asm/processor.h> /* Processor type for cache alignment. */#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>/* Kernel compatibility defines, common to David Hind's PCMCIA package. This is only in the support-all-kernels source code. */#if ! defined (LINUX_VERSION_CODE) || LINUX_VERSION_CODE < 0x20000#warning This driver version is only for kernel versions 2.0.0 and later.#endif#define RUN_AT(x) (jiffies + (x))#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");MODULE_PARM(debug, "i");MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");MODULE_PARM(rx_copybreak, "i");MODULE_PARM(max_interrupt_work, "i");#endif#if LINUX_VERSION_CODE < 0x20123#define test_and_set_bit(val, addr) set_bit(val, addr)#endif#if LINUX_VERSION_CODE <= 0x20139#define net_device_stats enet_statistics#define NETSTATS_VER2#endif#if LINUX_VERSION_CODE < 0x20159#define DEV_FREE_SKB(skb) dev_kfree_skb(skb, FREE_WRITE);#else /* Grrr, unneeded incompatible change. */#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);#endif/* The I/O extent. */#define EPIC_TOTAL_SIZE 0x100static int epic_debug = 1;/* Theory of OperationI. Board CompatibilityThis device driver is designed for the SMC "EPCI/100", the SMCsingle-chip Ethernet controllers for PCI. This chip is used onthe SMC EtherPower II boards.II. Board-specific settingsPCI bus devices are configured by the system at boot time, so no jumpersneed to be set on the board. The system BIOS will assign thePCI INTA signal to a (preferably otherwise unused) system IRQ line.Note: Kernel versions earlier than 1.3.73 do not support shared PCIinterrupt lines.III. Driver operationIIIa. Ring buffersIVb. Referenceshttp://www.smc.com/components/catalog/smc83c170.htmlhttp://cesdis.gsfc.nasa.gov/linux/misc/NWay.htmlhttp://www.national.com/pf/DP/DP83840.htmlIVc. Errata*//* The rest of these values should never change. */static struct device *epic_probe1(int pci_bus, int pci_devfn, struct device *dev, int card_idx);enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,};struct chip_info { const char *name; u16 vendor_id, device_id, device_id_mask, pci_flags; int io_size, min_latency; struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, int chip_idx);} chip_tbl[] = { {"SMSC EPIC/100", 0x10B8, 0x0005, 0x7fff, PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1}, {0,},};/* Offsets to registers, using the (ugh) SMC names. */enum epic_registers { COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14, PCIBurstCnt=0x18, TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28, /* Rx error counters. */ MIICtrl=0x30, MIIData=0x34, MIICfg=0x38, LAN0=64, /* MAC address. */ MC0=80, /* Multicast filter table. */ RxCtrl=96, TxCtrl=112, TxSTAT=0x74, PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC,};/* Interrupt register bits, using my own meaningful names. */enum IntrStatus { TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000, PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000, RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100, TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010, RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001,};/* The EPIC100 Rx and Tx buffer descriptors. */struct epic_tx_desc { s16 status; u16 txlength; u32 bufaddr; u16 buflength; u16 control; u32 next;};struct epic_rx_desc { s16 status; u16 rxlength; u32 bufaddr; u32 buflength; u32 next;};struct epic_private { char devname[8]; /* Used only for kernel debugging. */ const char *product_name; struct device *next_module; /* Rx and Rx rings here so that they remain paragraph aligned. */ struct epic_rx_desc rx_ring[RX_RING_SIZE]; struct epic_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; /* Ring pointers. */ unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ u8 pci_bus, pci_dev_fn; /* PCI bus location. */ u16 chip_id; struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ unsigned char mc_filter[8]; signed char phys[4]; /* MII device addresses. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Current duplex setting. */ unsigned int force_fd:1; /* Full-duplex operation requested. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ unsigned int mediasense:1; /* Media sensing in progress. */ int pad0, pad1; /* Used for 8-byte alignment */};/* Used to pass the full-duplex flag, etc. */#define MAX_UNITS 8static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};static int epic_open(struct device *dev);static int read_eeprom(long ioaddr, int location);static int mdio_read(long ioaddr, int phy_id, int location);static void mdio_write(long ioaddr, int phy_id, int location, int value);static void epic_restart(struct device *dev);static void epic_timer(unsigned long data);static void epic_tx_timeout(struct device *dev);static void epic_init_ring(struct device *dev);static int epic_start_xmit(struct sk_buff *skb, struct device *dev);static int epic_rx(struct device *dev);static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);static int epic_close(struct device *dev);static struct net_device_stats *epic_get_stats(struct device *dev);static void set_rx_mode(struct device *dev);/* A list of all installed EPIC devices, for removing the driver module. */static struct device *root_epic_dev = NULL;#ifndef CARDBUSint epic100_probe(struct device *dev){ int cards_found = 0; int chip_idx; u16 pci_command, new_command; unsigned char pci_bus, pci_device_fn;#ifdef PCI_SUPPORT_VER2 struct pci_dev *pcidev = NULL; while ((pcidev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pcidev)) != NULL) { long pci_ioaddr = pcidev->base_address[0] & ~3; int vendor = pcidev->vendor; int device = pcidev->device; for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++) if (vendor == chip_tbl[chip_idx].vendor_id && (device & chip_tbl[chip_idx].device_id_mask) == chip_tbl[chip_idx].device_id) break; if (chip_tbl[chip_idx].vendor_id == 0 /* Compiled out! */ || check_region(pci_ioaddr, chip_tbl[chip_idx].io_size)) continue; pci_bus = pcidev->bus->number; pci_device_fn = pcidev->devfn;#else int pci_index; if ( ! pcibios_present()) return -ENODEV; for (pci_index = 0; pci_index < 0xff; pci_index++) { u16 vendor, device; u32 pci_ioaddr; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) break; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++) if (vendor == chip_tbl[chip_idx].vendor_id && (device & chip_tbl[chip_idx].device_id_mask) == chip_tbl[chip_idx].device_id) break; if (chip_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ continue; pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr); /* Remove I/O space marker in bit 0. */ pci_ioaddr &= ~3; if (check_region(pci_ioaddr, chip_tbl[chip_idx].io_size)) continue;#endif /* EPIC-specific code: Soft-reset the chip ere setting as master. */ outl(0x0001, pci_ioaddr + GENCTL); /* Activate the card: fix for brain-damaged Win98 BIOSes. */ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; if (pci_command != new_command) { printk(KERN_INFO " The PCI BIOS has not enabled Ethernet" " device %4.4x-%4.4x." " Updating PCI command %4.4x->%4.4x.\n", vendor, device, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } dev = chip_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, cards_found); /* Check the latency timer. */ if (dev) { u8 pci_latency; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < chip_tbl[chip_idx].min_latency) { printk(KERN_INFO " PCI latency timer (CFLT) value of %d is " "unreasonably low, setting to %d.\n", pci_latency, chip_tbl[chip_idx].min_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, chip_tbl[chip_idx].min_latency); } dev = 0; cards_found++; } } return cards_found ? 0 : -ENODEV;}#endif /* not CARDBUS */static struct device *epic_probe1(int bus, int devfn, struct device *dev, int card_idx){ static int did_version = 0; /* Already printed version info. */ struct epic_private *ep; int i, option = 0, duplex = 0; u16 chip_id; u32 ioaddr; if (epic_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); if (dev && dev->mem_start) { option = dev->mem_start; duplex = (dev->mem_start & 16) ? 1 : 0; } else if (card_idx >= 0 && card_idx < MAX_UNITS) { if (options[card_idx] >= 0) option = options[card_idx]; if (full_duplex[card_idx] >= 0) duplex = full_duplex[card_idx]; } dev = init_etherdev(dev, 0); { /* Grrrr, badly consider interface change. */#if defined(PCI_SUPPORT_VER2) struct pci_dev *pdev = pci_find_slot(bus, devfn); ioaddr = pdev->base_address[0] & ~3; dev->irq = pdev->irq; chip_id = pdev->device;#else u8 irq; u32 ioaddr0; pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr0); pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id); ioaddr = ioaddr0 & ~3; dev->irq = irq;#endif } dev->base_addr = ioaddr; printk(KERN_INFO "%s: SMC EPIC/100 (chip ID %4.4x) at %#3x, IRQ %d, ", dev->name, chip_id, ioaddr, dev->irq); /* Bring the chip out of low-power mode. */ outl(0x4200, ioaddr + GENCTL); /* Magic?! If we don't set this bit the MII interface won't work. */ outl(0x0008, ioaddr + TEST1); /* Turn on the MII transceiver. */ outl(0x12, ioaddr + MIICfg); if (chip_id == 6) outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); outl(0x0200, ioaddr + GENCTL); /* This could also be read from the EEPROM. */ for (i = 0; i < 3; i++) ((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x.\n", dev->dev_addr[i]); if (epic_debug > 1) { printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name); for (i = 0; i < 64; i++) printk(" %4.4x%s", read_eeprom(ioaddr, i), i % 16 == 15 ? "\n" : ""); } /* We do a request_region() to register /proc/ioports info. */ request_region(ioaddr, EPIC_TOTAL_SIZE, "SMC EPIC/100"); /* The data structures must be quadword aligned. */ ep = kmalloc(sizeof(*ep), GFP_KERNEL | GFP_DMA); memset(ep, 0, sizeof(*ep)); dev->priv = ep; ep->next_module = root_epic_dev; root_epic_dev = dev; ep->pci_bus = bus; ep->pci_dev_fn = devfn; ep->chip_id = chip_id; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ { int phy, phy_idx; for (phy = 1, phy_idx = 0; phy < 32 && phy_idx < sizeof(ep->phys); phy++) { int mii_status = mdio_read(ioaddr, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { ep->phys[phy_idx++] = phy; printk(KERN_INFO "%s: MII transceiver #%d control " "%4.4x status %4.4x.\n" KERN_INFO "%s: Autonegotiation advertising %4.4x " "link partner %4.4x.\n", dev->name, phy, mdio_read(ioaddr, phy, 0), mii_status, dev->name, mdio_read(ioaddr, phy, 4), mdio_read(ioaddr, phy, 5)); } } if (phy_idx == 0) { printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n", dev->name); /* Use the known PHY address of the EPII. */ ep->phys[0] = 3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -