diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
commit | 78c388aed2b7184182c08428db1de6c872d815f5 (patch) | |
tree | 4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /drivers/net/rtl8139.c | |
parent | eb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff) |
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'drivers/net/rtl8139.c')
-rw-r--r-- | drivers/net/rtl8139.c | 1028 |
1 files changed, 540 insertions, 488 deletions
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c index 69ed516f0..21f69fa3b 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -1,6 +1,6 @@ /* rtl8139.c: A RealTek RTL8129/8139 Fast Ethernet driver for Linux. */ /* - Written 1997 by Donald Becker. + 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. @@ -15,14 +15,27 @@ Support and updates available at http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html + + Twister-tuning code contributed by Kinston <shangh@realtek.com.tw>. */ static const char *version = -"rtl8139.c:v0.14 12/9/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n"; +"rtl8139.c:v1.04 9/22/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n"; /* A few user-configurable values. */ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 10; +static int max_interrupt_work = 20; +#define rtl8129_debug debug +static int rtl8129_debug = 1; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* Used to pass the full-duplex flag, etc. */ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* Size of the in-memory receive ring. */ #define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ @@ -37,101 +50,61 @@ static int max_interrupt_work = 10; /* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */ #define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ #define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ -#define TX_DMA_BURST 4 +#define TX_DMA_BURST 4 /* Calculate as 16<<val. */ /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((4000*HZ)/1000) +#define TX_TIMEOUT (4*HZ) -#include <linux/config.h> -#ifdef MODULE -#ifdef MODVERSIONS -#include <linux/modversions.h> -#endif #include <linux/module.h> #include <linux/version.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> -#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> +#include <asm/processor.h> /* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> -/* Kernel compatibility defines, common to David Hind's PCMCIA package. +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. This is only in the support-all-kernels source code. */ -#include <linux/version.h> /* Evil, but neccessary */ - -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 -#define RUN_AT(x) (x) /* What to put in timer->expires. */ -#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#define virt_to_bus(addr) ((unsigned long)addr) -#define bus_to_virt(addr) ((void*)addr) -#else /* 1.3.0 and later */ #define RUN_AT(x) (jiffies + (x)) -#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) -#endif - -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 -#ifdef MODULE -#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) -char kernel_version[] = UTS_RELEASE; -#endif -#else -#undef MOD_INC_USE_COUNT -#define MOD_INC_USE_COUNT -#undef MOD_DEC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif -#endif /* 1.3.38 */ -#if (LINUX_VERSION_CODE >= 0x10344) -#define NEW_MULTICAST #include <linux/delay.h> -#endif -#ifdef SA_SHIRQ -#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) -#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) -#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#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 #else -#define FREE_IRQ(irqnum, dev) free_irq(irqnum) -#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) -#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#define NETSTATS_VER2 #endif - -#if (LINUX_VERSION_CODE < 0x20123) -#define test_and_set_bit(val, addr) set_bit(val, addr) +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ #include <linux/bios32.h> +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); #endif /* The I/O extent. */ #define RTL8129_TOTAL_SIZE 0x80 -#ifdef HAVE_DEVLIST -struct netdev_entry rtl8139_drv = -{"RTL8139", rtl8139_probe, RTL8129_TOTAL_SIZE, NULL}; -#endif - -static int rtl8129_debug = 1; - /* Theory of Operation @@ -177,16 +150,45 @@ http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html IVc. Errata */ + + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +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 pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +static struct device * rtl8129_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chp_idx, int fnd_cnt); + +static struct pci_id_info pci_tbl[] = +{{ "RealTek RTL8129 Fast Ethernet", + 0x10ec, 0x8129, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + { "RealTek RTL8139 Fast Ethernet", + 0x10ec, 0x8139, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + { "RealTek RTL8139 Fast Ethernet (mislabeled)", + 0x1113, 0x1211, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + {0,}, /* 0 terminated list. */ +}; + +/* The capability table matches the chip table above. */ +enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04}; +static int rtl_cap_tbl[] = { + HAS_MII_XCVR, HAS_CHIP_XCVR|HAS_LNK_CHNG, HAS_CHIP_XCVR|HAS_LNK_CHNG, +}; -#ifndef PCI_VENDOR_ID_REALTEK -#define PCI_VENDOR_ID_REALTEK 0x10ec -#endif -#ifndef PCI_DEVICE_ID_REALTEK_8129 -#define PCI_DEVICE_ID_REALTEK_8129 0x8129 -#endif -#ifndef PCI_DEVICE_ID_REALTEK_8139 -#define PCI_DEVICE_ID_REALTEK_8139 0x8139 -#endif /* The rest of these values should never change. */ #define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */ @@ -195,7 +197,7 @@ IVc. Errata enum RTL8129_registers { MAC0=0, /* Ethernet hardware address. */ MAR0=8, /* Multicast filter. */ - TxStat0=0x10, /* Transmit status (Four 32bit registers). */ + TxStatus0=0x10, /* Transmit status (Four 32bit registers). */ TxAddr0=0x20, /* Tx descriptors (also four 32bit). */ RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36, ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A, @@ -206,7 +208,12 @@ enum RTL8129_registers { Cfg9346=0x50, Config0=0x51, Config1=0x52, FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B, MultiIntr=0x5C, TxSummary=0x60, - BMCR=0x62, BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, NWayExpansion=0x6A, + MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, + NWayExpansion=0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS=0x70, /* FIFO Test Mode Select */ + CSCR=0x74, /* Chip Status and Configuration Register. */ + PARA78=0x78, PARA7c=0x7c, /* Magic transceiver parameter register. */ }; enum ChipCmdBits { @@ -228,25 +235,44 @@ enum RxStatusBits { RxBadAlign=0x0002, RxStatusOK=0x0001, }; +enum CSCRBits { + CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800, + CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0, + CSCR_LinkDownCmd=0x0f3c0, +}; + +/* Twister tuning parameters from RealTek. Completely undocumented. */ +unsigned long param[4][4]={ + {0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43}, + {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83}, + {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83}, + {0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83} +}; + struct rtl8129_private { char devname[8]; /* Used only for kernel debugging. */ const char *product_name; struct device *next_module; int chip_id; int chip_revision; + unsigned char pci_bus, pci_devfn; +#if LINUX_VERSION_CODE > 0x20139 + struct net_device_stats stats; +#else struct enet_statistics stats; +#endif struct timer_list timer; /* Media selection timer. */ - unsigned int cur_rx, cur_tx; /* The next free and used entries */ - unsigned int dirty_rx, dirty_tx; + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int cur_tx, dirty_tx, tx_flag; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[NUM_TX_DESC]; unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ unsigned char *rx_ring; unsigned char *tx_bufs; /* Tx bounce buffer region. */ - unsigned char mc_filter[8]; /* Current multicast filter. */ char phys[4]; /* MII device addresses. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock: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. */ @@ -254,24 +280,21 @@ struct rtl8129_private { }; #ifdef MODULE -/* Used to pass the full-duplex flag, etc. */ -static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -#if LINUX_VERSION_CODE > 0x20118 +#if LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); MODULE_DESCRIPTION("RealTek RTL8129/8139 Fast 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(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); #endif #endif -static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, - int chip_id, int options, int card_idx); static int rtl8129_open(struct device *dev); -static int read_eeprom(int ioaddr, int location); -static int mdio_read(int ioaddr, int phy_id, int location); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int val); static void rtl8129_timer(unsigned long data); static void rtl8129_tx_timeout(struct device *dev); static void rtl8129_init_ring(struct device *dev); @@ -279,161 +302,138 @@ static int rtl8129_start_xmit(struct sk_buff *skb, struct device *dev); static int rtl8129_rx(struct device *dev); static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int rtl8129_close(struct device *dev); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); static struct enet_statistics *rtl8129_get_stats(struct device *dev); -#ifdef NEW_MULTICAST +static inline u32 ether_crc(int length, unsigned char *data); static void set_rx_mode(struct device *dev); -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); -#endif - -#ifdef MODULE /* A list of all installed RTL8129 devices, for removing the driver module. */ static struct device *root_rtl8129_dev = NULL; -#endif + +/* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well when dynamically adding drivers. So instead we detect just the + Rtl81*9 cards in slot order. */ int rtl8139_probe(struct device *dev) { int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Rtl81*9 cards in slot order. */ + if ( ! pcibios_present()) + return -ENODEV; - if (pci_present()) { - unsigned char pci_bus, pci_device_fn; + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + int chip_idx, irq; + long ioaddr; - for (;pci_index < 0xff; pci_index++) { - unsigned char pci_latency; -#if LINUX_VERSION_CODE >= 0x20155 - unsigned int pci_irq_line; - struct pci_dev *pdev; -#else - unsigned char pci_irq_line; -#endif - unsigned short pci_command, new_command, vendor, device; - unsigned int pci_ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, -#ifdef REVERSE_PROBE_ORDER - 0xff - pci_index, -#else - pci_index, -#endif - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) + 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; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) break; - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_VENDOR_ID, &vendor); - if (vendor != PCI_VENDOR_ID_REALTEK) - continue; - - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_DEVICE_ID, &device); -#if LINUX_VERSION_CODE >= 0x20155 - pdev = pci_find_slot(pci_bus, pci_device_fn); - pci_irq_line = pdev->irq; - pci_ioaddr = pdev->base_address[0]; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; #else + u32 pci_ioaddr; + u8 pci_irq_line; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_irq_line); pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr); + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; #endif - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - - if (device != PCI_DEVICE_ID_REALTEK_8129 - && device != PCI_DEVICE_ID_REALTEK_8139) { - printk(KERN_NOTICE"Unknown RealTek PCI ethernet chip type " - "%4.4x detected: not configured.\n", device); - continue; - } - if (check_region(pci_ioaddr, RTL8129_TOTAL_SIZE)) - continue; - - 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 this" - " device! Updating PCI config %4.4x->%4.4x.\n", - pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + } -#ifdef MODULE - dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device, - options[cards_found], cards_found); -#else - dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device, - dev ? dev->mem_start : 0, -1); -#endif + if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && + check_region(ioaddr, pci_tbl[chip_idx].io_size)) + continue; + + /* 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_tbl[chip_idx].flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); - if (dev) { - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 32) { - printk(KERN_NOTICE" PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to 64 clocks.\n", - pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 64); - } else if (rtl8129_debug > 1) - printk(KERN_INFO" PCI latency timer (CFLT) is %#x.\n", - pci_latency); - dev = 0; - cards_found++; + if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { + printk(KERN_NOTICE " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); } } + dev = 0; + cards_found++; } -#if defined (MODULE) - return cards_found; -#else return cards_found ? 0 : -ENODEV; -#endif } -static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, - int chip_id, int options, int card_idx) +static struct device * rtl8129_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chip_idx, int found_cnt) { static int did_version = 0; /* Already printed version info. */ struct rtl8129_private *tp; - int i; + int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0; if (rtl8129_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); - printk(KERN_INFO "%s: RealTek RTL%x at %#3x, IRQ %d, ", - dev->name, chip_id, ioaddr, irq); + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", + dev->name, pci_tbl[chip_idx].name, ioaddr, irq); /* Bring the chip out of low-power mode. */ outb(0x00, ioaddr + Config1); - /* Perhaps this should be read from the EEPROM? */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = inb(ioaddr + MAC0 + i); + if (read_eeprom(ioaddr, 0) != 0xffff) + for (i = 0; i < 3; i++) + ((u16 *)(dev->dev_addr))[i] = read_eeprom(ioaddr, i + 7); + else + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(ioaddr + MAC0 + i); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x.\n", dev->dev_addr[i]); - if (rtl8129_debug > 1) { - printk(KERN_INFO "%s: EEPROM contents\n", dev->name); - for (i = 0; i < 64; i++) - printk(" %4.4x%s", read_eeprom(ioaddr, i), - i%16 == 15 ? "\n"KERN_INFO : ""); - } - /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, RTL8129_TOTAL_SIZE, "RealTek RTL8129/39 Fast Ethernet"); + request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; @@ -443,22 +443,21 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, memset(tp, 0, sizeof(*tp)); dev->priv = tp; -#ifdef MODULE tp->next_module = root_rtl8129_dev; root_rtl8129_dev = dev; -#endif - tp->chip_id = chip_id; + tp->chip_id = chip_idx; + tp->pci_bus = pci_bus; + tp->pci_devfn = pci_devfn; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ - if (chip_id == 0x8129) { + if (rtl_cap_tbl[chip_idx] & HAS_MII_XCVR) { int phy, phy_idx; for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(ioaddr, phy, 1); - + int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { tp->phys[phy_idx++] = phy; printk(KERN_INFO "%s: MII transceiver found at address %d.\n", @@ -472,7 +471,7 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, tp->phys[0] = -1; } } else { - tp->phys[0] = -1; + tp->phys[0] = 32; } /* Put the chip into low-power mode. */ @@ -481,18 +480,21 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ /* The lower four bits are the media type. */ - if (options > 0) { - tp->full_duplex = (options & 16) ? 1 : 0; - tp->default_port = options & 15; + if (option > 0) { + tp->full_duplex = (option & 0x200) ? 1 : 0; + tp->default_port = option & 15; if (tp->default_port) tp->medialock = 1; } -#ifdef MODULE - if (card_idx >= 0) { - if (full_duplex[card_idx] >= 0) - tp->full_duplex = full_duplex[card_idx]; + + if (found_cnt < MAX_UNITS && full_duplex[found_cnt] > 0) + tp->full_duplex = full_duplex[found_cnt]; + + if (tp->full_duplex) { + printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + mdio_write(dev, tp->phys[0], 4, 0x141); + tp->duplex_lock = 1; } -#endif /* The Rtl8129-specific entries in the device structure. */ dev->open = &rtl8129_open; @@ -500,6 +502,7 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, dev->stop = &rtl8129_close; dev->get_stats = &rtl8129_get_stats; dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; return dev; } @@ -516,24 +519,21 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, #define EE_ENB (0x80 | EE_CS) /* Delay between EEPROM clock transitions. - The 1.2 code is a "nasty" timing loop, but PC compatible machines are - *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ -#ifdef _LINUX_DELAY_H -#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) -#else -#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) -#endif + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() inl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) #define EE_READ_CMD (6 << 6) #define EE_ERASE_CMD (7 << 6) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location) { int i; - unsigned short retval = 0; - short ee_addr = ioaddr + Cfg9346; + unsigned retval = 0; + long ee_addr = ioaddr + Cfg9346; int read_cmd = location | EE_READ_CMD; outb(EE_ENB & ~EE_CS, ee_addr); @@ -541,22 +541,21 @@ static int read_eeprom(int ioaddr, int location) /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(EE_ENB | dataval, ee_addr); - eeprom_delay(100); + eeprom_delay(); outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - outb(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); + eeprom_delay(); } outb(EE_ENB, ee_addr); + eeprom_delay(); for (i = 16; i > 0; i--) { outb(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + eeprom_delay(); retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); outb(EE_ENB, ee_addr); - eeprom_delay(100); + eeprom_delay(); } /* Terminate the EEPROM access. */ @@ -566,43 +565,50 @@ static int read_eeprom(int ioaddr, int location) /* MII serial management: mostly bogus for now. */ /* Read and write the MII management registers using software-generated - serial MDIO protocol. The maxium data clock rate is 2.5 Mhz. */ + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ #define MDIO_DIR 0x80 #define MDIO_DATA_OUT 0x04 #define MDIO_DATA_IN 0x02 #define MDIO_CLK 0x01 -#ifdef _LINUX_DELAY_H -#define mdio_delay() udelay(1) /* Really 400ns. */ -#else -#define mdio_delay() __SLOW_DOWN_IO; -#endif +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() inb(mdio_addr) + +static char mii_2_8139_map[8] = {MII_BMCR, MII_BMSR, 0, 0, NWayAdvert, + NWayLPAR, NWayExpansion, 0 }; /* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync(int ioaddr) +static void mdio_sync(long mdio_addr) { int i; - int mdio_addr = ioaddr + MII_SMI; for (i = 32; i >= 0; i--) { - outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr); + outb(MDIO_WRITE1, mdio_addr); mdio_delay(); - outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr); + outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr); mdio_delay(); } return; } -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(struct device *dev, int phy_id, int location) { - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + long mdio_addr = dev->base_addr + MII_SMI; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; - int mdio_addr = ioaddr + MII_SMI; + int i; - mdio_sync(ioaddr); + if ((phy_id & 0x1f) == 0) { /* Really a 8139. Use internal registers. */ + return location < 8 && mii_2_8139_map[location] ? + inw(dev->base_addr + mii_2_8139_map[location]) : 0; + } + mdio_sync(mdio_addr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { - int dataval = - (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; outb(MDIO_DIR | dataval, mdio_addr); mdio_delay(); @@ -620,31 +626,52 @@ static int mdio_read(int ioaddr, int phy_id, int location) } return (retval>>1) & 0xffff; } + +static void mdio_write(struct device *dev, int phy_id, int location, int value) +{ + long mdio_addr = dev->base_addr + MII_SMI; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (phy_id == 32) { /* Really a 8139. Use internal registers. */ + if (location < 8 && mii_2_8139_map[location]) + outw(value, dev->base_addr + mii_2_8139_map[location]); + return; + } + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + outb(dataval, mdio_addr); + mdio_delay(); + outb(dataval | MDIO_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outb(0, mdio_addr); + mdio_delay(); + outb(MDIO_CLK, mdio_addr); + mdio_delay(); + } + return; +} + static int rtl8129_open(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i; - int full_duplex = 0; /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); -#ifdef SA_SHIRQ - if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, - "RealTek RTL8129/39 Fast Ethernet", dev)) { - return -EAGAIN; - } -#else - if (irq2dev_map[dev->irq] != NULL - || (irq2dev_map[dev->irq] = dev) == NULL - || dev->irq == 0 - || request_irq(dev->irq, &rtl8129_interrupt, 0, "RTL8129")) { + if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } -#endif MOD_INC_USE_COUNT; @@ -660,20 +687,10 @@ rtl8129_open(struct device *dev) } rtl8129_init_ring(dev); -#ifndef final_version - /* Used to monitor rx ring overflow. */ - memset(tp->rx_ring + RX_BUF_LEN, 0xcc, 16); -#endif - /* Check that the chip has finished the reset. */ for (i = 1000; i > 0; i--) if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; -#ifndef final_version - if (rtl8129_debug > 2) - printk(KERN_DEBUG"%s: reset finished with status %2.2x after %d loops.\n", - dev->name, inb(ioaddr + ChipCmd), 1000-i); -#endif for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + MAC0 + i); @@ -683,29 +700,26 @@ rtl8129_open(struct device *dev) outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); + tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000; - full_duplex = tp->full_duplex; - if (tp->phys[0] >= 0 || tp->chip_id == 0x8139) { - u16 mii_reg5; - if (tp->chip_id == 0x8139) - mii_reg5 = inw(ioaddr + NWayLPAR); - else - mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); + tp->full_duplex = tp->duplex_lock; + if (tp->phys[0] >= 0 || (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) { + u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5); if (mii_reg5 == 0xffff) ; /* Not there */ else if ((mii_reg5 & 0x0100) == 0x0100 || (mii_reg5 & 0x00C0) == 0x0040) - full_duplex = 1; + tp->full_duplex = 1; if (rtl8129_debug > 1) printk(KERN_INFO"%s: Setting %s%s-duplex based on" " auto-negotiated partner ability %4.4x.\n", dev->name, mii_reg5 == 0 ? "" : (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ", - full_duplex ? "full" : "half", mii_reg5); + tp->full_duplex ? "full" : "half", mii_reg5); } outb(0xC0, ioaddr + Cfg9346); - outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf); @@ -715,13 +729,6 @@ rtl8129_open(struct device *dev) set_rx_mode(dev); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); -#ifndef final_version - if (rtl8129_debug > 1) - printk(KERN_DEBUG"%s: In rtl8129_open() Tx/Rx Config %8.8x/%8.8x" - " Chip Config %2.2x/%2.2x.\n", - dev->name, inl(ioaddr + TxConfig), inl(ioaddr + RxConfig), - inb(ioaddr + Config0), inb(ioaddr + Config1)); -#endif dev->tbusy = 0; dev->interrupt = 0; @@ -732,10 +739,10 @@ rtl8129_open(struct device *dev) | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); if (rtl8129_debug > 1) - printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d" + printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), - full_duplex ? "full" : "half"); + tp->full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ @@ -752,26 +759,26 @@ static void rtl8129_timer(unsigned long data) { struct device *dev = (struct device *)data; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int next_tick = 0; - - if (tp->chip_id == 0x8139) { - u16 mii_reg5 = inw(ioaddr + NWayLPAR); - if ((mii_reg5 & 0x0100) == 0x0100 - || (mii_reg5 & 0x00C0) == 0x0040) - if ( ! tp->full_duplex) { - tp->full_duplex = 1; - if (rtl8129_debug > 0) - printk(KERN_INFO "%s: Switching to full-duplex based on " - "link partner ability of %4.4x.\n", - dev->name, mii_reg5); - outb(0xC0, ioaddr + Cfg9346); - outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); - outb(0x00, ioaddr + Cfg9346); - } + int mii_reg5 = mdio_read(dev, tp->phys[0], 5); + + if (! tp->duplex_lock && mii_reg5 != 0xffff) { + int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040; + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", dev->name, + tp->full_duplex ? "full" : "half", tp->phys[0], mii_reg5); + outb(0xC0, ioaddr + Cfg9346); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(0x00, ioaddr + Cfg9346); + } + next_tick = 60*HZ; } + if (rtl8129_debug > 2) { - if (tp->chip_id == 0x8129) + if (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR) printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", dev->name, inb(ioaddr + GPPinData)); else @@ -794,33 +801,80 @@ static void rtl8129_timer(unsigned long data) static void rtl8129_tx_timeout(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; - int i; + long ioaddr = dev->base_addr; + int mii_reg, i; if (rtl8129_debug > 0) - printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n", - dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus)); + printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x " + "media %2.2x.\n", + dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus), + inb(ioaddr + GPPinData)); + + /* Disable interrupts by clearing the interrupt mask. */ + outw(0x0000, ioaddr + IntrMask); + /* Emit info to figure out what went wrong. */ + printk("%s: Tx queue start entry %d dirty entry %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); for (i = 0; i < NUM_TX_DESC; i++) printk(KERN_DEBUG"%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, inl(ioaddr + TxStat0 + i*4), + dev->name, i, inl(ioaddr + TxStatus0 + i*4), i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); - if (tp->chip_id == 0x8129) { - int mii_reg; - printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); - for (mii_reg = 0; mii_reg < 8; mii_reg++) - printk(" %4.4x", mdio_read(ioaddr, tp->phys[0], mii_reg)); - printk(".\n"); - } else { - printk(KERN_DEBUG"%s: MII status register is %4.4x.\n", - dev->name, inw(ioaddr + BMSR)); - } - /* Restart the chip Tx process. */ + printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); + for (mii_reg = 0; mii_reg < 8; mii_reg++) + printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); + printk(".\n"); + + /* Soft reset the chip. */ + outb(CmdReset, ioaddr + ChipCmd); + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) + break; + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + MAC0 + i); + + outb(0x00, ioaddr + Cfg9346); + tp->cur_rx = 0; + /* Must enable Tx/Rx before setting transfer thresholds! */ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); - /* Continue from any transmit abort. */ - outl((TX_DMA_BURST<<8) || 0x03000001, ioaddr + TxConfig); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), + ioaddr + RxConfig); + outl((TX_DMA_BURST<<8), ioaddr + TxConfig); + set_rx_mode(dev); + { /* Save the unsent Tx packets. */ + struct sk_buff *saved_skb[NUM_TX_DESC], *skb; + int j; + for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) + saved_skb[j] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; + tp->dirty_tx = tp->cur_tx = 0; + + for (i = 0; i < j; i++) { + skb = tp->tx_skbuff[i] = saved_skb[i]; + if ((long)skb->data & 3) { /* Must use alignment buffer. */ + memcpy(tp->tx_buf[i], skb->data, skb->len); + outl(virt_to_bus(tp->tx_buf[i]), ioaddr + TxAddr0 + i*4); + } else + outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + i*4); + /* Note: the chip doesn't have auto-pad! */ + outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), + ioaddr + TxStatus0 + i*4); + } + tp->cur_tx = i; + while (i < NUM_TX_DESC) + tp->tx_skbuff[i] = 0; + if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ + dev->tbusy = 0; + tp->tx_full = 0; + } else { + tp->tx_full = 1; + } + } dev->trans_start = jiffies; tp->stats.tx_errors++; + /* Enable all known interrupts by setting the interrupt mask. */ + outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver + | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); return; } @@ -833,8 +887,8 @@ rtl8129_init_ring(struct device *dev) int i; tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; + tp->cur_rx = 0; + tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_skbuff[i] = 0; @@ -846,20 +900,9 @@ static int rtl8129_start_xmit(struct sk_buff *skb, struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int entry; -#ifndef final_version - if (skb == NULL || skb->len <= 0) { - printk(KERN_ERR"%s: Obsolete driver Tx request made: skbuff==NULL.\n", - dev->name); -#if 0 - dev_tint(dev); -#endif - return 0; - } -#endif - /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { @@ -879,12 +922,11 @@ rtl8129_start_xmit(struct sk_buff *skb, struct device *dev) } else outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4); /* Note: the chip doesn't have auto-pad! */ - outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | - (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), - ioaddr + TxStat0 + entry*4); + outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), + ioaddr + TxStatus0 + entry*4); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); } else { tp->tx_full = 1; } @@ -892,36 +934,36 @@ rtl8129_start_xmit(struct sk_buff *skb, struct device *dev) dev->trans_start = jiffies; if (rtl8129_debug > 4) printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n", - dev->name, skb->data, skb->len, entry); + dev->name, skb->data, (int)skb->len, entry); return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) +static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { -#ifdef SA_SHIRQ struct device *dev = (struct device *)dev_instance; -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif - struct rtl8129_private *tp; - int ioaddr, boguscnt = max_interrupt_work; + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int boguscnt = max_interrupt_work; int status; + long ioaddr = dev->base_addr; - if (dev == NULL) { - printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n", - irq); +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ return; } - - ioaddr = dev->base_addr; - tp = (struct rtl8129_private *)dev->priv; - if (test_and_set_bit(0, (void*)&dev->interrupt)) { +#else + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } + dev->interrupt = 1; +#endif do { status = inw(ioaddr + IntrStatus); @@ -944,9 +986,9 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) { int entry = dirty_tx % NUM_TX_DESC; - int txstatus = inl(ioaddr + TxStat0 + entry*4); + int txstatus = inl(ioaddr + TxStatus0 + entry*4); - if ( ! (txstatus & TxHostOwns)) + if ( ! (txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* It still hasn't been Txed */ /* Note: TxCarrierLost is always asserted at 100mbps. */ @@ -973,15 +1015,20 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r /* No count for tp->stats.tx_deferred */ #endif if (txstatus & TxUnderrun) { - /* Todo: increase the Tx FIFO threshold. */ + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; tp->stats.tx_fifo_errors++; } tp->stats.collisions += (txstatus >> 24) & 15; +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.tx_bytes += txstatus & 0x7ff; +#endif tp->stats.tx_packets++; } /* Free the original skb. */ - dev_kfree_skb(tp->tx_skbuff[entry]); + dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } @@ -993,8 +1040,7 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r } #endif - if (tp->tx_full && dev->tbusy - && dirty_tx > tp->cur_tx - NUM_TX_DESC) { + if (tp->tx_full && dirty_tx > tp->cur_tx - NUM_TX_DESC) { /* The ring is no longer full, clear tbusy. */ tp->tx_full = 0; dev->tbusy = 0; @@ -1007,17 +1053,29 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r /* Check uncommon events with one test. */ if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver |TxErr|RxErr)) { + if (rtl8129_debug > 2) + printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); -#ifndef final_version - if (rtl8129_debug > 0) - printk(KERN_DEBUG"%s: Unusual error, status %4.4x.\n", - dev->name, status); -#endif - + if (status == 0xffffffff) + break; /* Update the error count. */ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); + if ((status & RxUnderrun) && + (rtl_cap_tbl[tp->chip_id] & HAS_LNK_CHNG)) { + /* Really link-change on new chips. */ + int lpar = inw(ioaddr + NWayLPAR); + int duplex = (lpar&0x0100)||(lpar & 0x01C0) == 0x0040; + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + outb(0xC0, ioaddr + Cfg9346); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(0x00, ioaddr + Cfg9346); + } + status &= ~RxUnderrun; + } if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++; @@ -1028,7 +1086,14 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r tp->cur_rx = inw(ioaddr + RxBufAddr) % RX_BUF_LEN; outw(tp->cur_rx - 16, ioaddr + RxBufPtr); } - /* Error sources cleared above. */ + if (status & PCIErr) { + u32 pci_cmd_status; + pcibios_read_config_dword(tp->pci_bus, tp->pci_devfn, + PCI_COMMAND, &pci_cmd_status); + + printk(KERN_ERR "%s: PCI Bus error %4.4x.\n", + dev->name, pci_cmd_status); + } } if (--boguscnt < 0) { printk(KERN_WARNING"%s: Too much work at interrupt, " @@ -1044,29 +1109,20 @@ static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *r printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, inl(ioaddr + IntrStatus)); -#ifndef final_version - /* Code that should never be run! Perhaps remove after testing.. */ - { - static int stopit = 10; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ERR"%s: Emergency stop, looping startup interrupt.\n", - dev->name); - FREE_IRQ(irq, dev); - } - } -#endif - +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; +#endif return; } -/* Todo: The data sheet doesn't describe the Rx ring at all, so I'm winging - it here until I have a chip to play with. 8/30/97 */ -static int -rtl8129_rx(struct device *dev) +/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the + field alignments and semantics. */ +static int rtl8129_rx(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; unsigned char *rx_ring = tp->rx_ring; u16 cur_rx = tp->cur_rx; @@ -1077,9 +1133,9 @@ rtl8129_rx(struct device *dev) inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd)); while ((inb(ioaddr + ChipCmd) & 1) == 0) { - u16 ring_offset = cur_rx % RX_BUF_LEN; + int ring_offset = cur_rx % RX_BUF_LEN; u32 rx_status = *(u32*)(rx_ring + ring_offset); - u16 rx_size = rx_status >> 16; + int rx_size = rx_status >> 16; if (rtl8129_debug > 4) { int i; @@ -1105,12 +1161,18 @@ rtl8129_rx(struct device *dev) tp->stats.rx_frame_errors++; if (rx_status & (RxRunt|RxTooLong)) tp->stats.rx_length_errors++; if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + tp->cur_rx = 0; + outb(CmdTxEnb, ioaddr + ChipCmd); + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | + (RX_DMA_BURST<<8), ioaddr + RxConfig); } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ struct sk_buff *skb; - skb = DEV_ALLOC_SKB(rx_size + 2); + skb = dev_alloc_skb(rx_size + 2); if (skb == NULL) { printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", dev->name); @@ -1129,26 +1191,32 @@ rtl8129_rx(struct device *dev) rx_size-semi_count); if (rtl8129_debug > 4) { int i; - printk(KERN_DEBUG"%s: Frame wrap @%d", dev->name, semi_count); + printk(KERN_DEBUG"%s: Frame wrap @%d", + dev->name, semi_count); for (i = 0; i < 16; i++) printk(" %2.2x", rx_ring[i]); printk(".\n"); memset(rx_ring, 0xcc, 16); } - } else + } else { +#if 1 /* USE_IP_COPYSUM */ + eth_copy_and_sum(skb, &rx_ring[ring_offset + 4], + rx_size, 0); + skb_put(skb, rx_size); +#else memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4], rx_size); -#if LINUX_VERSION_CODE >= 0x10300 - skb->protocol = eth_type_trans(skb, dev); -#else - skb->len = rx_size; #endif + } + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.rx_bytes += rx_size; +#endif tp->stats.rx_packets++; } - cur_rx += rx_size + 4; - cur_rx = (cur_rx + 3) & ~3; + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; outw(cur_rx - 16, ioaddr + RxBufPtr); } if (rtl8129_debug > 4) @@ -1163,7 +1231,7 @@ rtl8129_rx(struct device *dev) static int rtl8129_close(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; @@ -1186,29 +1254,11 @@ rtl8129_close(struct device *dev) del_timer(&tp->timer); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif - -#ifndef final_version - /* Used to monitor rx ring overflow. */ - for (i = 0; i < 16; i++) - if (tp->rx_ring[RX_BUF_LEN+i] != 0xcc) { - printk(KERN_WARNING"%s: Rx ring overflowed! Values are ", - dev->name); - for (i = 0; i < 16; i++) - printk(" %2.2x", tp->rx_ring[RX_BUF_LEN + i]); - printk(".\n"); - break; - } -#endif for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i]); + dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } kfree(tp->rx_ring); @@ -1224,11 +1274,33 @@ rtl8129_close(struct device *dev) return 0; } +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = tp->phys[0] & 0x3f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + static struct enet_statistics * rtl8129_get_stats(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (dev->start) { tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); @@ -1239,94 +1311,69 @@ rtl8129_get_stats(struct device *dev) } /* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic - when re-entered but still correct. */ - -/* The little-endian AUTODIN II ethernet CRC calculation. - N.B. Do not use for bulk data, use a table-based routine instead. - This is common code and should be moved to net/core/crc.c */ -static unsigned const ethernet_polynomial_le = 0xedb88320U; -static inline unsigned ether_crc_le(int length, unsigned char *data) + This routine is not state sensitive and need not be SMP locked. */ + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) { - unsigned int crc = 0xffffffff; /* Initial value. */ - while(--length >= 0) { + int crc = -1; + + while(--length >= 0) { unsigned char current_octet = *data++; int bit; - for (bit = 8; --bit >= 0; current_octet >>= 1) { - if ((crc ^ current_octet) & 1) { - crc >>= 1; - crc ^= ethernet_polynomial_le; - } else - crc >>= 1; - } - } - return crc; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; } +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, + AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01, +}; -static void -#ifdef NEW_MULTICAST -set_rx_mode(struct device *dev) -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); -#endif +static void set_rx_mode(struct device *dev) { - int ioaddr = dev->base_addr; - struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - unsigned char mc_filter[8]; /* Multicast hash filter */ - int i; + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + if (rtl8129_debug > 3) + printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", + dev->name, dev->flags, inl(ioaddr + RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(0x0F, ioaddr + RxConfig); - } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + rx_mode = AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(0x0E, ioaddr + RxConfig); - } else if (dev->mc_count == 0) { - outb(0x0A, ioaddr + RxConfig); - return; + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; } else { struct dev_mc_list *mclist; - - memset(mc_filter, 0, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, - mc_filter); - } - /* ToDo: perhaps we need to stop the Tx and Rx process here? */ - if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) { - for (i = 0; i < 2; i++) - outl(((u32 *)mc_filter)[i], ioaddr + MAR0 + i*4); - memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter)); + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } - if (rtl8129_debug > 3) - printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", - dev->name, dev->flags, inl(ioaddr + RxConfig)); + /* We can safely update without stopping the chip. */ + outb(rx_mode, ioaddr + RxConfig); + outl(mc_filter[0], ioaddr + MAR0 + 0); + outl(mc_filter[1], ioaddr + MAR0 + 4); return; } #ifdef MODULE - -/* An additional parameter that may be passed in... */ -static int debug = -1; - -int -init_module(void) +int init_module(void) { - int cards_found; - - if (debug >= 0) - rtl8129_debug = debug; - - root_rtl8129_dev = NULL; - cards_found = rtl8139_probe(0); - - return cards_found ? 0 : -ENODEV; + return rtl8139_probe(0); } void @@ -1336,9 +1383,13 @@ cleanup_module(void) /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_rtl8129_dev) { - next_dev = ((struct rtl8129_private *)root_rtl8129_dev->priv)->next_module; + struct rtl8129_private *tp = + (struct rtl8129_private *)root_rtl8129_dev->priv; + next_dev = tp->next_module; unregister_netdev(root_rtl8129_dev); - release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE); + release_region(root_rtl8129_dev->base_addr, + pci_tbl[tp->chip_id].io_size); + kfree(tp); kfree(root_rtl8129_dev); root_rtl8129_dev = next_dev; } @@ -1348,7 +1399,8 @@ cleanup_module(void) /* * Local variables: - * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 |