patch-2.0.14 linux/drivers/net/de4x5.c

Next file: linux/drivers/net/de4x5.h
Previous file: linux/drivers/net/3c509.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.13/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -205,11 +205,16 @@
 			   reported by <csd@microplex.com> and 
 			   <baba@beckman.uiuc.edu>.
 			  Upgraded alloc_device() code.
+      0.431  28-Jun-96    Fix potential bug in queue_pkt() from discussion
+                          with <csd@microplex.com>
+      0.44   13-Aug-96    Fix RX overflow bug in 2114[023] chips.
+                          Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
+			  and <michael@compurex.com>
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.43 96/6/21 davies@wanton.lkg.dec.com\n";
+static const char *version = "de4x5.c:v0.44 96/8/13 davies@wanton.lkg.dec.com\n";
 
 #include <linux/module.h>
 
@@ -344,6 +349,7 @@
 
 #define MAX_EISA_SLOTS 16
 #define EISA_SLOT_INC 0x1000
+#define EISA_ALLOWED_IRQ_LIST  {5, 9, 10, 11}
 
 #define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"}
 #define DE4X5_NAME_LENGTH 8
@@ -534,6 +540,7 @@
 	int save_cnt;                       /* Flag if state already saved  */
 	struct sk_buff *skb;                /* Save the (re-ordered) skb's  */
     } cache;
+    int rx_ovf;                             /* Check for 'RX overflow' tag  */
 };
 
 /*
@@ -571,6 +578,7 @@
 static void    de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int     de4x5_close(struct device *dev);
 static struct  enet_statistics *de4x5_get_stats(struct device *dev);
+static void    de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
 static void    set_multicast_list(struct device *dev);
 static int     de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 
@@ -584,6 +592,7 @@
 static int     de4x5_tx(struct device *dev);
 static int     de4x5_ast(struct device *dev);
 static int     de4x5_txur(struct device *dev);
+static int     de4x5_rx_ovfc(struct device *dev);
 
 static int     autoconf_media(struct device *dev);
 static void    create_packet(struct device *dev, char *frame, int len);
@@ -672,7 +681,9 @@
 #endif /* MODULE */
 
 static char name[DE4X5_NAME_LENGTH + 1];
+static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
 static int num_de4x5s = 0, num_eth = 0;
+static int cfrv = 0;
 
 /*
 ** Miscellaneous defines...
@@ -756,10 +767,10 @@
     
     dev->base_addr = iobase;
     if (lp->bus == EISA) {
-	printk("%s: %s at %04lx (EISA slot %ld)", 
+	printk("%s: %s at 0x%04lx (EISA slot %ld)", 
 	       dev->name, name, iobase, ((iobase>>12)&0x0f));
     } else {                                 /* PCI port address */
-	printk("%s: %s at %04lx (PCI bus %d, device %d)", dev->name, name,
+	printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name,
 	       iobase, lp->bus_num, lp->device);
     }
     
@@ -888,6 +899,12 @@
 	/* Create a loopback packet frame for later media probing */
 	create_packet(dev, lp->frame, sizeof(lp->frame));
 
+	/* Check if the RX overflow bug needs testing for */
+	tmpchs = cfrv & 0x000000fe;
+	if ((lp->chipset == DC21140) && (tmpchs == 0x20)) {
+	    lp->rx_ovf = 1;
+	}
+
 	/* Initialise the adapter state */
 	lp->state = CLOSED;
 
@@ -1128,26 +1145,21 @@
 	}
 
 	while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
-	    set_bit(0, (void*)&dev->tbusy);
 	    cli();
-	    if (TX_BUFFS_AVAIL) {           /* Fill in a Tx ring entry */
-		load_packet(dev, skb->data, 
-			    TD_IC | TD_LS | TD_FS | skb->len, skb);
-		outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
+	    set_bit(0, (void*)&dev->tbusy);
+	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
-		lp->tx_new = (++lp->tx_new) % lp->txRingSize;
-		dev->trans_start = jiffies;
+	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+	    dev->trans_start = jiffies;
 		    
-		if (TX_BUFFS_AVAIL) {
-		    dev->tbusy = 0;         /* Another pkt may be queued */
-		}
-		skb = de4x5_get_cache(dev);
+	    if (TX_BUFFS_AVAIL) {
+		dev->tbusy = 0;         /* Another pkt may be queued */
 	    }
+	    skb = de4x5_get_cache(dev);
 	    sti();
 	}
-	if (skb && (dev->tbusy || lp->tx_skb[lp->tx_new])) {
-	    de4x5_putb_cache(dev, skb);
-	}
+	if (skb) de4x5_putb_cache(dev, skb);
     }
     
     lp->cache.lock = 0;
@@ -1234,13 +1246,20 @@
 de4x5_rx(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int i, entry;
+    u_long iobase = dev->base_addr;
+    int entry;
     s32 status;
-    char *buf;
     
     for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) {
 	status = lp->rx_ring[entry].status;
 	
+	if (lp->rx_ovf) {
+	    if (inl(DE4X5_MFC) & MFC_FOCM) {
+		de4x5_rx_ovfc(dev);
+		break;
+	    }
+	}
+
 	if (status & RD_FS) {                 /* Remember the start of frame */
 	    lp->rx_old = entry;
 	}
@@ -1269,34 +1288,13 @@
 		}
 		de4x5_dbg_rx(skb, pkt_len);
 
-	/* Push up the protocol stack */
+		/* Push up the protocol stack */
 		skb->protocol=eth_type_trans(skb,dev);
 		netif_rx(skb);
 		    
 		/* Update stats */
 		lp->stats.rx_packets++;
-		for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
-		    if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
-			lp->pktStats.bins[i]++;
-			i = DE4X5_PKT_STAT_SZ;
-		    }
-		}
-		buf = skb->data;              /* Look at the dest addr */
-		if (buf[0] & 0x01) {          /* Multicast/Broadcast */
-		    if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
-			lp->pktStats.broadcast++;
-		    } else {
-			lp->pktStats.multicast++;
-		    }
-		} else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
-			   (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
-		    lp->pktStats.unicast++;
-		}
-		
-		lp->pktStats.bins[0]++;       /* Duplicates stats.rx_packets */
-		if (lp->pktStats.bins[0] == 0) { /* Reset counters */
-		    memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
-		}
+		de4x5_local_stats(dev, skb->data, pkt_len);
 	    }
 	    
 	    /* Change buffer ownership for this frame, back to the adapter */
@@ -1338,7 +1336,6 @@
 		if (status & TD_NC) lp->stats.tx_carrier_errors++;
 		if (status & TD_LC) lp->stats.tx_window_errors++;
 		if (status & TD_UF) lp->stats.tx_fifo_errors++;
-		if (status & TD_LC) lp->stats.collisions++;
 		if (status & TD_EC) lp->pktStats.excessive_collisions++;
 		if (status & TD_DE) lp->stats.tx_aborted_errors++;
 	    
@@ -1353,6 +1350,10 @@
 		lp->lostMedia = 0;        /* Remove transient problem */
 		lp->linkOK++;
 	    }
+	    /* Update the collision counter */
+	    lp->stats.collisions += ((status & TD_EC) ? 16 : 
+				                      ((status & TD_CC) >> 3));
+
 	    /* Free the buffer. */
 	    if (lp->tx_skb[entry] != NULL) {
 		dev_kfree_skb(lp->tx_skb[entry], FREE_WRITE);
@@ -1416,6 +1417,27 @@
     return 0;
 }
 
+static int 
+de4x5_rx_ovfc(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int iobase = dev->base_addr;
+    int omr;
+
+    omr = inl(DE4X5_OMR);
+    outl(omr & ~OMR_SR, DE4X5_OMR);
+    while (inl(DE4X5_STS) & STS_RS);
+
+    for (; lp->rx_ring[lp->rx_new].status>=0;) {
+	lp->rx_ring[lp->rx_new].status = R_OWN;
+	lp->rx_new = (++lp->rx_new % lp->rxRingSize);
+    }
+
+    outl(omr, DE4X5_OMR);
+    
+    return 0;
+}
+
 static int
 de4x5_close(struct device *dev)
 {
@@ -1469,6 +1491,37 @@
 }
 
 static void
+de4x5_local_stats(struct device *dev, char *buf, int pkt_len)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int i;
+
+    for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
+        if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
+	    lp->pktStats.bins[i]++;
+	    i = DE4X5_PKT_STAT_SZ;
+	}
+    }
+    if (buf[0] & 0x01) {          /* Multicast/Broadcast */
+        if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
+	    lp->pktStats.broadcast++;
+	} else {
+	    lp->pktStats.multicast++;
+	}
+    } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
+	       (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+        lp->pktStats.unicast++;
+    }
+		
+    lp->pktStats.bins[0]++;       /* Duplicates stats.rx_packets */
+    if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+        memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+    }
+
+    return;
+}
+
+static void
 load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -1608,11 +1661,16 @@
     for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) {
 	    cfid = inl(PCI_CFID);
+	    cfrv = inl(PCI_CFRV);
 	    device = (u_short)(cfid >> 16);
 	    vendor = (u_short) cfid;
 	    
+	    /* Read the EISA Configuration Registers */
+	    dev->irq = inb(EISA_REG0);
+	    dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
+
 	    lp->chipset = device;
-	    DevicePresent(EISA_APROM);
+	    DevicePresent(DE4X5_APROM);
 	    /* Write the PCI Configuration Registers */
 	    outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
 	    outl(0x00006000, PCI_CFLT);
@@ -1690,6 +1748,9 @@
 	    /* Set the chipset information */
 	    lp->chipset = device;
 	    
+	    /* Get the chip configuration revision register */
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
 	    /* Get the board I/O address */
 	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
 	    iobase &= CBIO_MASK;
@@ -3552,7 +3613,10 @@
     
     /* Restore CSR6 */
     outl(omr, DE4X5_OMR);
-    
+
+    /* Reset CSR8 */
+    inl(DE4X5_MFC);
+
     return omr;
 }
 
@@ -3579,6 +3643,9 @@
     /* Restore CSR6 */
     outl(omr, DE4X5_OMR);
     
+    /* Reset CSR8 */
+    inl(DE4X5_MFC);
+
     return omr;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov