patch-2.2.16 linux/drivers/net/3c59x.c

Next file: linux/drivers/net/82596.c
Previous file: linux/drivers/net/3c515.c
Back to the patch index
Back to the overall index

diff -urN v2.2.15/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
@@ -14,8 +14,39 @@
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 */
 
+/*
+    15Apr00, Andrew Morton <andrewm@uow.edu.au>
+    - Don't set RxComplete in boomerang interrupt enable reg
+    - Added standard spinlocking.
+    - Removed setting/clearing of dev->interrupt
+    - Removed vp->in_interrupt
+    - spinlock in vortex_timer to protect mdio functions
+    - disable local interrupts around call to vortex_interrupt in
+      vortex_tx_timeout() (So vortex_interrupt can use spin_lock())
+    - Removed global save/restore_flags() and cli() from get_stats
+      and vortex_start_xmit()
+    - Select window 3 in vortex_timer()'s write to Wn3_MAC_Ctrl
+    - In vortex_start_xmit(), move the lock to _after_ we've altered
+      vp->cur_tx and vp->tx_full.  This defeats the race between
+      vortex_start_xmit() and vortex_interrupt which was identified
+      by Bogdan Costescu.
+    - Merged back support for six new cards from various sources
+    - Tell it that 3c905C has NWAY
+    - Fix handling of SetStatusEnd in 'Too much work..' code, as
+      per 2.3.99's 3c575_cb (Dave Hinds).  Added vp->deferred for this.
+
+    24May00 <2.2.16-pre4> andrewm
+    - Replace union wn3_config with BFINS/BFEXT manipulation for
+      sparc64 (Pete Zaitcev, Peter Jones)
+    - Use insl_ns/outsl_ns for __powerpc__ (Andreas Tobler)
+    - In vortex_error, do_tx_reset and vortex_tx_timeout(Vortex): clear
+      tbusy and force a BH rerun to better recover from errors.
+
+    - See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.2 for more details.
+*/
+
 static char *version =
-"3c59x.c:v0.99H 11/17/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99H 27May00 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
@@ -233,7 +264,7 @@
 							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
 };
 
-enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4,
+enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, IS_TORNADO=8,
 	   HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, };
 static struct device *vortex_probe1(int pci_bus, int pci_devfn,
 									struct device *dev, long ioaddr,
@@ -241,12 +272,18 @@
 static struct pci_id_info pci_tbl[] = {
 	{"3c590 Vortex 10Mbps",			0x10B7, 0x5900, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3c592 EISA 10mbps Demon/Vortex", 0x10B7, 0x5920, 0xffff,		/* AKPM: from Don's 3c59x_cb.c 0.49H */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3c597 EISA Fast Demon/Vortex", 0x10B7, 0x5970, 0xffff,		/* AKPM: from Don's 3c59x_cb.c 0.49H */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
 	{"3c595 Vortex 100baseTx",		0x10B7, 0x5950, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
 	{"3c595 Vortex 100baseT4",		0x10B7, 0x5951, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
 	{"3c595 Vortex 100base-MII",	0x10B7, 0x5952, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+
+#define EISA_TBL_OFFSET 6			/* AKPM: the offset of this entry */
 	{"3Com Vortex",					0x10B7, 0x5900, 0xff00,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
 	{"3c900 Boomerang 10baseT",		0x10B7, 0x9000, 0xffff,
@@ -257,6 +294,10 @@
 	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
 	{"3c900B-FL Cyclone 10base-FL",	0x10B7, 0x900A, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c900 Cyclone 10Mbps TPO",	0x10B7, 0x9004, 0xffff,			/* AKPM: from Don's 0.99M */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c900 Cyclone 10Mbps TPC",	0x10B7, 0x9006, 0xffff,			/* AKPM: from Don's 0.99M */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
 	{"3c905 Boomerang 100baseTx",	0x10B7, 0x9050, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
 	{"3c905 Boomerang 100baseT4",	0x10B7, 0x9051, 0xffff,
@@ -268,7 +309,7 @@
 	{"3c905B-FX Cyclone 100baseFx",	0x10B7, 0x905A, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
 	{"3c905C Tornado",	0x10B7, 0x9200, 0xffff,
-	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	 PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, vortex_probe1},
 	{"3c980 Cyclone",	0x10B7, 0x9800, 0xfff0,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
 	{"3cSOHO100-TX Hurricane",	0x10B7, 0x7646, 0xffff,
@@ -283,8 +324,13 @@
 	{"3CCFE656 Cyclone CardBus",	0x10B7, 0x6560, 0xffff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
 	 128, vortex_probe1},
+	{"3CCFEM656 Cyclone CardBus",	0x10B7, 0x6562, 0xffff,			/* AKPM: From the 2.3 driver ? */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
+	 128, vortex_probe1},
 	{"3c575 series CardBus (unknown version)", 0x10B7, 0x5057, 0xf0ff,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
+	{"3c450 HomePNA Tornado", 0x10B7, 0x4500, 0xffff,				/* AKPM: from Don's 0.99P */
+	 PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, vortex_probe1},
 	{"3Com Boomerang (unknown version)",	0x10B7, 0x9000, 0xff00,
 	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
 	{0,},						/* 0 terminated list. */
@@ -363,15 +409,21 @@
 enum Window3 {			/* Window 3: MAC/config bits. */
 	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
 };
-union wn3_config {
-	int i;
-	struct w3_config_fields {
-		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
-		int pad8:8;
-		unsigned int ram_split:2, pad18:2, xcvr:4, autoselect:1;
-		int pad24:7;
-	} u;
-};
+
+#define BFEXT(value, offset, bitcount)  \
+    ((((unsigned long)(value)) >> (offset)) & ((1 << (bitcount)) - 1))
+
+#define BFINS(lhs, rhs, offset, bitcount)			\
+    (((lhs) & ~((((1 << (bitcount)) - 1)) << (offset))) |	\
+        (((rhs) & ((1 << (bitcount)) - 1)) << (offset)))
+
+#define RAM_SIZE(v)		BFEXT(v, 0, 3)
+#define RAM_WIDTH(v)	BFEXT(v, 3, 1)
+#define RAM_SPEED(v)	BFEXT(v, 4, 2)
+#define ROM_SIZE(v)		BFEXT(v, 6, 2)
+#define RAM_SPLIT(v)	BFEXT(v, 16, 2)
+#define XCVR(v)			BFEXT(v, 20, 4)
+#define AUTOSELECT(v)	BFEXT(v, 24, 1)
 
 enum Window4 {		/* Window 4: Xcvr/media bits. */
 	Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
@@ -446,7 +498,6 @@
 	int chip_id;
 
 	/* The remainder are related to chip state, mostly media selection. */
-	unsigned long in_interrupt;
 	struct timer_list timer;	/* Media selection timer. */
 	int options;				/* User-settable misc. driver options. */
 	unsigned int media_override:3, 			/* Passed-in media type. */
@@ -462,6 +513,9 @@
 	u16 capabilities, info1, info2;		/* Various, from EEPROM. */
 	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[2];				/* MII device addresses. */
+	u16 deferred;						/* Resend these interrupts when we
+										 * bale from the ISR */
+	spinlock_t lock;
 };
 
 /* The action to take with a media selection timer tick.
@@ -765,7 +819,7 @@
 			if ((device_id & 0xFF00) != 0x5900)
 				continue;
 			vortex_probe1(0, 0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-						  4, cards_found);
+						  EISA_TBL_OFFSET, cards_found);
 			dev = 0;
 			cards_found++;
 		}
@@ -814,6 +868,7 @@
 	vp->next_module = root_vortex_dev;
 	root_vortex_dev = dev;
 
+	spin_lock_init(&vp->lock);
 	vp->chip_id = chip_idx;
 	vp->pci_bus = pci_bus;
 	vp->pci_devfn = pci_devfn;
@@ -866,7 +921,7 @@
 			checksum ^= eeprom[i++];
 		checksum = (checksum ^ (checksum >> 8)) & 0xff;
 	}
-	if (checksum != 0x00)
+	if (checksum != 0x00 && !(pci_tbl[chip_idx].drv_flags & IS_TORNADO))
 		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
 
 	for (i = 0; i < 3; i++)
@@ -905,24 +960,24 @@
 
 	{
 		char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
-		union wn3_config config;
+		unsigned int config;
 		EL3WINDOW(3);
 		vp->available_media = inw(ioaddr + Wn3_Options);
 		if ((vp->available_media & 0xff) == 0)		/* Broken 3c916 */
 			vp->available_media = 0x40;
-		config.i = inl(ioaddr + Wn3_Config);
+		config = inl(ioaddr + Wn3_Config);
 		if (vortex_debug > 1)
 			printk(KERN_DEBUG "  Internal config register is %4.4x, "
-				   "transceivers %#x.\n", config.i, inw(ioaddr + Wn3_Options));
+				   "transceivers %#x.\n", config, inw(ioaddr + Wn3_Options));
 		printk(KERN_INFO "  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
-			   8 << config.u.ram_size,
-			   config.u.ram_width ? "word" : "byte",
-			   ram_split[config.u.ram_split],
-			   config.u.autoselect ? "autoselect/" : "",
-			   config.u.xcvr > XCVR_ExtMII ? "<invalid transceiver>" :
-			   media_tbl[config.u.xcvr].name);
-		vp->default_media = config.u.xcvr;
-		vp->autoselect = config.u.autoselect;
+			   8 << RAM_SIZE(config),
+			   RAM_WIDTH(config) ? "word" : "byte",
+			   ram_split[RAM_SPLIT(config)],
+			   AUTOSELECT(config) ? "autoselect/" : "",
+			   XCVR(config) > XCVR_ExtMII ? "<invalid transceiver>" :
+			   media_tbl[XCVR(config)].name);
+		vp->default_media = XCVR(config);
+		vp->autoselect = AUTOSELECT(config);
 	}
 
 	if (vp->media_override != 7) {
@@ -990,12 +1045,12 @@
 {
 	long ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	union wn3_config config;
+	unsigned int config;
 	int i;
 
 	/* Before initializing select the active media port. */
 	EL3WINDOW(3);
-	config.i = inl(ioaddr + Wn3_Config);
+	config = inl(ioaddr + Wn3_Config);
 
 	if (vp->media_override != 7) {
 		if (vortex_debug > 1)
@@ -1024,8 +1079,8 @@
 			   dev->name, media_tbl[dev->if_port].name);
 
 	vp->full_duplex = vp->force_fd;
-	config.u.xcvr = dev->if_port;
-	outl(config.i, ioaddr + Wn3_Config);
+	config = BFINS(config, dev->if_port, 20, 4);
+	outl(config, ioaddr + Wn3_Config);
 
 	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
 		int mii_reg1, mii_reg5;
@@ -1051,7 +1106,7 @@
 
 	if (vortex_debug > 1) {
 		printk(KERN_DEBUG "%s: vortex_open() InternalConfig %8.8x.\n",
-			dev->name, config.i);
+			dev->name, config);
 	}
 
 	outw(TxReset, ioaddr + EL3_CMD);
@@ -1151,9 +1206,7 @@
 	set_rx_mode(dev);
 	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
 
-	vp->in_interrupt = 0;
 	dev->tbusy = 0;
-	dev->interrupt = 0;
 	dev->start = 1;
 
 	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
@@ -1163,7 +1216,8 @@
 		(vp->full_bus_master_tx ? DownComplete : TxAvailable) |
 		(vp->full_bus_master_rx ? UpComplete : RxComplete) |
 		(vp->bus_master ? DMADone : 0);
-	vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable | RxComplete |
+	vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable |
+		(vp->full_bus_master_rx ? 0 : RxComplete) |
 		StatsFull | HostError | TxComplete | IntReq
 		| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete;
 	outw(vp->status_enable, ioaddr + EL3_CMD);
@@ -1184,7 +1238,7 @@
 	struct device *dev = (struct device *)data;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int next_tick = 0;
+	int next_tick = 60 * HZ;
 	int ok = 0;
 	int media_status, mii_status, old_window;
 
@@ -1197,7 +1251,7 @@
 	EL3WINDOW(4);
 	media_status = inw(ioaddr + Wn4_Media);
 	switch (dev->if_port) {
-	case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
+	  case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
 		if (media_status & Media_LnkBeat) {
 		  ok = 1;
 		  if (vortex_debug > 1)
@@ -1208,6 +1262,9 @@
 				   dev->name, media_tbl[dev->if_port].name, media_status);
 		break;
 	  case XCVR_MII: case XCVR_NWAY:
+		{
+		  unsigned long flags;
+		  spin_lock_irqsave(&vp->lock, flags);
 		  mii_status = mdio_read(ioaddr, vp->phys[0], 1);
 		  ok = 1;
 		  if (debug > 1)
@@ -1225,6 +1282,7 @@
 							 dev->name, vp->full_duplex ? "full" : "half",
 							 vp->phys[0], mii_reg5);
 					  /* Set the full-duplex bit. */
+					  EL3WINDOW(3);   /* AKPM */
 					  outb((vp->full_duplex ? 0x20 : 0) |
 						   (dev->mtu > 1500 ? 0x40 : 0),
 						   ioaddr + Wn3_MAC_Ctrl);
@@ -1232,15 +1290,17 @@
 				  next_tick = 60*HZ;
 			  }
 		  }
-		  break;
+		  spin_unlock_irqrestore(&vp->lock, flags);
+		}
+		break;
 	  default:					/* Other media types handled by Tx timeouts. */
 		if (vortex_debug > 1)
-		  printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n",
+		  printk(KERN_DEBUG "%s: Media %s has no indication, %x.\n",
 				 dev->name, media_tbl[dev->if_port].name, media_status);
 		ok = 1;
 	}
 	if ( ! ok) {
-		union wn3_config config;
+		unsigned int config;
 
 		do {
 			dev->if_port = media_tbl[dev->if_port].next;
@@ -1262,9 +1322,9 @@
 			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
 
 		EL3WINDOW(3);
-		config.i = inl(ioaddr + Wn3_Config);
-		config.u.xcvr = dev->if_port;
-		outl(config.i, ioaddr + Wn3_Config);
+		config = inl(ioaddr + Wn3_Config);
+		config = BFINS(config, dev->if_port, 20, 4);
+		outl(config, ioaddr + Wn3_Config);
 
 		outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
 			 ioaddr + EL3_CMD);
@@ -1276,10 +1336,10 @@
 	  printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
 			 dev->name, media_tbl[dev->if_port].name);
 
-	if (next_tick) {
-		vp->timer.expires = RUN_AT(next_tick);
-		add_timer(&vp->timer);
-	}
+	vp->timer.expires = RUN_AT(next_tick);
+	add_timer(&vp->timer);
+	if (vp->deferred)
+		outw(FakeIntr, ioaddr + EL3_CMD);
 	return;
 }
 
@@ -1300,7 +1360,17 @@
 		printk(KERN_ERR "%s: Interrupt posted but not delivered --"
 			   " IRQ blocked by another device?\n", dev->name);
 		/* Bad idea here.. but we might as well handle a few events. */
-		vortex_interrupt(dev->irq, dev, 0);
+		{
+			/*
+			 * AKPM: block interrupts because vortex_interrupt
+			 * does a bare spin_lock()
+			 */
+			unsigned long flags;
+			__save_flags(flags);
+			__cli();
+			vortex_interrupt(dev->irq, dev, 0);
+			__restore_flags(flags);
+		}
 	}
 	outw(TxReset, ioaddr + EL3_CMD);
 	for (j = 200; j >= 0 ; j--)
@@ -1338,13 +1408,15 @@
 		}
 		outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
 		outw(DownUnstall, ioaddr + EL3_CMD);
-	} else
+	} else {
 		vp->stats.tx_dropped++;
-	
+		clear_bit(0, (void*)&dev->tbusy);
+	}
+
 	/* Issue Tx Enable */
 	outw(TxEnable, ioaddr + EL3_CMD);
 	dev->trans_start = jiffies;
-	
+
 	/* Switch to register set 7 for normal use. */
 	EL3WINDOW(7);
 }
@@ -1437,6 +1509,9 @@
 			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 				break;
 		outw(TxEnable, ioaddr + EL3_CMD);
+		clear_bit(0, (void*)&dev->tbusy);
+		if (!vp->full_bus_master_tx)
+			mark_bh(NET_BH);
 	}
 
 }
@@ -1465,7 +1540,11 @@
 		/* dev->tbusy will be cleared at the DMADone interrupt. */
 	} else {
 		/* ... and the packet rounded to a doubleword. */
+#ifdef __powerpc__
+		outsl_ns(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#else
 		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#endif
 		DEV_FREE_SKB(skb);
 		if (inw(ioaddr + TxFree) > 1536) {
 			clear_bit(0, (void*)&dev->tbusy);
@@ -1537,11 +1616,10 @@
 		vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
 		vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
 
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&vp->lock, flags);
 		outw(DownStall, ioaddr + EL3_CMD);
 		/* Wait for the stall to complete. */
-		for (i = 600; i >= 0 ; i--)
+		for (i = 4000; i >= 0 ; i--)
 			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
 				break;
 		prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry]));
@@ -1549,16 +1627,16 @@
 			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
 			queued_packet++;
 		}
-		outw(DownUnstall, ioaddr + EL3_CMD);
-		restore_flags(flags);
 
 		vp->cur_tx++;
 		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
 			vp->tx_full = 1;
-		else {					/* Clear previous interrupt enable. */
+		else {		/* Clear previous interrupt enable. */
 			prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
 			clear_bit(0, (void*)&dev->tbusy);
 		}
+		outw(DownUnstall, ioaddr + EL3_CMD);
+		spin_unlock_irqrestore(&vp->lock, flags);
 		dev->trans_start = jiffies;
 		vp->stats.tx_bytes += skb->len;
 		return 0;
@@ -1575,27 +1653,17 @@
 	int latency, status;
 	int work_done = max_interrupt_work;
 
-#if defined(__i386__)
-	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
-	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
-		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
-			   dev->name);
-		dev->interrupt = 0;	/* Avoid halting machine. */
-		return;
-	}
-#else
-	if (dev->interrupt) {
-		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
-		return;
-	}
-	dev->interrupt = 1;
-#endif
+	spin_lock(&vp->lock);
 
-	dev->interrupt = 1;
 	ioaddr = dev->base_addr;
 	latency = inb(ioaddr + Timer);
 	status = inw(ioaddr + EL3_STATUS);
 
+	if (status & IntReq) {
+		status |= vp->deferred;
+		vp->deferred = 0;
+	}
+
 	if (vortex_debug > 4)
 		printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n",
 			   dev->name, status, latency);
@@ -1666,15 +1734,22 @@
 				outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD);
 			} else {
 				printk(KERN_WARNING "%s: Too much work in interrupt, status "
-					   "%4.4x.  Temporarily disabling functions (%4.4x).\n",
-					   dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
+					   "%4.4x.\n", dev->name, status);
 				/* Disable all pending interrupts. */
-				outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
-				outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
+				do {
+					vp->deferred |= status;
+					outw(SetStatusEnb | (~vp->deferred & vp->status_enable),
+						 ioaddr + EL3_CMD);
+					outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
+				} while ((status = inw(ioaddr + EL3_CMD)) & IntLatch);
 				/* The timer will reenable interrupts. */
+				del_timer(&vp->timer);
+				vp->timer.expires = RUN_AT(1);
+				add_timer(&vp->timer);
 				break;
 			}
 		}
+
 		/* Acknowledge the IRQ. */
 		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
 		if (vp->cb_fn_base)			/* The PCMCIA people are idiots.  */
@@ -1686,11 +1761,7 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
 			   dev->name, status);
 
-#if defined(__i386__)
-	clear_bit(0, (void*)&dev->interrupt);
-#else
-	dev->interrupt = 0;
-#endif
+	spin_unlock(&vp->lock);
 	return;
 }
 
@@ -1702,7 +1773,7 @@
 	short rx_status;
 
 	if (vortex_debug > 5)
-		printk(KERN_DEBUG"   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+		printk(KERN_DEBUG"   In vortex_rx(), status %4.4x, rx_status %4.4x.\n",
 			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
 	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
 		if (rx_status & 0x4000) { /* Error, update stats. */
@@ -1737,8 +1808,13 @@
 					while (inw(ioaddr + Wn7_MasterStatus) & 0x8000)
 						;
 				} else {
+#ifdef __powerpc__
+					insl_ns(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+						 (pkt_len + 3) >> 2);
+#else
 					insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
 						 (pkt_len + 3) >> 2);
+#endif
 				}
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb, dev);
@@ -1844,7 +1920,7 @@
 		entry = (++vp->cur_rx) % RX_RING_SIZE;
 	}
 	/* Refill the Rx ring buffers. */
-	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+	for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
 		struct sk_buff *skb;
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
@@ -1929,10 +2005,9 @@
 	unsigned long flags;
 
 	if (dev->start) {
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&vp->lock, flags);
 		update_stats(dev->base_addr, dev);
-		restore_flags(flags);
+		spin_unlock_irqrestore(&vp->lock, flags);
 	}
 	return &vp->stats;
 }
@@ -1981,6 +2056,10 @@
 	long ioaddr = dev->base_addr;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = vp->phys[0] & 0x1f;
+	int retval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vp->lock, flags);
 
 	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
@@ -1988,16 +2067,23 @@
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
 		EL3WINDOW(4);
 		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
-		return 0;
+		retval = 0;
+		break;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
-			return -EPERM;
-		EL3WINDOW(4);
-		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
-		return 0;
+		if (!suser()) {
+			retval = -EPERM;
+		} else {
+			EL3WINDOW(4);
+			mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+			retval = 0;
+		}
+		break;
 	default:
-		return -EOPNOTSUPP;
+		retval = -EOPNOTSUPP;
 	}
+
+	spin_unlock_irqrestore(&vp->lock, flags);
+	return retval;
 }
 
 /* Pre-Cyclone chips have no documented multicast filter, so the only

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