patch-2.3.99-pre3 linux/drivers/net/via-rhine.c
Next file: linux/drivers/net/wan/Config.in
Previous file: linux/drivers/net/tulip/tulip_core.c
Back to the patch index
Back to the overall index
- Lines: 871
- Date:
Thu Mar 23 23:35:35 2000
- Orig file:
v2.3.99-pre2/linux/drivers/net/via-rhine.c
- Orig date:
Sun Mar 19 18:35:30 2000
diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
@@ -27,16 +27,19 @@
LK1.1.1:
- Justin Guyett: softnet and locking fixes
- Jeff Garzik: use PCI interface
-
-*/
-static const char *versionA =
-"via-rhine.c:v1.01-LK1.1.1 3/2/2000 Written by Donald Becker\n";
-static const char *versionB =
-" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+ LK1.1.2:
+ - Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions
+
+ LK1.1.3:
+ - Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c code)
+ update "Theory of Operation" with softnet/locking changes
+ - Dave Miller: PCI DMA and endian fixups
+ - Jeff Garzik: MOD_xxx race fixes, updated PCI resource allocation
+*/
/* A few user-configurable values. These may be modified when a driver
- module is loaded.*/
+ module is loaded. */
static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
static int max_interrupt_work = 20;
@@ -58,6 +61,7 @@
The Rhine has a 64 element 8390-like hash table. */
static const int multicast_filter_limit = 32;
+
/* Operational parameters that are set at compile time. */
/* Keep the ring sizes a power of two for compile efficiency.
@@ -68,12 +72,21 @@
#define TX_RING_SIZE 8
#define RX_RING_SIZE 16
+
/* Operational parameters that usually are not changed. */
+
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (2*HZ)
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error See the last lines of the source file for the proper compile-command.
+#endif
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -91,15 +104,20 @@
#include <asm/bitops.h>
#include <asm/io.h>
-/* This driver was written to use PCI memory space, however some x86
- motherboards only configure I/O space accesses correctly. */
-#if defined(__i386__) && !defined(VIA_USE_MEMORY)
-#define VIA_USE_IO
-#endif
-#if defined(__alpha__)
-#define VIA_USE_IO
-#endif
-#ifdef VIA_USE_IO
+static const char *versionA __devinitdata =
+"via-rhine.c:v1.03a-LK1.1.3 3/23/2000 Written by Donald Becker\n";
+static const char *versionB __devinitdata =
+" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+
+
+
+/* This driver was written to use PCI memory space, however most versions
+ of the Rhine only work correctly with I/O space accesses. */
+#if defined(VIA_USE_MEMORY)
+#warning Many adapters using the VIA Rhine chip are not configured to work
+#warning with PCI memory space accesses.
+#else
+#define USE_IO
#undef readb
#undef readw
#undef readl
@@ -114,9 +132,6 @@
#define writel outl
#endif
-/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
- This is only in the support-all-kernels source code. */
-
#define RUN_AT(x) (jiffies + (x))
MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
@@ -186,18 +201,17 @@
The driver runs as two independent, single-threaded flows of control. One
is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and interrupt handling software.
+dev->priv->lock spinlock. The other thread is the interrupt handler, which
+is single threaded by the hardware and interrupt handling software.
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
+The send packet thread has partial control over the Tx ring. It locks the
+dev->priv->lock whenever it's queuing a Tx packet. If the next slot in the ring
+is not available it stops the transmit queue by calling netif_stop_queue.
The interrupt handler has exclusive control over the Rx ring and records stats
from the Tx ring. After reaping the stats, it marks the Tx queue entry as
-empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
-clears both the tx_full and tbusy flags.
+empty by incrementing the dirty_tx mark. If at least half of the entries in
+the Rx ring are available the transmit queue is woken up if it was stopped.
IV. Notes
@@ -231,57 +245,49 @@
};
enum via_rhine_chips {
- vt86c100a = 0,
- vt3043,
+ VT86C100A = 0,
+ VT3043,
};
struct via_rhine_chip_info {
const char *name;
- u16 flags;
+ u16 pci_flags;
int io_size;
+ int drv_flags;
};
+enum chip_capability_flags {CanHaveMII=1, };
+
+#if defined(VIA_USE_MEMORY)
+#define RHINE_IOTYPE (PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR1)
+#else
+#define RHINE_IOTYPE (PCI_USES_IO | PCI_USES_MASTER | PCI_ADDR0)
+#endif
+
/* directly indexed by enum via_rhine_chips, above */
static struct via_rhine_chip_info via_rhine_chip_info[] __devinitdata =
{
- {"VIA VT86C100A Rhine-II",
- PCI_USES_MEM | PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
- 128,},
- {"VIA VT3043 Rhine",
- PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
- 128,},
+ { "VIA VT86C100A Rhine-II", RHINE_IOTYPE, 128, CanHaveMII },
+ { "VIA VT3043 Rhine", RHINE_IOTYPE, 128, CanHaveMII }
};
static struct pci_device_id via_rhine_pci_tbl[] __devinitdata =
{
- {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt86c100a},
- {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt3043},
+ {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A},
+ {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043},
{0,}, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl);
-
-/* A chip capabilities table, matching the entries in pci_tbl[] above. */
-enum chip_capability_flags {CanHaveMII=1, };
-struct chip_info {
- int io_size;
- int flags;
-} static cap_tbl[] = {
- {128, CanHaveMII, },
- {128, CanHaveMII, },
-};
-
-
-/* Offsets to the device registers.
-*/
+/* Offsets to the device registers. */
enum register_offsets {
StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
IntrStatus=0x0C, IntrEnable=0x0E,
MulticastFilter0=0x10, MulticastFilter1=0x14,
RxRingPtr=0x18, TxRingPtr=0x1C,
- MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E,
+ MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E,
MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72,
Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E,
};
@@ -295,21 +301,19 @@
IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
IntrTxAborted=0x2000, IntrLinkChange=0x4000,
IntrRxWakeUp=0x8000,
- IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260,
+ IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260,
};
/* The Rx and Tx buffer descriptors. */
struct rx_desc {
- u16 rx_status;
- u16 rx_length;
+ s32 rx_status;
u32 desc_length;
u32 addr;
u32 next_desc;
};
struct tx_desc {
- u16 tx_status;
- u16 tx_own;
+ s32 tx_status;
u32 desc_length;
u32 addr;
u32 next_desc;
@@ -317,9 +321,11 @@
/* Bits in *_desc.status */
enum rx_status_bits {
- RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F};
+ RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
+};
+
enum desc_status_bits {
- DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000,
+ DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,
};
/* Bits in ChipCmd. */
@@ -331,32 +337,42 @@
};
struct netdev_private {
- /* Descriptor rings first for alignment. */
- struct rx_desc rx_ring[RX_RING_SIZE];
- struct tx_desc tx_ring[TX_RING_SIZE];
+ /* Descriptor rings */
+ struct rx_desc *rx_ring;
+ struct tx_desc *tx_ring;
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_ring_dma;
+
/* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ dma_addr_t rx_skbuff_dma[RX_RING_SIZE];
+
/* The saved address of a sent-in-place packet/buffer, for later free(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ dma_addr_t tx_skbuff_dma[TX_RING_SIZE];
unsigned char *tx_buf[TX_RING_SIZE]; /* Tx bounce buffers */
- unsigned char *tx_bufs; /* Tx bounce buffer region. */
+
+ struct pci_dev *pdev;
struct net_device_stats stats;
struct timer_list timer; /* Media monitoring timer. */
spinlock_t lock;
+
/* Frequently used values: keep some adjacent for cache effect. */
int chip_id;
struct rx_desc *rx_head_desc;
- unsigned short int cur_rx, dirty_rx; /* Producer/consumer ring indices */
- unsigned short int cur_tx, dirty_tx;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int cur_tx, dirty_tx;
unsigned int rx_buf_sz; /* Based on MTU+slack. */
u16 chip_cmd; /* Current setting for ChipCmd */
unsigned int tx_full:1; /* The Tx queue is full. */
+
/* These values are keep track of the transceiver/media in use. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int duplex_lock:1;
unsigned int medialock:1; /* Do not sense media. */
unsigned int default_port:4; /* Last dev->if_port value. */
u8 tx_thresh, rx_thresh;
+
/* MII transceiver section. */
int mii_cnt; /* MII device addresses. */
u16 advertising; /* NWay media advertisement */
@@ -393,6 +409,9 @@
static int did_version = 0;
long ioaddr;
int io_size;
+ int pci_flags;
+ void *ring;
+ dma_addr_t ring_dma;
/* print version once and once only */
if (! did_version++) {
@@ -403,44 +422,67 @@
card_idx++;
option = card_idx < MAX_UNITS ? options[card_idx] : 0;
io_size = via_rhine_chip_info[chip_id].io_size;
+ pci_flags = via_rhine_chip_info[chip_id].pci_flags;
+ /* this should always be supported */
+ if (!pci_dma_supported(pdev, 0xffffffff)) {
+ printk(KERN_ERR "32-bit PCI DMA addresses not supported by the card!?\n");
+ goto err_out;
+ }
+
+ /* sanity check */
+ if ((pci_resource_len (pdev, 0) < io_size) ||
+ (pci_resource_len (pdev, 1) < io_size)) {
+ printk (KERN_ERR "Insufficient PCI resources, aborting\n");
+ goto err_out;
+ }
-#ifdef VIA_USE_IO
- ioaddr = pci_resource_start (pdev, 0);
-#else
- ioaddr = pci_resource_start (pdev, 1);
-#endif
+ /* allocate pci dma space for rx and tx descriptor rings */
+ ring = pci_alloc_consistent(pdev,
+ RX_RING_SIZE * sizeof(struct rx_desc) +
+ TX_RING_SIZE * sizeof(struct tx_desc),
+ &ring_dma);
+ if (!ring) {
+ printk(KERN_ERR "Could not allocate DMA memory.\n");
+ goto err_out;
+ }
+
+ ioaddr = pci_resource_start (pdev, pci_flags & PCI_ADDR0 ? 0 : 1);
if (pci_enable_device (pdev)) {
printk (KERN_ERR "unable to init PCI device (card #%d)\n",
card_idx);
- goto err_out;
+ goto err_out_free_dma;
}
- if (via_rhine_chip_info[chip_id].flags & PCI_USES_MASTER)
+ if (pci_flags & PCI_USES_MASTER)
pci_set_master (pdev);
dev = init_etherdev(NULL, sizeof(*np));
if (dev == NULL) {
printk (KERN_ERR "init_ethernet failed for card #%d\n",
card_idx);
- goto err_out;
+ goto err_out_free_dma;
}
- if (!request_region(pci_resource_start (pdev, 0), io_size, dev->name)) {
+ /* request all PIO and MMIO regions just to make sure
+ * noone else attempts to use any portion of our I/O space */
+ if (!request_region (pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0), dev->name)) {
printk (KERN_ERR "request_region failed for device %s, region 0x%X @ 0x%lX\n",
dev->name, io_size,
pci_resource_start (pdev, 0));
goto err_out_free_netdev;
}
- if (!request_mem_region(pci_resource_start (pdev, 1), io_size, dev->name)) {
+ if (!request_mem_region (pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1), dev->name)) {
printk (KERN_ERR "request_mem_region failed for device %s, region 0x%X @ 0x%lX\n",
dev->name, io_size,
pci_resource_start (pdev, 1));
goto err_out_free_pio;
}
-#ifndef VIA_USE_IO
+#ifndef USE_IO
ioaddr = (long) ioremap (ioaddr, io_size);
if (!ioaddr) {
printk (KERN_ERR "ioremap failed for device %s, region 0x%X @ 0x%X\n",
@@ -469,6 +511,11 @@
np = dev->priv;
spin_lock_init (&np->lock);
np->chip_id = chip_id;
+ np->pdev = pdev;
+ np->rx_ring = ring;
+ np->tx_ring = ring + RX_RING_SIZE * sizeof(struct rx_desc);
+ np->rx_ring_dma = ring_dma;
+ np->tx_ring_dma = ring_dma + RX_RING_SIZE * sizeof(struct rx_desc);
if (dev->mem_start)
option = dev->mem_start;
@@ -499,7 +546,7 @@
pdev->driver_data = dev;
- if (cap_tbl[np->chip_id].flags & CanHaveMII) {
+ if (via_rhine_chip_info[chip_id].drv_flags & CanHaveMII) {
int phy, phy_idx = 0;
np->phys[0] = 1; /* Standard for this chip. */
for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
@@ -518,18 +565,25 @@
return 0;
-#ifndef VIA_USE_IO
+#ifndef USE_IO
/* note this is ifdef'd because the ioremap is ifdef'd...
* so additional exit conditions above this must move
* release_mem_region outside of the ifdef */
err_out_free_mmio:
- release_mem_region(pci_resource_start (pdev, 1), io_size, dev->name));
+ release_mem_region(pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1));
#endif
err_out_free_pio:
- release_region(pci_resource_start (pdev, 0), io_size);
+ release_region(pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0));
err_out_free_netdev:
unregister_netdev (dev);
kfree (dev);
+err_out_free_dma:
+ pci_free_consistent(pdev,
+ RX_RING_SIZE * sizeof(struct rx_desc) +
+ TX_RING_SIZE * sizeof(struct tx_desc),
+ ring, ring_dma);
err_out:
return -ENODEV;
}
@@ -557,9 +611,12 @@
static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value)
{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
int boguscnt = 1024;
+ if (phy_id == np->phys[0] && regnum == 4)
+ np->advertising = value;
/* Wait for a previous command to complete. */
while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
;
@@ -578,32 +635,34 @@
long ioaddr = dev->base_addr;
int i;
+ MOD_INC_USE_COUNT;
+
/* Reset the chip. */
writew(CmdReset, ioaddr + ChipCmd);
- if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev))
- return -EAGAIN;
+ if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
+ return -EBUSY;
+ }
if (debug > 1)
printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n",
dev->name, dev->irq);
- MOD_INC_USE_COUNT;
-
via_rhine_init_ring(dev);
- writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
- writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+ writel(np->rx_ring_dma, ioaddr + RxRingPtr);
+ writel(np->tx_ring_dma, ioaddr + TxRingPtr);
for (i = 0; i < 6; i++)
writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
/* Initialize other registers. */
- writew(0x0006, ioaddr + PCIConfig); /* Tune configuration??? */
+ writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */
/* Configure the FIFO thresholds. */
writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */
np->tx_thresh = 0x20;
- np->rx_thresh = 0x60; /* Written in via_rhine_set_rx_mode(). */
+ np->rx_thresh = 0x60; /* Written in via_rhine_set_rx_mode(). */
if (dev->if_port == 0)
dev->if_port = np->default_port;
@@ -666,6 +725,7 @@
}
}
+
static void via_rhine_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
@@ -677,12 +737,14 @@
printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
dev->name, readw(ioaddr + IntrStatus));
}
+
via_rhine_check_duplex(dev);
np->timer.expires = RUN_AT(next_tick);
add_timer(&np->timer);
}
+
static void via_rhine_tx_timeout (struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *) dev->priv;
@@ -693,16 +755,17 @@
dev->name, readw (ioaddr + IntrStatus),
mdio_read (dev, np->phys[0], 1));
- /* Perhaps we should reinitialize the hardware here. */
+ /* XXX Perhaps we should reinitialize the hardware here. */
dev->if_port = 0;
+
/* Stop and restart the chip's Tx processes . */
+ /* XXX to do */
/* Trigger an immediate transmit demand. */
+ /* XXX to do */
dev->trans_start = jiffies;
np->stats.tx_errors++;
-
- netif_start_queue (dev);
}
@@ -720,13 +783,13 @@
for (i = 0; i < RX_RING_SIZE; i++) {
np->rx_ring[i].rx_status = 0;
- np->rx_ring[i].rx_length = 0;
- np->rx_ring[i].desc_length = np->rx_buf_sz;
- np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]);
+ np->rx_ring[i].desc_length = cpu_to_le32(np->rx_buf_sz);
+ np->rx_ring[i].next_desc =
+ cpu_to_le32(np->rx_ring_dma + sizeof(struct rx_desc)*(i+1));
np->rx_skbuff[i] = 0;
}
/* Mark the last entry as wrapping the ring. */
- np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]);
+ np->rx_ring[i-1].next_desc = cpu_to_le32(np->rx_ring_dma);
/* Fill in the Rx buffers. */
for (i = 0; i < RX_RING_SIZE; i++) {
@@ -734,20 +797,25 @@
np->rx_skbuff[i] = skb;
if (skb == NULL)
break;
- skb->dev = dev; /* Mark as being used by this device. */
- np->rx_ring[i].addr = virt_to_bus(skb->tail);
- np->rx_ring[i].rx_status = 0;
- np->rx_ring[i].rx_length = DescOwn;
+ skb->dev = dev; /* Mark as being used by this device. */
+
+ np->rx_skbuff_dma[i] =
+ pci_map_single(np->pdev, skb->tail, np->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+
+ np->rx_ring[i].addr = cpu_to_le32(np->rx_skbuff_dma[i]);
+ np->rx_ring[i].rx_status = cpu_to_le32(DescOwn);
}
for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_skbuff[i] = 0;
- np->tx_ring[i].tx_own = 0;
- np->tx_ring[i].desc_length = 0x00e08000;
- np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]);
+ np->tx_ring[i].tx_status = 0;
+ np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+ np->tx_ring[i].next_desc =
+ cpu_to_le32(np->tx_ring_dma + sizeof(struct tx_desc)*(i+1));
np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL);
}
- np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]);
+ np->tx_ring[i-1].next_desc = cpu_to_le32(np->tx_ring_dma);
return;
}
@@ -771,16 +839,24 @@
if ((long)skb->data & 3) { /* Must use alignment buffer. */
if (np->tx_buf[entry] == NULL &&
- (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL)
+ (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) {
+ spin_unlock_irqrestore (&np->lock, flags);
return 1;
+ }
memcpy(np->tx_buf[entry], skb->data, skb->len);
- np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]);
- } else
- np->tx_ring[entry].addr = virt_to_bus(skb->data);
-
- np->tx_ring[entry].desc_length = 0x00E08000 |
- (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
- np->tx_ring[entry].tx_own = DescOwn;
+ np->tx_skbuff_dma[entry] =
+ pci_map_single(np->pdev, np->tx_buf[entry], skb->len,
+ PCI_DMA_TODEVICE);
+ } else {
+ np->tx_skbuff_dma[entry] =
+ pci_map_single(np->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ }
+ np->tx_ring[entry].addr = cpu_to_le32(np->tx_skbuff_dma[entry]);
+
+ np->tx_ring[entry].desc_length =
+ cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+ np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn);
np->cur_tx++;
@@ -848,7 +924,7 @@
}
/* This routine is logically part of the interrupt handler, but isolated
- for clarity and better register allocation. */
+ for clarity. */
static void via_rhine_tx(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
@@ -858,15 +934,15 @@
/* if tx_full is set, they're all dirty, not clean */
while (np->dirty_tx != np->cur_tx) {
- if (np->tx_ring[entry].tx_own) /* transmit request pending */
+ txstatus = le32_to_cpu(np->tx_ring[entry].tx_status);
+ if (txstatus & DescOwn)
break;
- txstatus = np->tx_ring[entry].tx_status;
if (debug > 6)
- printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
+ printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n",
entry, txstatus);
if (txstatus & 0x8000) {
if (debug > 1)
- printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
dev->name, txstatus);
np->stats.tx_errors++;
if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
@@ -877,12 +953,15 @@
/* Transmitter restarted in 'abnormal' handler. */
} else {
np->stats.collisions += (txstatus >> 3) & 15;
- np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+ np->stats.tx_bytes += le32_to_cpu(np->tx_ring[entry].desc_length) & 0x7ff;
np->stats.tx_packets++;
- }
- /* Free the original skb. */
- dev_kfree_skb_irq(np->tx_skbuff[entry]);
- np->tx_skbuff[entry] = NULL;
+ }
+ /* Free the original skb. */
+ pci_unmap_single(np->pdev,
+ le32_to_cpu(np->tx_ring[entry].addr),
+ np->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(np->tx_skbuff[entry]);
+ np->tx_skbuff[entry] = NULL;
entry = (++np->dirty_tx) % TX_RING_SIZE;
}
if ((np->cur_tx - np->dirty_tx) <= TX_RING_SIZE/2)
@@ -896,33 +975,32 @@
static void via_rhine_rx(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
- int entry = (np->dirty_rx = np->cur_rx) % RX_RING_SIZE;
- int boguscnt = RX_RING_SIZE;
+ int entry = np->cur_rx % RX_RING_SIZE;
+ int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
if (debug > 4) {
- printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %4.4x.\n",
- entry, np->rx_head_desc->rx_length);
+ printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %8.8x.\n",
+ entry, le32_to_cpu(np->rx_head_desc->rx_status));
}
/* If EOP is set on the next entry, it's a new packet. Send it up. */
- while ( ! (np->rx_head_desc->rx_length & DescOwn)) {
+ while ( ! (np->rx_head_desc->rx_status & cpu_to_le32(DescOwn))) {
struct rx_desc *desc = np->rx_head_desc;
- int data_size = desc->rx_length;
- u16 desc_status = desc->rx_status;
+ u32 desc_status = le32_to_cpu(desc->rx_status);
+ int data_size = desc_status >> 16;
if (debug > 4)
- printk(KERN_DEBUG " via_rhine_rx() status is %4.4x.\n",
+ printk(KERN_DEBUG " via_rhine_rx() status is %8.8x.\n",
desc_status);
if (--boguscnt < 0)
break;
if ( (desc_status & (RxWholePkt | RxErr)) != RxWholePkt) {
if ((desc_status & RxWholePkt) != RxWholePkt) {
printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
- "multiple buffers, entry %#x length %d status %4.4x!\n",
+ "multiple buffers, entry %#x length %d status %8.8x!\n",
dev->name, entry, data_size, desc_status);
printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
- dev->name, np->rx_head_desc,
- &np->rx_ring[entry]);
+ dev->name, np->rx_head_desc, &np->rx_ring[entry]);
np->stats.rx_length_errors++;
} else if (desc_status & RxErr) {
/* There was a error. */
@@ -942,25 +1020,35 @@
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
+ pci_dma_sync_single(np->pdev, np->rx_skbuff_dma[entry],
+ np->rx_buf_sz, PCI_DMA_FROMDEVICE);
#if ! defined(__alpha__) || USE_IP_COPYSUM /* Avoid misaligned on Alpha */
- eth_copy_and_sum(skb, bus_to_virt(desc->addr),
- pkt_len, 0);
+ eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
skb_put(skb, pkt_len);
#else
- memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len);
+ memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail,
+ pkt_len);
#endif
} else {
- skb_put(skb = np->rx_skbuff[entry], pkt_len);
+ skb = np->rx_skbuff[entry];
+ if (skb == NULL) {
+ printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n",
+ dev->name);
+ break;
+ }
np->rx_skbuff[entry] = NULL;
+ skb_put(skb, pkt_len);
+ pci_unmap_single(np->pdev, np->rx_skbuff_dma[entry],
+ np->rx_buf_sz, PCI_DMA_FROMDEVICE);
}
skb->protocol = eth_type_trans(skb, dev);
- np->stats.rx_bytes+=skb->len;
netif_rx(skb);
dev->last_rx = jiffies;
+ np->stats.rx_bytes += skb->len;
np->stats.rx_packets++;
}
entry = (++np->cur_rx) % RX_RING_SIZE;
@@ -968,19 +1056,21 @@
}
/* Refill the Rx ring buffers. */
- while (np->dirty_rx != np->cur_rx) {
+ for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
struct sk_buff *skb;
- entry = np->dirty_rx++ % RX_RING_SIZE;
+ entry = np->dirty_rx % RX_RING_SIZE;
if (np->rx_skbuff[entry] == NULL) {
skb = dev_alloc_skb(np->rx_buf_sz);
np->rx_skbuff[entry] = skb;
if (skb == NULL)
break; /* Better luck next round. */
skb->dev = dev; /* Mark as being used by this device. */
- np->rx_ring[entry].addr = virt_to_bus(skb->tail);
+ np->rx_skbuff_dma[entry] =
+ pci_map_single(np->pdev, skb->tail, np->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ np->rx_ring[entry].addr = cpu_to_le32(np->rx_skbuff_dma[entry]);
}
- np->rx_ring[entry].rx_status = 0;
- np->rx_ring[entry].rx_length = DescOwn;
+ np->rx_ring[entry].rx_status = cpu_to_le32(DescOwn);
}
/* Pre-emptively restart Rx engine. */
@@ -1020,7 +1110,8 @@
printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
"threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
}
- if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug > 1) {
+ if ((intr_status & ~( IntrLinkChange | IntrStatsMax |
+ IntrTxAbort | IntrTxAborted)) && debug > 1) {
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status);
/* Recovery for other fault sources not known. */
@@ -1076,6 +1167,8 @@
} else if ((dev->mc_count > multicast_filter_limit)
|| (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
+ writel(0xffffffff, ioaddr + MulticastFilter0);
+ writel(0xffffffff, ioaddr + MulticastFilter1);
rx_mode = 0x0C;
} else {
struct dev_mc_list *mclist;
@@ -1083,12 +1176,11 @@
memset(mc_filter, 0, sizeof(mc_filter));
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next) {
- set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26,
- mc_filter);
+ set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter);
}
writel(mc_filter[0], ioaddr + MulticastFilter0);
writel(mc_filter[1], ioaddr + MulticastFilter1);
- rx_mode = 0x08;
+ rx_mode = 0x0C;
}
writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
}
@@ -1138,13 +1230,17 @@
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
- np->rx_ring[i].rx_length = 0;
- np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+ np->rx_ring[i].rx_status = 0;
+ np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
if (np->rx_skbuff[i]) {
+ pci_unmap_single(np->pdev,
+ np->rx_skbuff_dma[i],
+ np->rx_buf_sz, PCI_DMA_FROMDEVICE);
dev_kfree_skb(np->rx_skbuff[i]);
}
np->rx_skbuff[i] = 0;
}
+
for (i = 0; i < TX_RING_SIZE; i++) {
if (np->tx_skbuff[i])
dev_kfree_skb(np->tx_skbuff[i]);
@@ -1165,14 +1261,19 @@
unregister_netdev(dev);
release_region(pci_resource_start (pdev, 0),
- via_rhine_chip_info[np->chip_id].io_size);
+ pci_resource_len (pdev, 0));
release_mem_region(pci_resource_start (pdev, 1),
- via_rhine_chip_info[np->chip_id].io_size);
+ pci_resource_len (pdev, 1));
-#ifndef VIA_USE_IO
+#ifndef USE_IO
iounmap((char *)(dev->base_addr));
#endif
+ pci_free_consistent(pdev,
+ RX_RING_SIZE * sizeof(struct rx_desc) +
+ TX_RING_SIZE * sizeof(struct tx_desc),
+ np->rx_ring, np->rx_ring_dma);
+
kfree(dev);
}
@@ -1187,7 +1288,15 @@
static int __init via_rhine_init (void)
{
- return pci_module_init (&via_rhine_driver);
+ int rc;
+
+ MOD_INC_USE_COUNT;
+
+ rc = pci_module_init (&via_rhine_driver);
+
+ MOD_DEC_USE_COUNT;
+
+ return rc;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)