summaryrefslogtreecommitdiff
path: root/linux/src/drivers/net/rtl8139.c
diff options
context:
space:
mode:
authorThomas Schwinge <tschwinge@gnu.org>2006-01-22 15:54:41 +0000
committerThomas Schwinge <tschwinge@gnu.org>2009-06-18 00:26:29 +0200
commit4ad86505c480b2964548328a959c662e8aef5422 (patch)
tree9cc9b8f80d561ae7d7451d95a26ff8c252e86504 /linux/src/drivers/net/rtl8139.c
parent656d6634de02862e837f3a7d80a49e9ca072398d (diff)
2006-01-22 Thomas Schwinge <tschwinge@gnu.org>
* configure, i386/configure, i386/linux/configure, linux/configure, i386/linux/device-drivers.h.in: Regenerated. * linux/src/drivers/net/ne2k-pci.c: Resolve conflicts. 2006-01-22 Guillem Jover <guillem@hadrons.org> * i386/linux/configure.ac: Renamed winbond-840 driver to winbond_840. Enable the starfire, intel_gige and natsemi network drivers. Remove "CONFIG_" from cb_chim, starfire, sundance, winbond840, hamachi, natsemi, myson803 and ns820 driver declarations. Replace INTER_GIGE with INTEL_GIGE. * linux/dev/drivers/net/Space.c: Add conditional probes for natsemi, ns820, winbond840, hamachi, sundance, starfire, myson803 and intel-gige drivers. * linux/src/include/asm-i386/cache.h: New file from linux 2.2.26. * linux/dev/include/linux/malloc.h: Include <asm/cache.h>. * linux/src/drivers/net/ns820.c (netsami_drv_id): Renamed to ... (ns820_drv_id): ... this. Fix all callers. * linux/src/drivers/net/intel-gige.c (skel_netdev_probe): Renamed to ... (igige_probe): ... this. * linux/dev/drivers/net/eepro100.c: Remove obsoleted file. * linux/src/drivers/net/eepro100.c (pci_id_tbl): Add PCI ID's from linux-2.6.14-rc4. 2006-01-22 Alfred M. Szmidt <ams@gnu.org> * i386/linux/configure.ac: Added `pci-scan.o' to the network driver class. (ns820, myson803, sundance, winbond-840, hamachi): New drivers. * i386/linux/Makefile.in (linux-net-files): Added `cb_shim.c', `hamachi.c', `intel-gige.c', `myson803.c', `natsemi.c', `ns820.c', `starfire.c', `sundance.c', `winbond-840.c' and `pci-scan.c'. * linux/dev/include/linux/modversions.h: New file. * linux/src/drivers/net/cb_shim.c, linux/src/drivers/net/hamachi.c, linux/src/drivers/net/intel-gige.c, linux/src/drivers/net/myson803.c, linux/src/drivers/net/natsemi.c, linux/src/drivers/net/ns820.c, linux/src/drivers/net/starfire.c, linux/src/drivers/net/sundance.c, linux/src/drivers/net/winbond-840.c, linux/src/drivers/net/kern_compat.h, linux/src/drivers/net/pci-scan.c, linux/src/drivers/net/pci-scan.h: New files from netdrivers 3.5 package (http://www.scyld.com/network). * linux/src/drivers/net/3c59x.c, linux/src/drivers/net/eepro100.c, linux/src/drivers/net/epic100.c, linux/src/drivers/net/ne2k-pci.c, linux/src/drivers/net/rtl8139.c, linux/src/drivers/net/tulip.c, linux/src/drivers/net/via-rhine.c, linux/src/drivers/net/yellowfin.c: Updated files from netdrivers 3.5 (http://www.scyld.com/network).
Diffstat (limited to 'linux/src/drivers/net/rtl8139.c')
-rw-r--r--linux/src/drivers/net/rtl8139.c1719
1 files changed, 1077 insertions, 642 deletions
diff --git a/linux/src/drivers/net/rtl8139.c b/linux/src/drivers/net/rtl8139.c
index 0bd11a5..1d00f68 100644
--- a/linux/src/drivers/net/rtl8139.c
+++ b/linux/src/drivers/net/rtl8139.c
@@ -1,34 +1,62 @@
/* rtl8139.c: A RealTek RTL8129/8139 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.
+ Written and Copyright 1997-2003 by Donald Becker.
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
This driver is for boards based on the RTL8129 and RTL8139 PCI ethernet
chips.
- 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
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
Support and updates available at
- http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html
+ http://www.scyld.com/network/rtl8139.html
- Twister-tuning code contributed by Kinston <shangh@realtek.com.tw>.
+ Twister-tuning table provided by Kinston <shangh@realtek.com.tw>.
*/
-static const char *version =
-"rtl8139.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";
+/* These identify the driver base version and may not be removed. */
+static const char versionA[] =
+"rtl8139.c:v1.23a 8/24/2003 Donald Becker, becker@scyld.com.\n";
+static const char versionB[] =
+" http://www.scyld.com/network/rtl8139.html\n";
+
+#ifndef USE_MEM_OPS
+/* Note: Register access width and timing restrictions apply in MMIO mode.
+ This updated driver should nominally work, but I/O mode is better tested. */
+#define USE_IO_OPS
+#endif
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+/* Message enable level: 0..31 = no..all messages. See NETIF_MSG docs. */
+static int debug = 2;
-/* 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;
+
+/* 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. It
+ is efficient to update the hardware filter, but recalculating the table
+ for a long filter list is painful. */
+static int multicast_filter_limit = 32;
-/* Size of the in-memory receive ring. */
+/* 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};
+
+/* Operational parameters that are set at compile time. */
+
+/* Maximum size of the in-memory receive ring (smaller if no memory). */
#define RX_BUF_LEN_IDX 2 /* 0==8K, 1==16K, 2==32K, 3==64K */
-#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
#define TX_BUF_SIZE 1536
@@ -39,68 +67,86 @@ 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 (6*HZ)
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
+/* Allocation size of Rx buffers with full-sized Ethernet frames.
+ This is a cross-driver value that is not a limit,
+ but a way to keep a consistent allocation size among drivers.
+ */
+#define PKT_BUF_SZ 1536
+
+
+#ifndef __KERNEL__
+#define __KERNEL__
#endif
-#include <linux/module.h>
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include <linux/config.h>
+#if defined(CONFIG_SMP) && ! defined(__SMP__)
+#define __SMP__
+#endif
+#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
+#define MODVERSIONS
+#endif
+
#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
#endif
+#include <linux/module.h>
#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>
+#if LINUX_VERSION_CODE >= 0x20400
+#include <linux/slab.h>
+#else
#include <linux/malloc.h>
+#endif
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/bios32.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>
-#define RUN_AT(x) (jiffies + (x))
-
-#include <linux/delay.h>
-
-#if (LINUX_VERSION_CODE < 0x20123)
-#define test_and_set_bit(val, addr) set_bit(val, addr)
+#if LINUX_VERSION_CODE >= 0x20300
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= 0x20200
+#include <asm/spinlock.h>
#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};
+#ifdef INLINE_PCISCAN
+#include "k_compat.h"
+#else
+#include "pci-scan.h"
+#include "kern_compat.h"
#endif
-static int rtl8129_debug = 1;
+#if (LINUX_VERSION_CODE >= 0x20100) && defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
/*
Theory of Operation
I. Board Compatibility
-This device driver is designed for the RealTek RTL8129, the RealTek Fast
-Ethernet controllers for PCI. This chip is used on a few clone boards.
+This device driver is designed for the RealTek RTL8129 series, the RealTek
+Fast Ethernet controllers for PCI and CardBus. This chip is used on many
+low-end boards, sometimes with custom chip labels.
II. Board-specific settings
@@ -121,15 +167,17 @@ are sequentially stored into the Rx region, and the host copies them into
skbuffs.
Comment: While it is theoretically possible to process many frames in place,
-any delay in Rx processing would cause us to drop frames. More importantly,
-the Linux protocol stack is not designed to operate in this manner.
+any delay in Rx processing would block the Rx ring and cause us to drop
+frames. It would be difficult to design a protocol stack where the data
+buffer could be recalled by the device driver.
IIIb. Tx operation
-The RTL8129 uses a fixed set of four Tx descriptors in register space.
-In a stunningly bad design choice, Tx frames must be 32 bit aligned. Linux
-aligns the IP header on word boundaries, and 14 byte ethernet header means
-that almost all frames will need to be copied to an alignment buffer.
+The RTL8129 uses a fixed set of four Tx descriptors in register space. Tx
+frames must be 32 bit aligned. Linux aligns the IP header on word
+boundaries, and 14 byte ethernet header means that almost all frames will
+need to be copied to an alignment buffer. The driver statically allocates
+alignment the four alignment buffers at open() time.
IVb. References
@@ -139,15 +187,74 @@ http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
IVc. Errata
*/
+
-#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
+static void *rtl8139_probe1(struct pci_dev *pdev, void *init_dev,
+ long ioaddr, int irq, int chip_idx, int find_cnt);
+static int rtl_pwr_event(void *dev_instance, int event);
+
+enum chip_capability_flags {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02,
+ HAS_LNK_CHNG=0x04, HAS_DESC=0x08};
+#ifdef USE_IO_OPS
+#define RTL8139_IOTYPE PCI_USES_MASTER|PCI_USES_IO |PCI_ADDR0
+#else
+#define RTL8139_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR1
#endif
-#ifndef PCI_DEVICE_ID_REALTEK_8139
-#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+#define RTL8129_CAPS HAS_MII_XCVR
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+#define RTL8139D_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG|HAS_DESC
+
+/* Note: Update the marked constant in _attach() if the RTL8139B entry moves.*/
+static struct pci_id_info pci_tbl[] = {
+ {"RealTek RTL8139C+, 64 bit high performance",
+ { 0x813910ec, 0xffffffff, 0,0, 0x20, 0xff},
+ RTL8139_IOTYPE, 0x80, RTL8139D_CAPS, },
+ {"RealTek RTL8139C Fast Ethernet",
+ { 0x813910ec, 0xffffffff, 0,0, 0x10, 0xff},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"RealTek RTL8129 Fast Ethernet", { 0x812910ec, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8129_CAPS, },
+ {"RealTek RTL8139 Fast Ethernet", { 0x813910ec, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"RealTek RTL8139B PCI/CardBus", { 0x813810ec, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"SMC1211TX EZCard 10/100 (RealTek RTL8139)", { 0x12111113, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"Accton MPX5030 (RealTek RTL8139)", { 0x12111113, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"D-Link DFE-530TX+ (RealTek RTL8139C)",
+ { 0x13001186, 0xffffffff, 0x13011186, 0xffffffff,},
+ RTL8139_IOTYPE, 0x100, RTL8139_CAPS, },
+ {"D-Link DFE-538TX (RealTek RTL8139)", { 0x13001186, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"LevelOne FPC-0106Tx (RealTek RTL8139)", { 0x0106018a, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"Compaq HNE-300 (RealTek RTL8139c)", { 0x8139021b, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"Edimax EP-4103DL CardBus (RealTek RTL8139c)", { 0xab0613d1, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {"Siemens 1012v2 CardBus (RealTek RTL8139c)", { 0x101202ac, 0xffffffff,},
+ RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+ {0,}, /* 0 terminated list. */
+};
+
+struct drv_id_info rtl8139_drv_id = {
+ "realtek", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_tbl,
+ rtl8139_probe1, rtl_pwr_event };
+
+#ifndef USE_IO_OPS
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb readb
+#define inw readw
+#define inl readl
+#define outb writeb
+#define outw writew
+#define outl writel
#endif
/* The rest of these values should never change. */
@@ -157,7 +264,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,
@@ -168,9 +275,10 @@ 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 */
+ FIFOTMS=0x70, /* FIFO Control and test. */
CSCR=0x74, /* Chip Status and Configuration Register. */
PARA78=0x78, PARA7c=0x7c, /* Magic transceiver parameter register. */
};
@@ -194,281 +302,240 @@ enum RxStatusBits {
RxBadAlign=0x0002, RxStatusOK=0x0001,
};
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links. */
enum CSCRBits {
CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
CSCR_LinkDownCmd=0x0f3c0,
-};
-
-/* Twister tuning parameters from RealTek. Completely undocumented. */
+};
+#define PARA78_default 0x78fa8388
+#define PARA7c_default 0xcb38de43 /* param[0][3] */
+#define PARA7c_xxx 0xcb38de43
unsigned long param[4][4]={
- {0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43},
- {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
- {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
- {0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83}
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
};
+#define PRIV_ALIGN 15 /* Desired alignment mask */
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;
-#if LINUX_VERSION_CODE > 0x20139
+ struct net_device *next_module;
+ void *priv_addr; /* Unaligned address for kfree */
+
+ int chip_id, drv_flags;
+ struct pci_dev *pci_dev;
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;
+ int msg_level;
+ int max_interrupt_work;
+
+ /* Receive state. */
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
+ unsigned int rx_buf_len; /* Size (8K 16K 32K or 64KB) of the Rx ring */
+
+ /* Transmit state. */
+ unsigned int cur_tx, dirty_tx, tx_flag;
+ unsigned long tx_full; /* The Tx queue is full. */
/* 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. */
+
+ /* Receive filter state. */
+ unsigned int rx_config;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int cur_rx_mode;
+ int multicast_filter_limit;
+
+ /* Transceiver state. */
char phys[4]; /* MII device addresses. */
- int in_interrupt; /* Alpha needs word-wide lock. */
- unsigned int tx_full:1; /* The Tx queue is full. */
+ u16 advertising; /* NWay media advertisement */
+ char twistie, twist_row, twist_col; /* Twister tune state. */
+ u8 config1;
unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int default_port:4; /* Last dev->if_port value. */
+ unsigned int duplex_lock:1;
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. */
+ unsigned int default_port; /* Last dev->if_port value. */
};
-#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
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
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_LICENSE("GPL");
+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");
-#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);
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Driver message level (0-31)");
+MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex");
+MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex.");
+MODULE_PARM_DESC(multicast_filter_limit,
+ "Multicast addresses before switching to Rx-all-multicast");
+MODULE_PARM_DESC(max_interrupt_work,
+ "Driver maximum events handled per interrupt");
+
+static int rtl8129_open(struct net_device *dev);
+static void rtl_hw_start(struct net_device *dev);
+static int read_eeprom(long ioaddr, int location, int addr_len);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_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);
-static int rtl8129_start_xmit(struct sk_buff *skb, struct device *dev);
-static int rtl8129_rx(struct device *dev);
+static void rtl8129_tx_timeout(struct net_device *dev);
+static void rtl8129_init_ring(struct net_device *dev);
+static int rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int rtl8129_rx(struct net_device *dev);
static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int rtl8129_close(struct device *dev);
-static struct enet_statistics *rtl8129_get_stats(struct device *dev);
-static void set_rx_mode(struct device *dev);
+static void rtl_error(struct net_device *dev, int status, int link_status);
+static int rtl8129_close(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8129_get_stats(struct net_device *dev);
+static inline u32 ether_crc(int length, unsigned char *data);
+static void set_rx_mode(struct net_device *dev);
-#ifdef MODULE
/* A list of all installed RTL8129 devices, for removing the driver module. */
-static struct device *root_rtl8129_dev = NULL;
-#endif
+static struct net_device *root_rtl8129_dev = NULL;
-int rtl8139_probe(struct device *dev)
+#ifndef MODULE
+int rtl8139_probe(struct net_device *dev)
{
- int cards_found = 0;
- static int pci_index = 0; /* Static, for multiple probe calls. */
-
- /* 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()) {
- unsigned char pci_bus, pci_device_fn;
-
- for (;pci_index < 0xff; pci_index++) {
- u8 pci_irq_line, pci_latency;
- u16 pci_command, new_command, vendor, device;
- u32 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)
- 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);
- 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);
- /* 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;
-
- /* 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 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 (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++;
- }
- }
- }
+ static int did_version = 0; /* Already printed version info. */
-#if defined (MODULE)
- return cards_found;
-#else
- return cards_found ? 0 : -ENODEV;
-#endif
+ if (debug >= NETIF_MSG_DRV /* Emit version even if no cards detected. */
+ && did_version++ == 0)
+ printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+ return pci_drv_register(&rtl8139_drv_id, dev);
}
+#endif
-static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
- int chip_id, int options, int card_idx)
+static void *rtl8139_probe1(struct pci_dev *pdev, void *init_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;
+ struct net_device *dev;
+ struct rtl8129_private *np;
+ void *priv_mem;
+ int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0;
+ int config1;
- if (rtl8129_debug > 0 && did_version++ == 0)
- printk(KERN_INFO "%s", version);
+ dev = init_etherdev(init_dev, 0);
+ if (!dev)
+ return NULL;
- 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);
+ config1 = inb(ioaddr + Config1);
+ if (pci_tbl[chip_idx].drv_flags & HAS_MII_XCVR) /* rtl8129 chip */
+ outb(config1 & ~0x03, ioaddr + Config1);
+
+ {
+ int addr_len = read_eeprom(ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ ((u16 *)(dev->dev_addr))[i] =
+ le16_to_cpu(read_eeprom(ioaddr, i+7, addr_len));
+ }
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 : "");
- }
+ /* Make certain elements e.g. descriptor lists are aligned. */
+ priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL);
+ /* Check for the very unlikely case of no memory. */
+ if (priv_mem == NULL)
+ return NULL;
/* 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;
- /* Some data structures must be quadword aligned. */
- tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
- memset(tp, 0, sizeof(*tp));
- dev->priv = tp;
+ dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN);
+ memset(np, 0, sizeof(*np));
+ np->priv_addr = priv_mem;
-#ifdef MODULE
- tp->next_module = root_rtl8129_dev;
+ np->next_module = root_rtl8129_dev;
root_rtl8129_dev = dev;
-#endif
- tp->chip_id = chip_id;
+ np->pci_dev = pdev;
+ np->chip_id = chip_idx;
+ np->drv_flags = pci_tbl[chip_idx].drv_flags;
+ np->msg_level = (1 << debug) - 1;
+ np->max_interrupt_work = max_interrupt_work;
+ np->multicast_filter_limit = multicast_filter_limit;
+
+ np->config1 = config1;
/* Find the connected MII xcvrs.
Doing this in open() would allow detecting external xcvrs later, but
takes too much time. */
- if (chip_id == 0x8129) {
- 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);
-
- if (mii_status != 0xffff && mii_status != 0x0000) {
- tp->phys[phy_idx++] = phy;
- printk(KERN_INFO "%s: MII transceiver found at address %d.\n",
- dev->name, phy);
+ if (np->drv_flags & HAS_MII_XCVR) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < sizeof(np->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ np->phys[phy_idx++] = phy;
+ np->advertising = mdio_read(dev, phy, 4);
+ printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
+ "advertising %4.4x.\n",
+ dev->name, phy, mii_status, np->advertising);
}
}
if (phy_idx == 0) {
printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
"transceiver.\n",
dev->name);
- tp->phys[0] = -1;
+ np->phys[0] = 32;
}
- } else {
- tp->phys[0] = -1;
- }
+ } else
+ np->phys[0] = 32;
/* Put the chip into low-power mode. */
outb(0xC0, ioaddr + Cfg9346);
- outb(0x03, ioaddr + Config1);
+ if (np->drv_flags & HAS_MII_XCVR) /* rtl8129 chip */
+ outb(0x03, ioaddr + Config1);
+
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 (tp->default_port)
- tp->medialock = 1;
+ if (option > 0) {
+ np->full_duplex = (option & 0x220) ? 1 : 0;
+ np->default_port = option & 0x330;
+ if (np->default_port)
+ np->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)
+ np->full_duplex = full_duplex[found_cnt];
+
+ if (np->full_duplex) {
+ printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+ /* Changing the MII-advertised media might prevent re-connection. */
+ np->duplex_lock = 1;
+ }
+ if (np->default_port) {
+ printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n",
+ (option & 0x300 ? 100 : 10),
+ (option & 0x220 ? "full" : "half"));
+ mdio_write(dev, np->phys[0], 0,
+ ((option & 0x300) ? 0x2000 : 0) | /* 100mbps? */
+ ((option & 0x220) ? 0x0100 : 0)); /* Full duplex? */
}
-#endif
- /* The Rtl8129-specific entries in the device structure. */
+ /* The rtl81x9-specific entries in the device structure. */
dev->open = &rtl8129_open;
dev->hard_start_xmit = &rtl8129_start_xmit;
dev->stop = &rtl8129_close;
dev->get_stats = &rtl8129_get_stats;
dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &mii_ioctl;
return dev;
}
@@ -485,48 +552,43 @@ static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
#define EE_ENB (0x80 | EE_CS)
/* Delay between EEPROM clock transitions.
- No extra delay is needed with 33Mhz PCI, but 66Mhz is untested.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
*/
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec) udelay(1)
-#else
-#define eeprom_delay(nanosec) do { ; } while (0)
-#endif
+#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)
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location, int addr_len)
{
int i;
unsigned retval = 0;
- int ee_addr = ioaddr + Cfg9346;
- int read_cmd = location | EE_READ_CMD;
+ long ee_addr = ioaddr + Cfg9346;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
outb(EE_ENB & ~EE_CS, ee_addr);
outb(EE_ENB, ee_addr);
/* Shift the read command bits out. */
- for (i = 10; i >= 0; i--) {
+ for (i = 4 + addr_len; i >= 0; i--) {
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. */
@@ -544,288 +606,402 @@ static int read_eeprom(int ioaddr, int location)
#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() do { ; } while (0)
-#endif
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay(mdio_addr) 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);
- mdio_delay();
- outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr);
- mdio_delay();
+ outb(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr);
+ mdio_delay(mdio_addr);
}
return;
}
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(struct net_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 > 31) { /* 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();
+ mdio_delay(mdio_addr);
outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr);
- mdio_delay();
+ mdio_delay(mdio_addr);
}
/* Read the two transition, 16 data, and wire-idle bits. */
for (i = 19; i > 0; i--) {
outb(0, mdio_addr);
- mdio_delay();
+ mdio_delay(mdio_addr);
retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 0);
outb(MDIO_CLK, mdio_addr);
- mdio_delay();
+ mdio_delay(mdio_addr);
}
return (retval>>1) & 0xffff;
}
-
-static int
-rtl8129_open(struct device *dev)
+
+static void mdio_write(struct net_device *dev, int phy_id, int location,
+ int value)
{
- struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ long mdio_addr = dev->base_addr + MII_SMI;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
int i;
- int full_duplex = 0;
- /* Soft reset the chip. */
- outb(CmdReset, ioaddr + ChipCmd);
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ long ioaddr = dev->base_addr;
+ if (location == 0) {
+ outb(0xC0, ioaddr + Cfg9346);
+ outw(value, ioaddr + MII_BMCR);
+ outb(0x00, ioaddr + Cfg9346);
+ } else if (location < 8 && mii_2_8139_map[location])
+ outw(value, ioaddr + 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(mdio_addr);
+ outb(dataval | MDIO_CLK, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outb(0, mdio_addr);
+ mdio_delay(mdio_addr);
+ outb(MDIO_CLK, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+
+
+static int rtl8129_open(struct net_device *dev)
+{
+ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int rx_buf_len_idx;
+ MOD_INC_USE_COUNT;
if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
return -EAGAIN;
}
- MOD_INC_USE_COUNT;
+ /* The Rx ring allocation size is 2^N + delta, which is worst-case for
+ the kernel binary-buddy allocation. We allocate the Tx bounce buffers
+ at the same time to use some of the otherwise wasted space.
+ The delta of +16 is required for dribble-over because the receiver does
+ not wrap when the packet terminates just beyond the end of the ring. */
+ rx_buf_len_idx = RX_BUF_LEN_IDX;
+ do {
+ tp->rx_buf_len = 8192 << rx_buf_len_idx;
+ tp->rx_ring = kmalloc(tp->rx_buf_len + 16 +
+ (TX_BUF_SIZE * NUM_TX_DESC), GFP_KERNEL);
+ } while (tp->rx_ring == NULL && --rx_buf_len_idx >= 0);
- tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL);
- tp->rx_ring = kmalloc(RX_BUF_LEN + 16, GFP_KERNEL);
- if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
- free_irq (dev->irq, dev);
- if (tp->tx_bufs)
- kfree(tp->tx_bufs);
- if (rtl8129_debug > 0)
+ if (tp->rx_ring == NULL) {
+ if (debug > 0)
printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n",
- dev->name, RX_BUF_LEN);
+ dev->name, tp->rx_buf_len);
+ MOD_DEC_USE_COUNT;
return -ENOMEM;
}
+ tp->tx_bufs = tp->rx_ring + tp->rx_buf_len + 16;
+
rtl8129_init_ring(dev);
+ tp->full_duplex = tp->duplex_lock;
+ tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000;
+ tp->rx_config =
+ (RX_FIFO_THRESH << 13) | (rx_buf_len_idx << 11) | (RX_DMA_BURST<<8);
+
+ rtl_hw_start(dev);
+ netif_start_tx_queue(dev);
+
+ if (tp->msg_level & NETIF_MSG_IFUP)
+ 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),
+ tp->full_duplex ? "full" : "half");
+
+ /* Set the timer to switch to check for link beat and perhaps switch
+ to an alternate media type. */
+ init_timer(&tp->timer);
+ tp->timer.expires = jiffies + 3*HZ;
+ tp->timer.data = (unsigned long)dev;
+ tp->timer.function = &rtl8129_timer;
+ add_timer(&tp->timer);
+
+ return 0;
+}
+/* Start the hardware at open or resume. */
+static void rtl_hw_start(struct net_device *dev)
+{
+ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int i;
+
+ /* 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;
+ /* Restore our idea of the MAC address. */
+ outb(0xC0, ioaddr + Cfg9346);
+ outl(cpu_to_le32(*(u32*)(dev->dev_addr + 0)), ioaddr + MAC0 + 0);
+ outl(cpu_to_le32(*(u32*)(dev->dev_addr + 4)), ioaddr + MAC0 + 4);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + MAC0 + i);
+ /* Hmmm, do these belong here? */
+ tp->cur_rx = 0;
/* Must enable Tx/Rx before setting transfer thresholds! */
outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
- outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
- ioaddr + RxConfig);
- outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig);
-
- 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);
+ outl(tp->rx_config, ioaddr + RxConfig);
+ /* Check this value: the documentation contradicts ifself. Is the
+ IFG correct with bit 28:27 zero, or with |0x03000000 ? */
+ outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
+
+ /* This is check_duplex() */
+ if (tp->phys[0] >= 0 || (tp->drv_flags & 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;
- if (rtl8129_debug > 1)
+ tp->full_duplex = 1;
+ if (tp->msg_level & NETIF_MSG_LINK)
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);
+ if (tp->drv_flags & HAS_MII_XCVR) /* rtl8129 chip */
+ outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
outb(0x00, ioaddr + Cfg9346);
outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf);
-
/* Start the chip's Tx and Rx process. */
outl(0, ioaddr + RxMissed);
set_rx_mode(dev);
-
outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
-
/* Enable all known interrupts by setting the interrupt mask. */
outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
- | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
-
- if (rtl8129_debug > 1)
- printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d"
- " GP Pins %2.2x %s-duplex.\n",
- dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData),
- full_duplex ? "full" : "half");
-
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
- tp->timer.data = (unsigned long)dev;
- tp->timer.function = &rtl8129_timer; /* timer handler */
- add_timer(&tp->timer);
+ | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
- return 0;
}
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;
- 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);
+ struct net_device *dev = (struct net_device *)data;
+ struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int next_tick = 60*HZ;
+ int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+
+ if (! np->duplex_lock && mii_reg5 != 0xffff) {
+ int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+ if (np->full_duplex != duplex) {
+ np->full_duplex = duplex;
+ printk(KERN_INFO "%s: Using %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n", dev->name,
+ np->full_duplex ? "full" : "half", np->phys[0], mii_reg5);
+ if (np->drv_flags & HAS_MII_XCVR) {
outb(0xC0, ioaddr + Cfg9346);
- outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
+ outb(np->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
outb(0x00, ioaddr + Cfg9346);
}
+ }
+ }
+#if LINUX_VERSION_CODE < 0x20300
+ /* Check for bogusness. */
+ if (inw(ioaddr + IntrStatus) & (TxOK | RxOK)) {
+ int status = inw(ioaddr + IntrStatus); /* Double check */
+ if (status & (TxOK | RxOK) && ! dev->interrupt) {
+ printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, status %x.\n",
+ dev->name, status);
+ rtl8129_interrupt(dev->irq, dev, 0);
+ }
+ }
+ if (dev->tbusy && jiffies - dev->trans_start >= 2*TX_TIMEOUT)
+ rtl8129_tx_timeout(dev);
+#else
+ if (netif_queue_paused(dev) &&
+ np->cur_tx - np->dirty_tx > 1 &&
+ (jiffies - dev->trans_start) > TX_TIMEOUT) {
+ rtl8129_tx_timeout(dev);
}
- if (rtl8129_debug > 2) {
- if (tp->chip_id == 0x8129)
+#endif
+
+#if defined(RTL_TUNE_TWISTER)
+ /* This is a complicated state machine to configure the "twister" for
+ impedance/echos based on the cable length.
+ All of this is magic and undocumented.
+ */
+ if (np->twistie) switch(np->twistie) {
+ case 1: {
+ if (inw(ioaddr + CSCR) & CSCR_LinkOKBit) {
+ /* We have link beat, let us tune the twister. */
+ outw(CSCR_LinkDownOffCmd, ioaddr + CSCR);
+ np->twistie = 2; /* Change to state 2. */
+ next_tick = HZ/10;
+ } else {
+ /* Just put in some reasonable defaults for when beat returns. */
+ outw(CSCR_LinkDownCmd, ioaddr + CSCR);
+ outl(0x20,ioaddr + FIFOTMS); /* Turn on cable test mode. */
+ outl(PARA78_default ,ioaddr + PARA78);
+ outl(PARA7c_default ,ioaddr + PARA7c);
+ np->twistie = 0; /* Bail from future actions. */
+ }
+ } break;
+ case 2: {
+ /* Read how long it took to hear the echo. */
+ int linkcase = inw(ioaddr + CSCR) & CSCR_LinkStatusBits;
+ if (linkcase == 0x7000) np->twist_row = 3;
+ else if (linkcase == 0x3000) np->twist_row = 2;
+ else if (linkcase == 0x1000) np->twist_row = 1;
+ else np->twist_row = 0;
+ np->twist_col = 0;
+ np->twistie = 3; /* Change to state 2. */
+ next_tick = HZ/10;
+ } break;
+ case 3: {
+ /* Put out four tuning parameters, one per 100msec. */
+ if (np->twist_col == 0) outw(0, ioaddr + FIFOTMS);
+ outl(param[(int)np->twist_row][(int)np->twist_col], ioaddr + PARA7c);
+ next_tick = HZ/10;
+ if (++np->twist_col >= 4) {
+ /* For short cables we are done.
+ For long cables (row == 3) check for mistune. */
+ np->twistie = (np->twist_row == 3) ? 4 : 0;
+ }
+ } break;
+ case 4: {
+ /* Special case for long cables: check for mistune. */
+ if ((inw(ioaddr + CSCR) & CSCR_LinkStatusBits) == 0x7000) {
+ np->twistie = 0;
+ break;
+ } else {
+ outl(0xfb38de03, ioaddr + PARA7c);
+ np->twistie = 5;
+ next_tick = HZ/10;
+ }
+ } break;
+ case 5: {
+ /* Retune for shorter cable (column 2). */
+ outl(0x20,ioaddr + FIFOTMS);
+ outl(PARA78_default, ioaddr + PARA78);
+ outl(PARA7c_default, ioaddr + PARA7c);
+ outl(0x00,ioaddr + FIFOTMS);
+ np->twist_row = 2;
+ np->twist_col = 0;
+ np->twistie = 3;
+ next_tick = HZ/10;
+ } break;
+ }
+#endif
+
+ if (np->msg_level & NETIF_MSG_TIMER) {
+ if (np->drv_flags & HAS_MII_XCVR)
printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n",
dev->name, inb(ioaddr + GPPinData));
else
printk(KERN_DEBUG"%s: Media selection tick, Link partner %4.4x.\n",
dev->name, inw(ioaddr + NWayLPAR));
- printk(KERN_DEBUG"%s: Other registers are IntMask %4.4x IntStatus %4.4x"
- " RxStatus %4.4x.\n",
+ printk(KERN_DEBUG"%s: Other registers are IntMask %4.4x "
+ "IntStatus %4.4x RxStatus %4.4x.\n",
dev->name, inw(ioaddr + IntrMask), inw(ioaddr + IntrStatus),
- inl(ioaddr + RxEarlyStatus));
+ (int)inl(ioaddr + RxEarlyStatus));
printk(KERN_DEBUG"%s: Chip config %2.2x %2.2x.\n",
dev->name, inb(ioaddr + Config0), inb(ioaddr + Config1));
}
- if (next_tick) {
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
- }
+ np->timer.expires = jiffies + next_tick;
+ add_timer(&np->timer);
}
-static void rtl8129_tx_timeout(struct device *dev)
+static void rtl8129_tx_timeout(struct net_device *dev)
{
struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
- int ioaddr = dev->base_addr;
- int i;
+ long ioaddr = dev->base_addr;
+ int status = inw(ioaddr + IntrStatus);
+ int mii_reg, i;
+
+ /* Could be wrapped with if (tp->msg_level & NETIF_MSG_TX_ERR) */
+ printk(KERN_ERR "%s: Transmit timeout, status %2.2x %4.4x "
+ "media %2.2x.\n",
+ dev->name, inb(ioaddr + ChipCmd), status, inb(ioaddr + GPPinData));
+
+ if (status & (TxOK | RxOK)) {
+ printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, status %x.\n",
+ dev->name, status);
+ }
/* Disable interrupts by clearing the interrupt mask. */
outw(0x0000, ioaddr + IntrMask);
-
- if (rtl8129_debug > 0)
- printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n",
- dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus));
/* Emit info to figure out what went wrong. */
+ printk(KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d%s.\n",
+ dev->name, tp->cur_tx, tp->dirty_tx, tp->tx_full ? ", full" : "");
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),
+ printk(KERN_DEBUG "%s: Tx descriptor %d is %8.8x.%s\n",
+ dev->name, i, (int)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));
- }
-
- /* Soft reset the chip. */
- outb(CmdReset, ioaddr + ChipCmd);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + MAC0 + i);
-
- { /* Save the unsent Tx packets. */
- struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
- int j = 0;
- for (; tp->cur_tx - tp->dirty_tx > 0 ; 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(((TX_FIFO_THRESH<<11) & 0x003f0000) |
- (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
- ioaddr + TxStat0 + i*4);
- }
- tp->cur_tx = i;
- while (i < NUM_TX_DESC)
+ 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");
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ tp->dirty_tx = tp->cur_tx = 0;
+ /* Dump the unsent Tx packets. */
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ if (tp->tx_skbuff[i]) {
+ dev_free_skb(tp->tx_skbuff[i]);
tp->tx_skbuff[i] = 0;
- if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
- dev->tbusy = 0;
- } else {
- tp->tx_full = 1;
+ tp->stats.tx_dropped++;
}
}
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- set_rx_mode(dev);
- outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
- outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
- ioaddr + RxConfig);
- outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
-
- 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);
+ rtl_hw_start(dev);
+ netif_unpause_tx_queue(dev);
+ tp->tx_full = 0;
return;
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void
-rtl8129_init_ring(struct device *dev)
+rtl8129_init_ring(struct net_device *dev)
{
struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
int i;
tp->tx_full = 0;
- tp->cur_rx = tp->cur_tx = 0;
- tp->dirty_rx = tp->dirty_tx = 0;
+ tp->dirty_tx = tp->cur_tx = 0;
for (i = 0; i < NUM_TX_DESC; i++) {
tp->tx_skbuff[i] = 0;
@@ -834,18 +1010,16 @@ rtl8129_init_ring(struct device *dev)
}
static int
-rtl8129_start_xmit(struct sk_buff *skb, struct device *dev)
+rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ long ioaddr = dev->base_addr;
int entry;
- /* 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) {
- if (jiffies - dev->trans_start < TX_TIMEOUT)
- return 1;
- rtl8129_tx_timeout(dev);
+ if (netif_pause_tx_queue(dev) != 0) {
+ /* This watchdog code is redundant with the media monitor timer. */
+ if (jiffies - dev->trans_start > TX_TIMEOUT)
+ rtl8129_tx_timeout(dev);
return 1;
}
@@ -859,20 +1033,26 @@ 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);
-
- if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
- dev->tbusy = 0;
- } else {
- tp->tx_full = 1;
- }
+ outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
+ ioaddr + TxStatus0 + entry*4);
+
+ /* There is a race condition here -- we might read dirty_tx, take an
+ interrupt that clears the Tx queue, and only then set tx_full.
+ So we do this in two phases. */
+ if (++tp->cur_tx - tp->dirty_tx >= NUM_TX_DESC) {
+ set_bit(0, &tp->tx_full);
+ if (tp->cur_tx - (volatile unsigned int)tp->dirty_tx < NUM_TX_DESC) {
+ clear_bit(0, &tp->tx_full);
+ netif_unpause_tx_queue(dev);
+ } else
+ netif_stop_tx_queue(dev);
+ } else
+ netif_unpause_tx_queue(dev);
dev->trans_start = jiffies;
- if (rtl8129_debug > 4)
- printk(KERN_DEBUG"%s: Queued Tx packet at %p size %ld to slot %d.\n",
- dev->name, skb->data, skb->len, entry);
+ if (tp->msg_level & NETIF_MSG_TX_QUEUED)
+ printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n",
+ dev->name, skb->data, (int)skb->len, entry);
return 0;
}
@@ -881,31 +1061,32 @@ rtl8129_start_xmit(struct sk_buff *skb, struct device *dev)
after the Tx thread. */
static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
- struct device *dev = (struct device *)dev_instance;
- struct rtl8129_private *tp;
- int ioaddr, boguscnt = max_interrupt_work;
- int status;
-
- if (dev == NULL) {
- printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n",
- irq);
+ struct net_device *dev = (struct net_device *)dev_instance;
+ struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+ struct rtl8129_private *tp = np;
+ int boguscnt = np->max_interrupt_work;
+ long ioaddr = dev->base_addr;
+ int link_changed = 0; /* Grrr, avoid bogus "uninitialized" warning */
+
+#if defined(__i386__) && LINUX_VERSION_CODE < 0x20123
+ /* 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*)&tp->in_interrupt)) {
- printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
- return;
- }
- dev->interrupt = 1;
+#endif
do {
- status = inw(ioaddr + IntrStatus);
- /* Acknowledge all of the current interrupt sources ASAP. */
+ int status = inw(ioaddr + IntrStatus);
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (status & RxUnderrun)
+ link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit;
outw(status, ioaddr + IntrStatus);
- if (rtl8129_debug > 4)
+ if (tp->msg_level & NETIF_MSG_INTR)
printk(KERN_DEBUG"%s: interrupt status=%#4.4x new intstat=%#4.4x.\n",
dev->name, status, inw(ioaddr + IntrStatus));
@@ -917,27 +1098,25 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
rtl8129_rx(dev);
if (status & (TxOK | TxErr)) {
- unsigned int dirty_tx;
+ unsigned int dirty_tx = tp->dirty_tx;
- for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) {
+ while (tp->cur_tx - dirty_tx > 0) {
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. */
if (txstatus & (TxOutOfWindow | TxAborted)) {
/* There was an major error, log it. */
-#ifndef final_version
- if (rtl8129_debug > 1)
+ if (tp->msg_level & NETIF_MSG_TX_ERR)
printk(KERN_NOTICE"%s: Transmit error, Tx status %8.8x.\n",
dev->name, txstatus);
-#endif
tp->stats.tx_errors++;
if (txstatus&TxAborted) {
tp->stats.tx_aborted_errors++;
- outl((TX_DMA_BURST<<8)|0x03000001, ioaddr + TxConfig);
+ outl(TX_DMA_BURST << 8, ioaddr + TxConfig);
}
if (txstatus&TxCarrierLost) tp->stats.tx_carrier_errors++;
if (txstatus&TxOutOfWindow) tp->stats.tx_window_errors++;
@@ -946,11 +1125,13 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
tp->stats.collisions16++;
#endif
} else {
-#ifdef ETHER_STATS
- /* No count for tp->stats.tx_deferred */
-#endif
+ if (tp->msg_level & NETIF_MSG_TX_DONE)
+ printk(KERN_DEBUG "%s: Transmit done, Tx status"
+ " %8.8x.\n", dev->name, txstatus);
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;
@@ -961,48 +1142,34 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
}
/* Free the original skb. */
- dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
+ dev_free_skb_irq(tp->tx_skbuff[entry]);
tp->tx_skbuff[entry] = 0;
+ if (test_bit(0, &tp->tx_full)) {
+ /* The ring is no longer full, clear tbusy. */
+ clear_bit(0, &tp->tx_full);
+ netif_resume_tx_queue(dev);
+ }
+ dirty_tx++;
}
#ifndef final_version
if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
printk(KERN_ERR"%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
- dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
+ dev->name, dirty_tx, tp->cur_tx, (int)tp->tx_full);
dirty_tx += NUM_TX_DESC;
}
#endif
-
- if (tp->tx_full && dev->tbusy
- && dirty_tx > tp->cur_tx - NUM_TX_DESC) {
- /* The ring is no longer full, clear tbusy. */
- tp->tx_full = 0;
- dev->tbusy = 0;
- mark_bh(NET_BH);
- }
-
tp->dirty_tx = dirty_tx;
}
/* Check uncommon events with one test. */
if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver
|TxErr|RxErr)) {
- /* Update the error count. */
- tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
- outl(0, ioaddr + RxMissed);
-
- if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))
- tp->stats.rx_errors++;
-
- if (status & (PCSTimeout)) tp->stats.rx_length_errors++;
- if (status & (RxUnderrun|RxFIFOOver)) tp->stats.rx_fifo_errors++;
- if (status & RxOverflow) {
- tp->stats.rx_over_errors++;
- tp->cur_rx = inw(ioaddr + RxBufAddr) % RX_BUF_LEN;
- outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
- }
- /* Error sources cleared above. */
+ if (status == 0xffff) /* Missing chip! */
+ break;
+ rtl_error(dev, status, link_changed);
}
+
if (--boguscnt < 0) {
printk(KERN_WARNING"%s: Too much work at interrupt, "
"IntrStatus=0x%4.4x.\n",
@@ -1013,55 +1180,65 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
}
} while (1);
- if (rtl8129_debug > 3)
+ if (tp->msg_level & NETIF_MSG_INTR)
printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n",
- dev->name, inl(ioaddr + IntrStatus));
+ dev->name, inw(ioaddr + IntrStatus));
- dev->interrupt = 0;
- clear_bit(0, (void*)&tp->in_interrupt);
+#if defined(__i386__) && LINUX_VERSION_CODE < 0x20123
+ clear_bit(0, (void*)&dev->interrupt);
+#endif
return;
}
/* 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)
+static int rtl8129_rx(struct net_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;
- if (rtl8129_debug > 4)
+ if (tp->msg_level & NETIF_MSG_RX_STATUS)
printk(KERN_DEBUG"%s: In rtl8129_rx(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n",
dev->name, cur_rx, inw(ioaddr + RxBufAddr),
inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd));
- while ((inb(ioaddr + ChipCmd) & 1) == 0) {
- int ring_offset = cur_rx % RX_BUF_LEN;
- u32 rx_status = *(u32*)(rx_ring + ring_offset);
- int rx_size = rx_status >> 16;
+ while ((inb(ioaddr + ChipCmd) & RxBufEmpty) == 0) {
+ int ring_offset = cur_rx % tp->rx_buf_len;
+ u32 rx_status = le32_to_cpu(*(u32*)(rx_ring + ring_offset));
+ int rx_size = rx_status >> 16; /* Includes the CRC. */
- if (rtl8129_debug > 4) {
+ if (tp->msg_level & NETIF_MSG_RX_STATUS) {
int i;
- printk(KERN_DEBUG"%s: rtl8129_rx() status %4.4x, size %4.4x, cur %4.4x.\n",
+ printk(KERN_DEBUG"%s: rtl8129_rx() status %4.4x, size %4.4x,"
+ " cur %4.4x.\n",
dev->name, rx_status, rx_size, cur_rx);
printk(KERN_DEBUG"%s: Frame contents ", dev->name);
for (i = 0; i < 70; i++)
printk(" %2.2x", rx_ring[ring_offset + i]);
printk(".\n");
}
- if (rx_status & RxTooLong) {
- if (rtl8129_debug > 0)
- printk(KERN_NOTICE"%s: Oversized Ethernet frame, status %4.4x!\n",
- dev->name, rx_status);
- tp->stats.rx_length_errors++;
- } else if (rx_status &
- (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) {
- if (rtl8129_debug > 1)
+ if (rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) {
+ if (tp->msg_level & NETIF_MSG_RX_ERR)
printk(KERN_DEBUG"%s: Ethernet frame had errors,"
- " status %4.4x.\n", dev->name, rx_status);
+ " status %8.8x.\n", dev->name, rx_status);
+ if (rx_status == 0xffffffff) {
+ printk(KERN_NOTICE"%s: Invalid receive status at ring "
+ "offset %4.4x\n", dev->name, ring_offset);
+ rx_status = 0;
+ }
+ if (rx_status & RxTooLong) {
+ if (tp->msg_level & NETIF_MSG_DRV)
+ printk(KERN_NOTICE"%s: Oversized Ethernet frame, status"
+ " %4.4x!\n",
+ dev->name, rx_status);
+ /* A.C.: The chip hangs here.
+ This should never occur, which means that we are screwed
+ when it does.
+ */
+ }
tp->stats.rx_errors++;
if (rx_status & (RxBadSymbol|RxBadAlign))
tp->stats.rx_frame_errors++;
@@ -1070,15 +1247,18 @@ rtl8129_rx(struct device *dev)
/* Reset the receiver, based on RealTek recommendation. (Bug?) */
tp->cur_rx = 0;
outb(CmdTxEnb, ioaddr + ChipCmd);
+ /* A.C.: Reset the multicast list. */
+ set_rx_mode(dev);
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;
+ int pkt_size = rx_size - 4;
- skb = dev_alloc_skb(rx_size + 2);
+ /* Allocate a common-sized skbuff if we are close. */
+ skb = dev_alloc_skb(1400 < pkt_size && pkt_size < PKT_BUF_SZ-2 ?
+ PKT_BUF_SZ : pkt_size + 2);
if (skb == NULL) {
printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n",
dev->name);
@@ -1089,13 +1269,14 @@ rtl8129_rx(struct device *dev)
}
skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP fields. */
- if (ring_offset+rx_size+4 > RX_BUF_LEN) {
- int semi_count = RX_BUF_LEN - ring_offset - 4;
+ if (ring_offset + rx_size > tp->rx_buf_len) {
+ int semi_count = tp->rx_buf_len - ring_offset - 4;
+ /* This could presumably use two calls to copy_and_sum()? */
memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4],
semi_count);
- memcpy(skb_put(skb, rx_size-semi_count), rx_ring,
- rx_size-semi_count);
- if (rtl8129_debug > 4) {
+ memcpy(skb_put(skb, pkt_size-semi_count), rx_ring,
+ pkt_size-semi_count);
+ if (tp->msg_level & NETIF_MSG_PKTDATA) {
int i;
printk(KERN_DEBUG"%s: Frame wrap @%d",
dev->name, semi_count);
@@ -1104,22 +1285,23 @@ rtl8129_rx(struct device *dev)
printk(".\n");
memset(rx_ring, 0xcc, 16);
}
- } else
- memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4],
- rx_size);
+ } else {
+ eth_copy_and_sum(skb, &rx_ring[ring_offset + 4],
+ pkt_size, 0);
+ skb_put(skb, pkt_size);
+ }
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
#if LINUX_VERSION_CODE > 0x20119
- tp->stats.rx_bytes += rx_size;
+ tp->stats.rx_bytes += pkt_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)
+ if (tp->msg_level & NETIF_MSG_RX_STATUS)
printk(KERN_DEBUG"%s: Done rtl8129_rx(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n",
dev->name, cur_rx, inw(ioaddr + RxBufAddr),
@@ -1128,17 +1310,77 @@ rtl8129_rx(struct device *dev)
return 0;
}
+/* Error and abnormal or uncommon events handlers. */
+static void rtl_error(struct net_device *dev, int status, int link_changed)
+{
+ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ if (tp->msg_level & NETIF_MSG_LINK)
+ printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n",
+ dev->name, status);
+
+ /* Update the error count. */
+ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
+ outl(0, ioaddr + RxMissed);
+
+ if (status & RxUnderrun){
+ /* This might actually be a link change event. */
+ if ((tp->drv_flags & HAS_LNK_CHNG) && link_changed) {
+ /* Really link-change on new chips. */
+ int lpar = inw(ioaddr + NWayLPAR);
+ int duplex = (lpar&0x0100) || (lpar & 0x01C0) == 0x0040
+ || tp->duplex_lock;
+ /* Do not use MII_BMSR as that clears sticky bit. */
+ if (inw(ioaddr + GPPinData) & 0x0004) {
+ netif_link_down(dev);
+ } else
+ netif_link_up(dev);
+ if (tp->msg_level & NETIF_MSG_LINK)
+ printk(KERN_DEBUG "%s: Link changed, link partner "
+ "%4.4x new duplex %d.\n",
+ dev->name, lpar, duplex);
+ tp->full_duplex = duplex;
+ /* Only count as errors with no link change. */
+ status &= ~RxUnderrun;
+ } else {
+ /* If this does not work, we will do rtl_hw_start(dev); */
+ outb(CmdTxEnb, ioaddr + ChipCmd);
+ set_rx_mode(dev); /* Reset the multicast list. */
+ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
+
+ tp->stats.rx_errors++;
+ tp->stats.rx_fifo_errors++;
+ }
+ }
+
+ if (status & (RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++;
+ if (status & (PCSTimeout)) tp->stats.rx_length_errors++;
+ if (status & RxFIFOOver) tp->stats.rx_fifo_errors++;
+ if (status & RxOverflow) {
+ tp->stats.rx_over_errors++;
+ tp->cur_rx = inw(ioaddr + RxBufAddr) % tp->rx_buf_len;
+ outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
+ }
+ if (status & PCIErr) {
+ u32 pci_cmd_status;
+ pci_read_config_dword(tp->pci_dev, PCI_COMMAND, &pci_cmd_status);
+
+ printk(KERN_ERR "%s: PCI Bus error %4.4x.\n",
+ dev->name, pci_cmd_status);
+ }
+}
+
static int
-rtl8129_close(struct device *dev)
+rtl8129_close(struct net_device *dev)
{
- int ioaddr = dev->base_addr;
+ long ioaddr = dev->base_addr;
struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
int i;
- dev->start = 0;
- dev->tbusy = 1;
+ netif_stop_tx_queue(dev);
- if (rtl8129_debug > 1)
+ if (tp->msg_level & NETIF_MSG_IFDOWN)
printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n",
dev->name, inw(ioaddr + IntrStatus));
@@ -1158,15 +1400,15 @@ rtl8129_close(struct device *dev)
for (i = 0; i < NUM_TX_DESC; i++) {
if (tp->tx_skbuff[i])
- dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+ dev_free_skb(tp->tx_skbuff[i]);
tp->tx_skbuff[i] = 0;
}
kfree(tp->rx_ring);
- kfree(tp->tx_bufs);
+ tp->rx_ring = 0;
/* Green! Put the chip in low-power mode. */
outb(0xC0, ioaddr + Cfg9346);
- outb(0x03, ioaddr + Config1);
+ outb(tp->config1 | 0x03, ioaddr + Config1);
outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */
MOD_DEC_USE_COUNT;
@@ -1174,13 +1416,69 @@ rtl8129_close(struct device *dev)
return 0;
}
-static struct enet_statistics *
-rtl8129_get_stats(struct device *dev)
+/*
+ Handle user-level ioctl() calls.
+ We must use two numeric constants as the key because some clueless person
+ changed value for the symbolic name.
+*/
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+ u32 *data32 = (void *)&rq->ifr_data;
+
+ switch(cmd) {
+ case 0x8947: case 0x89F0:
+ /* SIOCGMIIPHY: Get the address of the PHY in use. */
+ data[0] = np->phys[0] & 0x3f;
+ /* Fall Through */
+ case 0x8948: case 0x89F1:
+ /* SIOCGMIIREG: Read the specified MII register. */
+ data[3] = mdio_read(dev, data[0], data[1] & 0x1f);
+ return 0;
+ case 0x8949: case 0x89F2:
+ /* SIOCSMIIREG: Write the specified MII register */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (data[0] == np->phys[0]) {
+ u16 value = data[2];
+ switch (data[1]) {
+ case 0:
+ /* Check for autonegotiation on or reset. */
+ np->medialock = (value & 0x9000) ? 0 : 1;
+ if (np->medialock)
+ np->full_duplex = (value & 0x0100) ? 1 : 0;
+ break;
+ case 4: np->advertising = value; break;
+ }
+ }
+ mdio_write(dev, data[0], data[1] & 0x1f, data[2]);
+ return 0;
+ case SIOCGPARAMS:
+ data32[0] = np->msg_level;
+ data32[1] = np->multicast_filter_limit;
+ data32[2] = np->max_interrupt_work;
+ data32[3] = 0; /* No rx_copybreak, always copy. */
+ return 0;
+ case SIOCSPARAMS:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ np->msg_level = data32[0];
+ np->multicast_filter_limit = data32[1];
+ np->max_interrupt_work = data32[2];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct net_device_stats *
+rtl8129_get_stats(struct net_device *dev)
{
struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ long ioaddr = dev->base_addr;
- if (dev->start) {
+ if (netif_running(dev)) {
tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
outl(0, ioaddr + RxMissed);
}
@@ -1189,100 +1487,236 @@ 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;
- }
+ for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+ crc = (crc << 1) ^
+ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
}
return crc;
}
-static void set_rx_mode(struct device *dev)
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
+ AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
+};
+
+static void set_rx_mode(struct net_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 (tp->msg_level & NETIF_MSG_RXFILTER)
+ printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
+ dev->name, dev->flags, (int)inl(ioaddr + RxConfig));
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ /* 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 > tp->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. */
+ outl(tp->rx_config | rx_mode, ioaddr + RxConfig);
+ tp->mc_filter[0] = mc_filter[0];
+ tp->mc_filter[1] = mc_filter[1];
+ 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;
+static int rtl_pwr_event(void *dev_instance, int event)
+{
+ struct net_device *dev = dev_instance;
+ struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ if (np->msg_level & NETIF_MSG_LINK)
+ printk("%s: Handling power event %d.\n", dev->name, event);
+ switch(event) {
+ case DRV_ATTACH:
+ MOD_INC_USE_COUNT;
+ break;
+ case DRV_SUSPEND:
+ netif_device_detach(dev);
+ /* Disable interrupts, stop Tx and Rx. */
+ outw(0x0000, ioaddr + IntrMask);
+ outb(0x00, ioaddr + ChipCmd);
+ /* Update the error counts. */
+ np->stats.rx_missed_errors += inl(ioaddr + RxMissed);
+ outl(0, ioaddr + RxMissed);
+ break;
+ case DRV_RESUME:
+ netif_device_attach(dev);
+ rtl_hw_start(dev);
+ break;
+ case DRV_DETACH: {
+ struct net_device **devp, **next;
+ if (dev->flags & IFF_UP) {
+ dev_close(dev);
+ dev->flags &= ~(IFF_UP|IFF_RUNNING);
+ }
+ unregister_netdev(dev);
+ release_region(dev->base_addr, pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+ iounmap((char *)dev->base_addr);
+#endif
+ for (devp = &root_rtl8129_dev; *devp; devp = next) {
+ next = &((struct rtl8129_private *)(*devp)->priv)->next_module;
+ if (*devp == dev) {
+ *devp = *next;
+ break;
+ }
+ }
+ if (np->priv_addr)
+ kfree(np->priv_addr);
+ kfree(dev);
+ MOD_DEC_USE_COUNT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CARDBUS
-int
-init_module(void)
+#include <pcmcia/driver_ops.h>
+
+static dev_node_t *rtl8139_attach(dev_locator_t *loc)
{
- int cards_found;
+ struct net_device *dev;
+ u16 dev_id;
+ u32 pciaddr;
+ u8 bus, devfn, irq;
+ long hostaddr;
+ /* Note: the chip index should match the 8139B pci_tbl[] entry. */
+ int chip_idx = 2;
+
+ if (loc->bus != LOC_PCI) return NULL;
+ bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
+ printk(KERN_DEBUG "rtl8139_attach(bus %d, function %d)\n", bus, devfn);
+#ifdef USE_IO_OPS
+ pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &pciaddr);
+ hostaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK;
+#else
+ pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_1, &pciaddr);
+ hostaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK,
+ pci_tbl[chip_idx].io_size);
+#endif
+ pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+ pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
+ if (hostaddr == 0 || irq == 0) {
+ printk(KERN_ERR "The %s interface at %d/%d was not assigned an %s.\n"
+ KERN_ERR " It will not be activated.\n",
+ pci_tbl[chip_idx].name, bus, devfn,
+ hostaddr == 0 ? "address" : "IRQ");
+ return NULL;
+ }
+ dev = rtl8139_probe1(pci_find_slot(bus, devfn), NULL,
+ hostaddr, irq, chip_idx, 0);
+ if (dev) {
+ dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+ strcpy(node->dev_name, dev->name);
+ node->major = node->minor = 0;
+ node->next = NULL;
+ MOD_INC_USE_COUNT;
+ return node;
+ }
+ return NULL;
+}
- if (debug >= 0)
- rtl8129_debug = debug;
+static void rtl8139_detach(dev_node_t *node)
+{
+ struct net_device **devp, **next;
+ printk(KERN_INFO "rtl8139_detach(%s)\n", node->dev_name);
+ for (devp = &root_rtl8129_dev; *devp; devp = next) {
+ next = &((struct rtl8129_private *)(*devp)->priv)->next_module;
+ if (strcmp((*devp)->name, node->dev_name) == 0) break;
+ }
+ if (*devp) {
+ struct rtl8129_private *np =
+ (struct rtl8129_private *)(*devp)->priv;
+ unregister_netdev(*devp);
+ release_region((*devp)->base_addr, pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+ iounmap((char *)(*devp)->base_addr);
+#endif
+ kfree(*devp);
+ if (np->priv_addr)
+ kfree(np->priv_addr);
+ *devp = *next;
+ kfree(node);
+ MOD_DEC_USE_COUNT;
+ }
+}
- root_rtl8129_dev = NULL;
- cards_found = rtl8139_probe(0);
+struct driver_operations realtek_ops = {
+ "realtek_cb",
+ rtl8139_attach, /*rtl8139_suspend*/0, /*rtl8139_resume*/0, rtl8139_detach
+};
- return cards_found ? 0 : -ENODEV;
+#endif /* Cardbus support */
+
+#ifdef MODULE
+int init_module(void)
+{
+ if (debug >= NETIF_MSG_DRV) /* Emit version even if no cards detected. */
+ printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#ifdef CARDBUS
+ register_driver(&realtek_ops);
+ return 0;
+#else
+ return pci_drv_register(&rtl8139_drv_id, NULL);
+#endif
}
-void
-cleanup_module(void)
+void cleanup_module(void)
{
- struct device *next_dev;
+ struct net_device *next_dev;
+
+#ifdef CARDBUS
+ unregister_driver(&realtek_ops);
+#else
+ pci_drv_unregister(&rtl8139_drv_id);
+#endif
- /* 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 *np = (void *)(root_rtl8129_dev->priv);
unregister_netdev(root_rtl8129_dev);
- release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE);
+ release_region(root_rtl8129_dev->base_addr,
+ pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+ iounmap((char *)(root_rtl8129_dev->base_addr));
+#endif
+ next_dev = np->next_module;
+ if (np->priv_addr)
+ kfree(np->priv_addr);
kfree(root_rtl8129_dev);
root_rtl8129_dev = next_dev;
}
@@ -1292,8 +1726,9 @@ cleanup_module(void)
/*
* Local variables:
- * 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`"
+ * compile-command: "make KERNVER=`uname -r` rtl8139.o"
+ * compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c rtl8139.c"
+ * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes -O6 -c rtl8139.c -o realtek_cb.o -I/usr/src/pcmcia/include/"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4