patch-2.4.3 linux/drivers/net/tulip/pnic.c

Next file: linux/drivers/net/tulip/tulip.h
Previous file: linux/drivers/net/tulip/media.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.2/linux/drivers/net/tulip/pnic.c linux/drivers/net/tulip/pnic.c
@@ -50,6 +50,61 @@
 	}
 }
 
+/* Modified version of tulip_check_duplex:
+ * Always update the 100mbps bit, even if the
+ * full duplex bit didn't change.
+ *	Manfred Spraul <manfred@colorfullife.com>
+ */
+int pnic_check_duplex(struct net_device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int mii_reg1, mii_reg5, negotiated, duplex;
+	int new_csr6;
+
+	mii_reg1 = tulip_mdio_read(dev, tp->phys[0], 1);
+	mii_reg5 = tulip_mdio_read(dev, tp->phys[0], 5);
+	if (tulip_debug > 1)
+		printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
+			   "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
+	if (mii_reg1 == 0xffff)
+		return -2;
+	if ((mii_reg1 & 0x0004) == 0) {
+		int new_reg1 = tulip_mdio_read(dev, tp->phys[0], 1);
+		if ((new_reg1 & 0x0004) == 0) {
+			if (tulip_debug  > 1)
+				printk(KERN_INFO "%s: No link beat on the MII interface,"
+					   " status %4.4x.\n", dev->name, new_reg1);
+			return -1;
+		}
+	}
+	negotiated = mii_reg5 & tp->advertising[0];
+	/* 100baseTx-FD  or  10T-FD, but not 100-HD */
+	duplex = ((negotiated & 0x0300) == 0x0100
+			  || (negotiated & 0x00C0) == 0x0040) ||
+		tp->full_duplex_lock;
+
+	new_csr6 = tp->csr6;
+	if (negotiated & 0x0380)	/* 100mbps. */
+		new_csr6 &= ~0x00400000;
+	 else
+		new_csr6 |= 0x00400000;
+	if (duplex)
+		new_csr6 |= 0x0200;
+	 else	
+		new_csr6 &= ~0x0200;
+	if (new_csr6 != tp->csr6) {
+		tp->full_duplex = duplex;
+		tp->csr6 = new_csr6;
+		if (tulip_debug > 0)
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+				   "#%d link partner capability of %4.4x.\n",
+				   dev->name, tp->full_duplex ? "full" : "half",
+				   tp->phys[0], mii_reg5);
+		tulip_restart_rxtx(tp, tp->csr6);
+		return 1;
+	}
+	return 0;
+}
 
 void pnic_lnk_change(struct net_device *dev, int csr5)
 {
@@ -62,6 +117,11 @@
 			   dev->name, phy_reg, csr5);
 	if (inl(ioaddr + CSR5) & TPLnkFail) {
 		outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
+		/* If we use an external MII, then we mustn't use the 
+		 * internal negotiation.
+		 */
+		if (tulip_media_cap[dev->if_port] & MediaIsMII)
+			return;
 		if (! tp->nwayset  ||  jiffies - dev->trans_start > 1*HZ) {
 			tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
 			tulip_outl_csr(tp, tp->csr6, CSR6);
@@ -70,11 +130,18 @@
 			dev->trans_start = jiffies;
 		}
 	} else if (inl(ioaddr + CSR5) & TPLnkPass) {
-		pnic_do_nway(dev);
+		if (tulip_media_cap[dev->if_port] & MediaIsMII) {
+			spin_lock(&tp->lock);
+			pnic_check_duplex(dev);
+			spin_unlock(&tp->lock);
+		} else {
+			pnic_do_nway(dev);
+		}
 		outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7);
 	}
 }
 
+int tulip_refill_rx(struct net_device *dev);
 
 void pnic_timer(unsigned long data)
 {
@@ -82,10 +149,21 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int next_tick = 60*HZ;
+	
+	if(!inl(ioaddr + CSR7)) {
+		/* the timer was called due to a work overflow
+		 * in the interrupt handler. Skip the connection
+		 * checks, the nic is definitively speaking with
+		 * his link partner.
+		 */
+		goto too_good_connection;
+	}
 
 	if (tulip_media_cap[dev->if_port] & MediaIsMII) {
-		if (tulip_check_duplex(dev) > 0)
+		spin_lock_irq(&tp->lock);
+		if (pnic_check_duplex(dev) > 0)
 			next_tick = 3*HZ;
+		spin_unlock_irq(&tp->lock);
 	} else {
 		int csr12 = inl(ioaddr + CSR12);
 		int new_csr6 = tp->csr6 & ~0x40C40200;
@@ -137,7 +215,14 @@
 			}
 		}
 	}
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
+too_good_connection:
+	mod_timer(&tp->timer, RUN_AT(next_tick));
+	if(!inl(ioaddr + CSR7)) {
+		if (tulip_debug > 1)
+			printk(KERN_INFO "%s: sw timer wakeup.\n", dev->name);
+		disable_irq(dev->irq);
+		tulip_refill_rx(dev);
+		enable_irq(dev->irq);
+		outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+	}
 }
-

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)