diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
commit | 012bb3e61e5eced6c610f9e036372bf0c8def2d1 (patch) | |
tree | 87efc733f9b164e8c85c0336f92c8fb7eff6d183 /drivers/net/8139too.c | |
parent | 625a1589d3d6464b5d90b8a0918789e3afffd220 (diff) |
Merge with Linux 2.4.0-test9. Please check DECstation, I had a number
of rejects to fixup while integrating Linus patches. I also found
that this kernel will only boot SMP on Origin; the UP kernel freeze
soon after bootup with SCSI timeout messages. I commit this anyway
since I found that the last CVS versions had the same problem.
Diffstat (limited to 'drivers/net/8139too.c')
-rw-r--r-- | drivers/net/8139too.c | 556 |
1 files changed, 312 insertions, 244 deletions
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 6d06b0a5f..bbd2f0fac 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -2,8 +2,37 @@ 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. - Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com> - Originally: Written 1997-1999 by Donald Becker. + Maintained by Jeff Garzik <jgarzik@mandrakesoft.com> + + Much code comes from Donald Becker's rtl8139.c driver, + versions 1.11 and older. This driver was originally based + on rtl8139.c version 1.07. Header of rtl8139.c version 1.11: + + -----<snip>----- + + Written 1997-2000 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@scyld.com, or C/O Scyld + Computing Corporation 410 Severn Ave., Suite 210 Annapolis + MD 21403 + + Support and updates available at + http://www.scyld.com/network/rtl8139.html + + Twister-tuning table provided by Kinston + <shangh@realtek.com.tw>. + + -----<snip>----- This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -16,7 +45,7 @@ Tigran Aivazian - bug fixes, skbuff free cleanup Martin Mares - suggestions for PCI cleanup - + David S. Miller - PCI DMA and softnet updates Ernst Gill - fixes ported from BSD driver @@ -24,7 +53,22 @@ Daniel Kobras - identified specific locations of posted MMIO write bugginess - Gerard Sharp - bug fix + Gerard Sharp - bug fix, testing and feedback + + David Ford - Rx ring wrap fix + + Dan DeMaggio - swapped RTL8139 cards with me, and allowed me + to find and fix a crucial bug on older chipsets. + + Donald Becker/Chris Butterworth/Marcus Westergren - + Noticed various Rx packet size-related buglets. + + Santiago Garcia Mantinan - testing and feedback + + Jens David - 2.2.x kernel backports + + Martin Dennett - incredibly helpful insight on undocumented + features of the 8139 chips Submitting bug reports: @@ -73,7 +117,7 @@ that almost all frames will need to be copied to an alignment buffer. IVb. References http://www.realtek.com.tw/cn/cn.html -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.scyld.com/expert/NWay.html IVc. Errata @@ -97,25 +141,33 @@ an MMIO register read. #include <asm/io.h> -#define RTL8139_VERSION "0.9.7" +#define RTL8139_VERSION "0.9.10" #define RTL8139_MODULE_NAME "8139too" #define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION #define PFX RTL8139_MODULE_NAME ": " -#undef RTL8139_DEBUG /* define to 1 to enable copious debugging info */ + +/* define to 1 to enable PIO instead of MMIO */ +#undef USE_IO_OPS + +/* define to 1 to enable copious debugging info */ +#undef RTL8139_DEBUG + +/* define to 1 to disable lightweight runtime debugging checks */ +#undef RTL8139_NDEBUG + #ifdef RTL8139_DEBUG /* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) #else -#define DPRINTK(fmt, args...) +# define DPRINTK(fmt, args...) #endif -#undef RTL8139_NDEBUG /* define to 1 to disable lightweight runtime checks */ #ifdef RTL8139_NDEBUG -#define assert(expr) +# define assert(expr) do {} while (0) #else -#define assert(expr) \ +# define assert(expr) \ if(!(expr)) { \ printk( "Assertion failed! %s,%s,%s,line=%d\n", \ #expr,__FILE__,__FUNCTION__,__LINE__); \ @@ -125,12 +177,6 @@ an MMIO register read. #define arraysize(x) (sizeof(x)/sizeof(*(x))) -#ifndef PCI_GET_DRIVER_DATA - #define PCI_GET_DRIVER_DATA(pdev) ((pdev)->driver_data) - #define PCI_SET_DRIVER_DATA(pdev,data) (((pdev)->driver_data) = (data)) -#endif /* PCI_GET_DRIVER_DATA */ - - /* A few user-configurable values. */ /* media options */ static int media[] = {-1, -1, -1, -1, -1, -1, -1, -1}; @@ -146,7 +192,8 @@ static int multicast_filter_limit = 32; #define RX_BUF_LEN_IDX 2 /* 0==8K, 1==16K, 2==32K, 3==64K */ #define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) #define RX_BUF_PAD 16 -#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD) +#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ +#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) /* Number of Tx descriptor registers. */ #define NUM_TX_DESC 4 @@ -160,9 +207,9 @@ static int multicast_filter_limit = 32; #define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ /* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ -#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 4 /* Maximum PCI burst, '7' is unlimited */ -#define TX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 */ +#define RX_FIFO_THRESH 6 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ /* Operational parameters that usually are not changed. */ @@ -321,6 +368,7 @@ enum tx_config_bits { TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ }; @@ -341,7 +389,7 @@ enum RxConfigBits { /* Early Rx threshold, none or X/16 */ RxCfgEarlyRxNone = 0, RxCfgEarlyRxShift = 24, - + /* rx fifo threshold */ RxCfgFIFOShift = 13, RxCfgFIFONone = (7 << RxCfgFIFOShift), @@ -355,6 +403,9 @@ enum RxConfigBits { RxCfgRcv16K = (1 << 11), RxCfgRcv32K = (1 << 12), RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer */ + RxNoWrap = (1 << 7), }; @@ -411,17 +462,17 @@ const static struct { 0x40, 0xf0fe0040, /* XXX copied from RTL8139A, verify */ }, - + { "RTL-8139 rev K", 0x60, - 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + 0xf0fe0040, }, - + { "RTL-8139A", 0x70, 0xf0fe0040, }, - + { "RTL-8139B", 0x78, 0xf0fc0040 @@ -497,6 +548,32 @@ static void rtl8139_set_rx_mode (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev); +#ifdef USE_IO_OPS + +#define RTL_R8(reg) inb (((unsigned long)ioaddr) + (reg)) +#define RTL_R16(reg) inw (((unsigned long)ioaddr) + (reg)) +#define RTL_R32(reg) inl (((unsigned long)ioaddr) + (reg)) +#define RTL_W8(reg, val8) outb ((val8), ((unsigned long)ioaddr) + (reg)) +#define RTL_W16(reg, val16) outw ((val16), ((unsigned long)ioaddr) + (reg)) +#define RTL_W32(reg, val32) outl ((val32), ((unsigned long)ioaddr) + (reg)) +#define RTL_W8_F RTL_W8 +#define RTL_W16_F RTL_W16 +#define RTL_W32_F RTL_W32 +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb(addr) inb((unsigned long)(addr)) +#define readw(addr) inw((unsigned long)(addr)) +#define readl(addr) inl((unsigned long)(addr)) +#define writeb(val,addr) outb((val),(unsigned long)(addr)) +#define writew(val,addr) outw((val),(unsigned long)(addr)) +#define writel(val,addr) outl((val),(unsigned long)(addr)) + +#else + /* write MMIO register, with flush */ /* Flush avoids rtl8139 bug w/ posted MMIO writes */ #define RTL_W8_F(reg, val8) do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0) @@ -525,13 +602,17 @@ static void rtl8139_hw_start (struct net_device *dev); #define RTL_R16(reg) readw (ioaddr + (reg)) #define RTL_R32(reg) readl (ioaddr + (reg)) +#endif /* USE_IO_OPS */ + -static const u16 rtl8139_intr_mask = +static const u16 rtl8139_intr_mask = PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; static const unsigned int rtl8139_rx_config = - RxCfgEarlyRxNone | RxCfgFIFONone | RxCfgRcv32K | RxCfgDMAUnlimited; + RxCfgEarlyRxNone | RxCfgRcv32K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); static int __devinit rtl8139_init_board (struct pci_dev *pdev, @@ -577,7 +658,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* set this immediately, we need to know before * we talk to the chip directly */ DPRINTK("PIO region size == 0x%02X\n", pio_len); - DPRINTK("MMIO region size == 0x%02X\n", mmio_len); + DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); if (pio_len == RTL8139B_IO_SIZE) tp->chipset = CH_8139B; @@ -587,14 +668,14 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, rc = -ENODEV; goto err_out; } - + /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } - + /* check for weird/broken PCI region reporting */ if ((pio_len != mmio_len) || (pio_len < RTL_MIN_IO_SIZE) || @@ -610,14 +691,14 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, rc = -EBUSY; goto err_out; } - + /* make sure our MMIO region in PCI space is available */ if (!request_mem_region (mmio_start, mmio_len, dev->name)) { printk (KERN_ERR PFX "no mem resource available, aborting\n"); rc = -EBUSY; goto err_out_free_pio; } - + /* enable device (incl. PCI PM wakeup), and bus-mastering */ rc = pci_enable_device (pdev); if (rc) @@ -625,6 +706,9 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, pci_set_master (pdev); +#ifdef USE_IO_OPS + ioaddr = (void *) pio_start; +#else /* ioremap MMIO region */ ioaddr = ioremap (mmio_start, mmio_len); if (ioaddr == NULL) { @@ -632,6 +716,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, rc = -EIO; goto err_out_free_mmio; } +#endif /* USE_IO_OPS */ /* Soft reset the chip. */ RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset); @@ -653,12 +738,14 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, RTL_W8 (Config1, 0); } +#ifndef USE_IO_OPS /* sanity checks -- ensure PIO and MMIO registers agree */ assert (inb (pio_start+Config0) == readb (ioaddr+Config0)); assert (inb (pio_start+Config1) == readb (ioaddr+Config1)); assert (inb (pio_start+TxConfig) == readb (ioaddr+TxConfig)); assert (inb (pio_start+RxConfig) == readb (ioaddr+RxConfig)); - +#endif /* !USE_IO_OPS */ + /* make sure chip thinks PIO and MMIO are enabled */ tmp8 = RTL_R8 (Config1); if ((tmp8 & Cfg1_PIO) == 0) { @@ -671,7 +758,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, rc = -EIO; goto err_out_iounmap; } - + /* identify chip attached to board */ tmp = RTL_R8 (ChipVersion); for (i = arraysize (rtl_chip_info) - 1; i >= 0; i--) @@ -691,15 +778,17 @@ match: tmp, tp->chipset, rtl_chip_info[tp->chipset].name); - + DPRINTK ("EXIT, returning 0\n"); *ioaddr_out = ioaddr; *dev_out = dev; - return 0; + return 0; err_out_iounmap: assert (ioaddr > 0); +#ifndef USE_IO_OPS iounmap (ioaddr); +#endif /* !USE_IO_OPS */ err_out_free_mmio: release_mem_region (mmio_start, mmio_len); err_out_free_pio: @@ -720,14 +809,11 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, int i, addr_len, option; void *ioaddr = NULL; static int board_idx = -1; - u8 tmp; - -#ifndef RTL8139_NDEBUG static int printed_version = 0; -#endif /* RTL8139_NDEBUG */ + u8 tmp; DPRINTK ("ENTER\n"); - + assert (pdev != NULL); assert (ent != NULL); @@ -743,9 +829,9 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, DPRINTK ("EXIT, returning %d\n", i); return i; } - + tp = dev->priv; - + assert (ioaddr != NULL); assert (dev != NULL); assert (tp != NULL); @@ -766,7 +852,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, dev->watchdog_timeo = TX_TIMEOUT; dev->irq = pdev->irq; - dev->base_addr = pci_resource_start (pdev, 1); + dev->base_addr = (unsigned long) ioaddr; /* dev->priv/tp zeroed and aligned in init_etherdev */ tp = dev->priv; @@ -779,24 +865,23 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, tp->mmio_addr = ioaddr; tp->lock = SPIN_LOCK_UNLOCKED; - PCI_SET_DRIVER_DATA (pdev, dev); + pdev->driver_data = dev; tp->phys[0] = 32; - printk (KERN_INFO "%s: %s board found at 0x%lx, IRQ %d\n", - dev->name, board_info[ent->driver_data].name, - dev->base_addr, dev->irq); - - printk (KERN_INFO "%s: Chip is '%s'\n", - dev->name, - rtl_chip_info[tp->chipset].name); - - printk (KERN_INFO "%s: MAC address " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", + printk (KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", dev->name, + board_info[ent->driver_data].name, + dev->base_addr, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5]); + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", + dev->name, rtl_chip_info[tp->chipset].name); /* Put the chip into low-power mode. */ RTL_W8_F (Cfg9346, Cfg9346_Unlock); @@ -831,7 +916,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, static void __devexit rtl8139_remove_one (struct pci_dev *pdev) { - struct net_device *dev = PCI_GET_DRIVER_DATA (pdev); + struct net_device *dev = pdev->driver_data; struct rtl8139_private *np; DPRINTK ("ENTER\n"); @@ -843,7 +928,10 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev) unregister_netdev (dev); +#ifndef USE_IO_OPS iounmap (np->mmio_addr); +#endif /* !USE_IO_OPS */ + release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0)); release_mem_region (pci_resource_start (pdev, 1), @@ -857,7 +945,9 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev) #endif /* RTL8139_NDEBUG */ kfree (dev); - + + pdev->driver_data = NULL; + DPRINTK ("EXIT\n"); } @@ -1061,6 +1151,7 @@ static void mdio_write (struct net_device *dev, int phy_id, int location, static int rtl8139_open (struct net_device *dev) { struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + int retval; #ifdef RTL8139_DEBUG void *ioaddr = tp->mmio_addr; #endif @@ -1069,10 +1160,11 @@ static int rtl8139_open (struct net_device *dev) MOD_INC_USE_COUNT; - if (request_irq (dev->irq, &rtl8139_interrupt, SA_SHIRQ, dev->name, dev)) { - DPRINTK ("EXIT, returning -EBUSY\n"); + retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) { + DPRINTK ("EXIT, returning %d\n", retval); MOD_DEC_USE_COUNT; - return -EBUSY; + return retval; } tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, @@ -1092,9 +1184,9 @@ static int rtl8139_open (struct net_device *dev) DPRINTK ("EXIT, returning -ENOMEM\n"); MOD_DEC_USE_COUNT; return -ENOMEM; - + } - + tp->full_duplex = tp->duplex_lock; tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; @@ -1129,7 +1221,7 @@ static void rtl8139_hw_start (struct net_device *dev) u8 tmp; DPRINTK ("ENTER\n"); - + /* Soft reset the chip. */ RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset); udelay (100); @@ -1152,7 +1244,7 @@ static void rtl8139_hw_start (struct net_device *dev) RTL_W32_F (RxConfig, i); /* Check this value: the documentation for IFG contradicts ifself. */ - RTL_W32 (TxConfig, (TX_DMA_BURST << 8)); + RTL_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift)); /* unlock Config[01234] and BMCR register writes */ RTL_W8_F (Cfg9346, Cfg9346_Unlock); @@ -1173,9 +1265,9 @@ static void rtl8139_hw_start (struct net_device *dev) if (tp->chipset >= CH_8139B) { tmp = RTL_R8 (Config4) & ~(1<<2); /* chip will clear Rx FIFO overflow automatically */ - tmp |= (1<<7); + tmp |= (1<<7); RTL_W8 (Config4, tmp); - + /* disable magic packet scanning, which is enabled * when PM is enabled above (Config1) */ RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5)); @@ -1332,16 +1424,13 @@ static void rtl8139_timer (unsigned long data) { struct net_device *dev = (struct net_device *) data; struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; int next_tick = 60 * HZ; int mii_reg5; - spin_lock_irq (&tp->lock); - mii_reg5 = mdio_read (dev, tp->phys[0], 5); -#if 0 if (!tp->duplex_lock && mii_reg5 != 0xffff) { - void *ioaddr = tp->mmio_addr; int duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040; if (tp->full_duplex != duplex) { @@ -1356,7 +1445,6 @@ static void rtl8139_timer (unsigned long data) RTL_W8 (Cfg9346, Cfg9346_Lock); } } -#endif rtl8139_tune_twister (dev, tp); @@ -1371,8 +1459,6 @@ static void rtl8139_timer (unsigned long data) dev->name, RTL_R8 (Config0), RTL_R8 (Config1)); - spin_unlock_irq (&tp->lock); - tp->timer.expires = jiffies + next_tick; add_timer (&tp->timer); } @@ -1383,6 +1469,7 @@ static void rtl8139_tx_timeout (struct net_device *dev) struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; void *ioaddr = tp->mmio_addr; int i; + unsigned long flags; DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " "media %2.2x.\n", dev->name, @@ -1390,25 +1477,20 @@ static void rtl8139_tx_timeout (struct net_device *dev) RTL_R16 (IntrStatus), RTL_R8 (MediaStatus)); - spin_lock_irq (&tp->lock); - /* Disable interrupts by clearing the interrupt mask. */ RTL_W16 (IntrMask, 0x0000); - spin_unlock_irq (&tp->lock); - /* Emit info to figure out what went wrong. */ - printk (KERN_DEBUG - "%s: Tx queue start entry %d dirty entry %d.\n", + printk (KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d.\n", dev->name, atomic_read (&tp->cur_tx), atomic_read (&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, RTL_R32 (TxStatus0 + (i * 4)), - i == - atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? " (queue head)" : ""); + i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? + " (queue head)" : ""); - spin_lock_irq (&tp->lock); + spin_lock_irqsave (&tp->lock, flags); /* Stop a shared interrupt from scavenging while we are. */ atomic_set (&tp->cur_tx, 0); @@ -1417,19 +1499,21 @@ static void rtl8139_tx_timeout (struct net_device *dev) /* Dump the unsent Tx packets. */ for (i = 0; i < NUM_TX_DESC; i++) { struct ring_info *rp = &tp->tx_info[i]; + if (rp->mapping != 0) { + pci_unmap_single (tp->pci_dev, rp->mapping, + rp->skb->len, PCI_DMA_TODEVICE); + rp->mapping = 0; + } if (rp->skb) { dev_kfree_skb (rp->skb); rp->skb = NULL; tp->stats.tx_dropped++; } - if (rp->mapping != 0) { - pci_unmap_single (tp->pci_dev, rp->mapping, rp->skb->len, PCI_DMA_TODEVICE); - rp->mapping = 0; - } } - - spin_unlock_irq (&tp->lock); + spin_unlock_irqrestore (&tp->lock, flags); + + /* ...and finally, reset everything */ rtl8139_hw_start (dev); } @@ -1444,18 +1528,17 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) /* Calculate the next Tx descriptor entry. */ entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC; + assert (tp->tx_info[entry].skb == NULL); + assert (tp->tx_info[entry].mapping == 0); + tp->tx_info[entry].skb = skb; - tp->tx_info[entry].mapping = 0; + /* tp->tx_info[entry].mapping = 0; */ memcpy (tp->tx_buf[entry], skb->data, skb->len); - spin_lock_irq (&tp->lock); - /* Note: the chip doesn't have auto-pad! */ RTL_W32 (TxStatus0 + (entry * sizeof(u32)), tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); - spin_unlock_irq (&tp->lock); - dev->trans_start = jiffies; atomic_inc (&tp->cur_tx); if ((atomic_read (&tp->cur_tx) - atomic_read (&tp->dirty_tx)) >= NUM_TX_DESC) @@ -1477,20 +1560,15 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev, assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); - - /* drop lock held in rtl8139_interrupt */ - spin_unlock (&tp->lock); - + dirty_tx = atomic_read (&tp->dirty_tx); while ((atomic_read (&tp->cur_tx) - dirty_tx) > 0) { int entry = dirty_tx % NUM_TX_DESC; int txstatus; - spin_lock (&tp->lock); - txstatus = RTL_R32 (TxStatus0 + (entry * 4)); - spin_unlock (&tp->lock); - + txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); + if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* It still hasn't been Txed */ @@ -1502,9 +1580,7 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev, tp->stats.tx_errors++; if (txstatus & TxAborted) { tp->stats.tx_aborted_errors++; - spin_lock (&tp->lock); - RTL_W32 (TxConfig, (TX_DMA_BURST << 8)); - spin_unlock (&tp->lock); + RTL_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift)); } if (txstatus & TxCarrierLost) tp->stats.tx_carrier_errors++; @@ -1527,7 +1603,13 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev, } /* Free the original skb. */ - dev_kfree_skb_irq (tp->tx_info[entry].skb); + if (tp->tx_info[entry].mapping != 0) { + pci_unmap_single(tp->pci_dev, + tp->tx_info[entry].mapping, + tp->tx_info[entry].skb->len, + PCI_DMA_TODEVICE); + tp->tx_info[entry].mapping = 0; + } dev_kfree_skb_irq (tp->tx_info[entry].skb); tp->tx_info[entry].skb = NULL; dirty_tx++; if (netif_queue_stopped (dev) && @@ -1545,17 +1627,63 @@ static inline void rtl8139_tx_interrupt (struct net_device *dev, #endif /* RTL8139_NDEBUG */ atomic_set (&tp->dirty_tx, dirty_tx); - - /* obtain lock need for rtl8139_interrupt */ - spin_lock (&tp->lock); +} + + +/* TODO: clean this up! Rx reset need not be this intensive */ +static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, + struct rtl8139_private *tp, void *ioaddr) +{ + u8 tmp8; + int tmp_work = 1000; + + DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n", + dev->name, rx_status); + if (rx_status & RxTooLong) { + DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", + dev->name, rx_status); + /* A.C.: The chip hangs here. */ + } + tp->stats.rx_errors++; + if (rx_status & (RxBadSymbol | RxBadAlign)) + 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; + + /* disable receive */ + tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear; + RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb); + + /* A.C.: Reset the multicast list. */ + rtl8139_set_rx_mode (dev); + + /* XXX potentially temporary hack to + * restart hung receiver */ + while (--tmp_work > 0) { + tmp8 = RTL_R8 (ChipCmd); + if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) + break; + RTL_W8_F (ChipCmd, + (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb); + } + + /* G.S.: Re-enable receiver */ + /* XXX temporary hack to work around receiver hang */ + rtl8139_set_rx_mode (dev); + + if (tmp_work <= 0) + printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); } /* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the field alignments and semantics. */ static void rtl8139_rx_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void *ioaddr) + struct rtl8139_private *tp, void *ioaddr) { unsigned char *rx_ring; u16 cur_rx; @@ -1563,32 +1691,33 @@ static void rtl8139_rx_interrupt (struct net_device *dev, assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); - + rx_ring = tp->rx_ring; cur_rx = tp->cur_rx; DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), - RTL_R8 (ChipCmd)); + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { int ring_offset = cur_rx % RX_BUF_LEN; - u32 rx_status = - le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); + u32 rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); int rx_size = rx_status >> 16; + struct sk_buff *skb; + int pkt_size = rx_size - 4; DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x," - " cur %4.4x.\n", dev->name, rx_status, - rx_size, cur_rx); + " cur %4.4x.\n", dev->name, rx_status, + rx_size, cur_rx); #if RTL8139_DEBUG > 2 { - int i; - DPRINTK ("%s: Frame contents ", dev->name); - for (i = 0; i < 70; i++) - printk (" %2.2x", rx_ring[ring_offset + i]); - printk (".\n"); + int i; + DPRINTK ("%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + printk (" %2.2x", + rx_ring[ring_offset + i]); + printk (".\n"); } #endif @@ -1604,127 +1733,68 @@ static void rtl8139_rx_interrupt (struct net_device *dev, if (rx_size == 0xfff0) break; + /* if Rx err received, Rx process gets reset, so + * we abort any further Rx processing + */ if (rx_status & - (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr | - RxBadAlign)) { - u8 tmp8; - int tmp_work = 1000; - - DPRINTK ("%s: Ethernet frame had errors," - " status %8.8x.\n", dev->name, - rx_status); - if (rx_status & RxTooLong) { - DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", - dev->name, rx_status); - /* A.C.: The chip hangs here. */ - } - tp->stats.rx_errors++; - if (rx_status & (RxBadSymbol | RxBadAlign)) - 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; - - /* disable receive */ - tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear; - RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb); - - /* A.C.: Reset the multicast list. */ - rtl8139_set_rx_mode (dev); - - /* XXX potentially temporary hack to - * restart hung receiver */ - while (--tmp_work > 0) { - tmp8 = RTL_R8 (ChipCmd); - if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) - break; - RTL_W8_F (ChipCmd, (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb); - } + (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr | RxBadAlign)) { + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + return; + } - /* G.S.: Re-enable receiver */ - /* XXX temporary hack to work around receiver hang */ - rtl8139_set_rx_mode (dev); + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ - if (tmp_work <= 0) - printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); - } 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); - if (skb == NULL) { - printk (KERN_WARNING - "%s: Memory squeeze, deferring packet.\n", - dev->name); - /* We should check that some rx space is free. - If not, free one and mark stats->rx_dropped++. */ - tp->stats.rx_dropped++; - break; - } - 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; - /* 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); -#ifdef RTL8139_DEBUG - { - int i; - 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); - } -#endif /* RTL8139_DEBUG */ - - } else { - eth_copy_and_sum (skb, - &rx_ring[ring_offset + 4], - rx_size - 4, 0); - skb_put (skb, rx_size - 4); - } - skb->protocol = eth_type_trans (skb, dev); - netif_rx (skb); - tp->stats.rx_bytes += rx_size; - tp->stats.rx_packets++; + /* TODO: consider allocating skb's outside of + * interrupt context, both to speed interrupt processing, + * and also to reduce the chances of having to + * drop packets here under memory pressure. + */ + + skb = dev_alloc_skb (pkt_size + 2); + if (skb == NULL) { + printk (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + tp->stats.rx_dropped++; + break; } + skb->dev = dev; + skb_reserve (skb, 2); /* 16 byte align the IP fields. */ + + 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); + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; RTL_W16_F (RxBufPtr, cur_rx - 16); } + DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), - RTL_R8 (ChipCmd)); + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + tp->cur_rx = cur_rx; } -static int rtl8139_weird_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void *ioaddr, - int status, int link_changed) +static void rtl8139_weird_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void *ioaddr, + int status, int link_changed) { - DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", - dev->name, status); - + printk (KERN_DEBUG "%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); + assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); - + /* Update the error count. */ tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); @@ -1762,8 +1832,6 @@ static int rtl8139_weird_interrupt (struct net_device *dev, printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", dev->name, pci_cmd_status); } - - return 0; } @@ -1779,14 +1847,14 @@ static void rtl8139_interrupt (int irq, void *dev_instance, int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */ spin_lock (&tp->lock); - + do { status = RTL_R16 (IntrStatus); /* h/w no longer present (hotplug?) or major error, bail */ - if (status == 0xFFFFFFFF) + if (status == 0xFFFF) break; - + /* Acknowledge all of the current interrupt sources ASAP, but an first get an additional status bit from CSCR. */ if (status & RxUnderrun) @@ -1848,7 +1916,7 @@ static void rtl8139_interrupt (int irq, void *dev_instance, } spin_unlock (&tp->lock); - + DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, RTL_R16 (IntrStatus)); } @@ -1868,6 +1936,8 @@ static int rtl8139_close (struct net_device *dev) DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, RTL_R16 (IntrStatus)); + del_timer_sync (&tp->timer); + spin_lock_irqsave (&tp->lock, flags); /* Disable interrupts by clearing the interrupt mask. */ @@ -1881,9 +1951,7 @@ static int rtl8139_close (struct net_device *dev) RTL_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); - - del_timer (&tp->timer); - + /* snooze for a small bit */ if (current->need_resched) schedule (); @@ -2047,7 +2115,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev) set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } - + /* if called from irq handler, lock already acquired */ if (!in_irq ()) spin_lock_irq (&tp->lock); @@ -2068,13 +2136,13 @@ static void rtl8139_set_rx_mode (struct net_device *dev) static void rtl8139_suspend (struct pci_dev *pdev) { - struct net_device *dev = PCI_GET_DRIVER_DATA (pdev); + struct net_device *dev = pdev->driver_data; struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; netif_device_detach (dev); - + spin_lock_irqsave (&tp->lock, flags); /* Disable interrupts, stop Tx and Rx. */ @@ -2091,7 +2159,7 @@ static void rtl8139_suspend (struct pci_dev *pdev) static void rtl8139_resume (struct pci_dev *pdev) { - struct net_device *dev = PCI_GET_DRIVER_DATA (pdev); + struct net_device *dev = pdev->driver_data; netif_device_attach (dev); rtl8139_hw_start (dev); |