patch-2.1.132 linux/drivers/net/acenic.c

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

diff -u --recursive --new-file v2.1.131/linux/drivers/net/acenic.c linux/drivers/net/acenic.c
@@ -13,8 +13,6 @@
  * (at your option) any later version.
  */
 
-#define DEBUG 1
-#define RX_DMA_SKBUFF 1
 #define PKT_COPY_THRESHOLD 300
 
 #include <linux/module.h>
@@ -70,14 +68,71 @@
  * by running `ifconfig eth<X> mtu <MTU>' with <X> being the Ethernet
  * interface number and <MTU> being the MTU value.
  *
+ * Module parameters:
+ *
+ * When compiled as a loadable module, the driver allows for a number
+ * of module parameters to be specified. The driver supports the
+ * following module parameters:
+ *
+ *  trace=<val> - Firmware trace level. This requires special traced
+ *                firmware to replace the firmware supplied with
+ *                the driver - for debugging purposes only.
+ *
+ *  link=<val>  - Link state. Normally you want to use the default link
+ *                parameters set by the driver. This can be used to
+ *                override these in case your switch doesn't negotiate
+ *                the link properly. Valid values are:
+ *         0x0001 - Force half duplex link.
+ *         0x0002 - Do not negotiate line speed with the other end.
+ *         0x0010 - 10Mbit/sec link.
+ *         0x0020 - 100Mbit/sec link.
+ *         0x0040 - 1000Mbit/sec link.
+ *         0x0100 - Do not negotiate flow control.
+ *         0x0200 - Enable RX flow control Y
+ *         0x0400 - Enable TX flow control Y (Tigon II NICs only).
+ *                Default value is 0x0270, ie. enable link+flow
+ *                control negotiation. Negotiating the highest
+ *                possible link speed with RX flow control enabled.
+ *
+ *                When disabling link speed negotiation, only one link
+ *                speed is allowed to be specified!
+ *
+ *  tx_coal_tick=<val> - number of coalescing clock ticks (us) allowed
+ *                to wait for more packets to arive before
+ *                interrupting the host, from the time the first
+ *                packet arrives.
+ *
+ *  rx_coal_tick=<val> - number of coalescing clock ticks (us) allowed
+ *                to wait for more packets to arive in the transmit ring,
+ *                before interrupting the host, after transmitting the
+ *                first packet in the ring.
+ *
+ *  max_tx_desc=<val> - maximum number of transmit descriptors
+ *                (packets) transmitted before interrupting the host.
+ *
+ *  max_rx_desc=<val> - maximum number of receive descriptors
+ *                (packets) received before interrupting the host.
+ *
+ * If you use more than one NIC, specify the parameters for the
+ * individual NICs with a comma, ie. trace=0,0x00001fff,0 you want to
+ * run tracing on NIC #2 but not on NIC #1 and #3.
+ *
  * TODO:
  *
  * - Add multicast support.
- * - Make all the tuning parameters and link speed negotiation, user
- *   settable at driver/module init time.
+ * - The Tigon II firmware fails to run in some PCs.
+ * - NIC dump support.
+ * - More tuning parameters.
  */
 
-static const char *version = "acenic.c: v0.13 11/25/98  Jes Sorensen (Jes.Sorensen@cern.ch)\n";
+static int link[8] = {0, };
+static int trace[8] = {0, };
+static int tx_coal_tick[8] = {0, };
+static int rx_coal_tick[8] = {0, };
+static int max_tx_desc[8] = {0, };
+static int max_rx_desc[8] = {0, };
+
+static const char *version = "acenic.c: v0.19 12/17/98  Jes Sorensen (Jes.Sorensen@cern.ch)\n";
 
 static struct device *root_dev = NULL;
 
@@ -149,6 +204,7 @@
 
 		if (!(ap->pci_command & PCI_COMMAND_MASTER)){
 			ap->pci_command |= PCI_COMMAND_MASTER;
+
 			pcibios_write_config_word(pci_bus, pci_dev_fun,
 						  PCI_COMMAND,
 						  ap->pci_command);
@@ -226,7 +282,11 @@
 			break;
 		}
 
-		ace_init(dev);
+#ifdef MODULE
+		ace_init(dev, boards_found);
+#else
+		ace_init(dev, -1);
+#endif
 
 		boards_found++;
 
@@ -255,6 +315,17 @@
 
 
 #ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");
+MODULE_DESCRIPTION("AceNIC/3C985 Gigabit Ethernet driver");
+MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
+#endif
+
 int init_module(void)
 {
 	int cards;
@@ -299,6 +370,8 @@
 		}
 
 		iounmap(regs);
+		if(ap->trace_buf)
+			kfree(ap->trace_buf);
 		kfree(ap->info);
 		free_irq(root_dev->irq, root_dev);
 		unregister_netdev(root_dev);
@@ -326,7 +399,7 @@
 }
 
 
-__initfunc(static int ace_init(struct device *dev))
+__initfunc(static int ace_init(struct device *dev, int board_idx))
 {
 	struct ace_private *ap;
 	struct ace_regs *regs;
@@ -345,7 +418,7 @@
 
 	{
 		long myjif = jiffies + HZ;
-		while (myjif > jiffies);
+		while (time_before(jiffies, myjif));
 	}
 #endif
 
@@ -430,7 +503,14 @@
 	 */
 	tmp = READ_CMD_MEM | WRITE_CMD_MEM;
 	if (ap->version == 2){
+#if 0
+		/*
+		 * According to the documentation this enables writes
+		 * to all PCI regs - NOT good.
+		 */
 		tmp |= DMA_WRITE_ALL_ALIGN;
+#endif
+		tmp |= MEM_READ_MULTIPLE;
 		if (ap->pci_command & PCI_COMMAND_INVALIDATE){
 			switch(L1_CACHE_BYTES){
 			case 16:
@@ -473,6 +553,13 @@
 		return -EAGAIN;
 	}
 
+	/*
+	 * Register the device here to be able to catch allocated
+	 * interrupt handlers in case the firmware doesn't come up.
+	 */
+	ap->next = root_dev;
+	root_dev = dev;
+
 	ap->info = info;
 	memset(info, 0, sizeof(struct ace_info));
 
@@ -546,12 +633,16 @@
 
 	info->tx_csm_ptr = virt_to_bus(&ap->tx_csm);
 
+	/*
+	 * Potential item for tuning parameter
+	 */
 	regs->DmaReadCfg = DMA_THRESH_8W;
 	regs->DmaWriteCfg = DMA_THRESH_8W;
 
 	regs->MaskInt = 0;
 	regs->IfIdx = 1;
 
+	regs->AssistState = 1;
 #if 0
 {
 	u32 tmp;
@@ -565,22 +656,87 @@
 	regs->MacRxState = tmp;
 }
 #endif
+
 	regs->TuneStatTicks = 2 * TICKS_PER_SEC;
-	regs->TuneTxCoalTicks = TICKS_PER_SEC / 500;
-	regs->TuneMaxTxDesc = 7;
-	regs->TuneRxCoalTicks = TICKS_PER_SEC / 10000;
-	regs->TuneMaxRxDesc = 2;
-	regs->TuneTrace = 0 /* 0x30001fff */;
-	tmp = LNK_ENABLE | LNK_FULL_DUPLEX | LNK_1000MB |
-		LNK_RX_FLOW_CTL_Y | LNK_NEG_FCTL | LNK_NEGOTIATE;
-	if(ap->version == 1)
-		regs->TuneLink = tmp;
-	else{
-		tmp |= LNK_TX_FLOW_CTL_Y;
-		regs->TuneLink = tmp;
-		regs->TuneFastLink = tmp;
+
+	if ((board_idx < 8) && tx_coal_tick[board_idx])
+		regs->TuneTxCoalTicks = tx_coal_tick[board_idx] *
+			TICKS_PER_SEC / 1000;
+	else
+		regs->TuneTxCoalTicks = TICKS_PER_SEC / 500;
+	if ((board_idx < 8) && max_tx_desc[board_idx])
+		regs->TuneMaxTxDesc = max_tx_desc[board_idx];
+	else
+		regs->TuneMaxTxDesc = 7;
+
+	if ((board_idx < 8) && rx_coal_tick[board_idx])
+		regs->TuneRxCoalTicks = rx_coal_tick[board_idx] *
+			TICKS_PER_SEC / 1000;
+	else
+		regs->TuneRxCoalTicks = TICKS_PER_SEC / 10000;
+	if ((board_idx < 8) && max_rx_desc[board_idx])
+		regs->TuneMaxRxDesc = max_rx_desc[board_idx];
+	else
+		regs->TuneMaxRxDesc = 2;
+
+	if (board_idx < 8)
+		regs->TuneTrace = trace[board_idx];
+	else
+		regs->TuneTrace = 0;
+
+	tmp = LNK_ENABLE;
+
+	if ((board_idx > 7) || !(link[board_idx])){
+		if (board_idx > 8)
+			printk(KERN_WARNING "%s: more then 8 NICs detected, "
+			       "ignoring link options!\n", dev->name);
+		/*
+		 * No link options specified, we go for the defaults
+		 */
+		tmp |=  LNK_FULL_DUPLEX | LNK_1000MB | LNK_100MB | LNK_10MB |
+			LNK_RX_FLOW_CTL_Y | LNK_NEG_FCTL | LNK_NEGOTIATE;
+
+		if(ap->version == 2)
+			tmp |= LNK_TX_FLOW_CTL_Y;
+	} else {
+		int option = link[board_idx];
+		if (option & 0x01){
+			printk(KERN_INFO "%s: Setting half duplex link\n",
+			       dev->name);
+			tmp |= LNK_FULL_DUPLEX;
+		}
+		if ((option & 0x02) == 0)
+			tmp |= LNK_NEGOTIATE;
+		if (option & 0x10)
+			tmp |= LNK_10MB;
+		if (option & 0x20)
+			tmp |= LNK_100MB;
+		if (option & 0x40)
+			tmp |= LNK_1000MB;
+		if ((option & 0x70) == 0){
+			printk(KERN_WARNING "%s: No media speed specified, "
+			       "forcing auto negotiation\n", dev->name);
+			tmp |= LNK_NEGOTIATE | LNK_1000MB |
+				LNK_100MB | LNK_10MB;
+		}
+		if ((option & 0x100) == 0)
+			tmp |= LNK_NEG_FCTL;
+		else
+			printk(KERN_INFO "%s: Disabling flow control "
+			       "negotiation\n", dev->name);
+		if (option & 0x200)
+			tmp |= LNK_RX_FLOW_CTL_Y;
+		if ((option & 0x400) && (ap->version == 2)){
+			printk(KERN_INFO "%s: Enabling TX flow control\n",
+			       dev->name);
+			tmp |= LNK_TX_FLOW_CTL_Y;
+		}
 	}
 
+	regs->TuneLink = tmp;
+	if (ap->version == 2)
+		regs->TuneFastLink = tmp;
+
 	if (ap->version == 1)
 		regs->Pc = tigonFwStartAddr;
 	else if (ap->version == 2)
@@ -595,18 +751,17 @@
 	regs->CpuCtrl = (regs->CpuCtrl & ~(CPU_HALT | CPU_TRACE));
 
 	/*
-	 * Wait for the firmware to spin up - max 2 seconds.
+	 * Wait for the firmware to spin up - max 3 seconds.
 	 */
 	myjif = jiffies + 3 * HZ;
-	while ((myjif > jiffies) && !ap->fw_running);
+	while (time_before(jiffies, myjif) && !ap->fw_running);
 	if (!ap->fw_running){
 		printk(KERN_ERR "%s: firmware NOT running!\n", dev->name);
+		ace_dump_trace(ap);
+		regs->CpuCtrl |= CPU_HALT;
 		return -EBUSY;
 	}
 
-	ap->next = root_dev;
-	root_dev = dev;
-
 	/*
 	 * We load the ring here as there seem to be no way to tell the
 	 * firmware to wipe the ring without re-initializing it.
@@ -620,7 +775,6 @@
 /*
  * Monitor the card to detect hangs.
  */
-
 static void ace_timer(unsigned long data)
 {
 	struct device *dev = (struct device *)data;
@@ -643,6 +797,17 @@
 
 
 /*
+ * Copy the contents of the NIC's trace buffer to kernel memory.
+ */
+static void ace_dump_trace(struct ace_private *ap)
+{
+	if (!ap->trace_buf)
+		if (!(ap->trace_buf = kmalloc(ACE_TRACE_SIZE, GFP_KERNEL)));
+		    return;
+}
+
+
+/*
  * Load the standard rx ring.
  */
 static int ace_load_std_rx_ring(struct device *dev)
@@ -822,7 +987,6 @@
 			ap->fw_running = 1;
 			break;
 		case E_STATS_UPDATED:
-			mod_timer(&ap->timer, jiffies + (5/2*HZ));
 			break;
 		case E_LNK_STATE:
 		{
@@ -1036,18 +1200,6 @@
 		return;
 	}
 
-#if 0
-	/*
-	 * Since we are also using a spinlock, I wonder if this is
-	 * actually worth it.
-	 */
-	if (test_and_set_bit(0, (void*)&dev->interrupt) != 0) {
-		printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n",
-		       dev->name);
-		return;
-	}
-#endif
-
 	/*
 	 * Tell the card not to generate interrupts while we are in here.
 	 */
@@ -1076,6 +1228,12 @@
 			ap->tx_full = 0;
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
+
+			/*
+			 * TX ring is no longer full, aka the
+			 * transmitter is working fine - kill timer.
+			 */
+			del_timer(&ap->timer);
 		}
 
 		ap->tx_ret_csm = txcsm;
@@ -1098,9 +1256,6 @@
 	regs->Mb0Lo = 0;
 
 	spin_unlock(&ap->lock);
-#if 0
-	dev->interrupt = 0;
-#endif
 }
 
 
@@ -1140,7 +1295,7 @@
 
 #if 0
 	{ long myjif = jiffies + HZ;
-	while (jiffies < myjif);
+	while (time_before(jiffies, myjif));
 	}
 
 	cmd.evt = C_LNK_NEGOTIATION;
@@ -1159,10 +1314,8 @@
 	 * Setup the timer
 	 */
 	init_timer(&ap->timer);
-	ap->timer.expires = jiffies + 5/2 * HZ;
 	ap->timer.data = (unsigned long)dev;
-	ap->timer.function = &ace_timer;
-	add_timer(&ap->timer);
+	ap->timer.function = ace_timer;
 	return 0;
 }
 
@@ -1239,6 +1392,12 @@
 	if ((idx + 1) % TX_RING_ENTRIES == ap->tx_ret_csm){
 		ap->tx_full = 1;
 		set_bit(0, (void*)&dev->tbusy);
+		/*
+		 * Queue is full, add timer to detect whether the
+		 * transmitter is stuck.
+		 */
+		ap->timer.expires = jiffies + (3 * HZ);
+		add_timer(&ap->timer);
 	}
 
 	spin_unlock_irqrestore(&ap->lock, flags);

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