patch-2.2.17 linux/drivers/net/tlan.c

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

diff -u --recursive --new-file v2.2.16/drivers/net/tlan.c linux/drivers/net/tlan.c
@@ -42,8 +42,21 @@
  * 				  to overwrite timers like TLAN_TIMER_ACTIVITY
  * 				  Patch from John Cagle <john.cagle@compaq.com>
  * 				- Removed dependency of HZ being 100.
- *				- Statistics is now fixed.
+ *				- Statistics are now fixed.
  *				- Minor stuff.
+ *
+ *	v1.3 June 9, 2000	- Updated for better SMP performance.
+ *				- Fixed forced speed/duplex settings.
+ *				- Added support for custom MII ioctls.
+ *				- Code clean up.
+ *				- Thanks to Herman Bos <hbos@mih.net> and 
+ *				  Martin Lathoud <nytral@endirect.qc.ca> 
+ *				  for providing me with extensive debug information.
+ *
+ *	v1.3a June 21, 2000     - Minor fixes when driver build as module.
+ *
+ * 	v1.3b June 28, 2000	- Clean ups.
+ *
  ********************************************************************************/
 
 
@@ -55,29 +68,28 @@
 #include <linux/pci.h>
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
-
+#include <asm/spinlock.h>
 
 
 typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
 
-
-#ifdef MODULE
+#ifdef MODULE   
 
 static	struct device	*TLanDevices = NULL;
 static	int		TLanDevicesInstalled = 0;
 
 static	int		aui = 0;
-static	int		sa_int = 0;
 static	int		duplex = 0;
 static	int		speed = 0;
+static const char *tlan_banner = "ThunderLAN driver v1.3b\n";
 
+MODULE_AUTHOR("Maintainer: Torben Mathiasen <torben.mathiasen@compaq.com>");
+MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet adapters");
 MODULE_PARM(aui, "i");
-MODULE_PARM(sa_int, "i");
 MODULE_PARM(duplex, "i");
 MODULE_PARM(speed, "i");
 MODULE_PARM(debug, "i");
 EXPORT_NO_SYMBOLS;
-
 #endif
 
 
@@ -85,8 +97,6 @@
 static	int		bbuf = 0;
 static	u8		*TLanPadBuffer;
 static	char		TLanSignature[] = "TLAN";
-static	int		TLanVersionMajor = 1;
-static	int		TLanVersionMinor = 2;
 
 
 static	TLanAdapterEntry TLanAdapterList[] = {
@@ -185,6 +195,7 @@
 static int	TLan_Close(struct device *);
 static struct	net_device_stats *TLan_GetStats( struct device * );
 static void	TLan_SetMulticastList( struct device * );
+static int	TLan_ioctl(struct device *dev, struct ifreq *req, int cmd);
 
 static u32	TLan_HandleInvalid( struct device *, u16 );
 static u32	TLan_HandleTxEOF( struct device *, u16 );
@@ -246,14 +257,16 @@
 TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
 {
 	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+	unsigned long flags;
 
-	cli();
+	spin_lock_irqsave(&priv->lock, flags);
 	if ( priv->timer.function != NULL && 
 		priv->timerType != TLAN_TIMER_ACTIVITY) {
+		spin_unlock_irqrestore(&priv->lock, flags);
 		return;
 	}
 	priv->timer.function = &TLan_Timer;
-	sti();
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 	priv->timer.data = (unsigned long) dev;
 	priv->timer.expires = jiffies + ticks;
@@ -305,10 +318,8 @@
 	u8		irq;
 	u8		rev;
 
-	printk(KERN_INFO "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
-		TLanVersionMajor,
-		TLanVersionMinor
-	      );
+	printk(KERN_INFO "%s", tlan_banner);
+	
 	TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
 					( GFP_KERNEL | GFP_DMA )
 				      );
@@ -347,11 +358,12 @@
 			speed = 0;
 		}
 		priv->speed =      speed;
-		priv->sa_int =     sa_int;
 		priv->debug =      debug;
-
+			
 		ether_setup( dev );
 
+		spin_lock_init(&priv->lock);
+
 		failed = register_netdev( dev );
 
 		if ( failed ) {
@@ -496,13 +508,9 @@
 	} else if ( priv->speed == 0x2 ) {
 		priv->speed = TLAN_SPEED_100;
 	}
-	priv->sa_int =     dev->mem_start & 0x02;
 	priv->debug =      dev->mem_end;
 
-
-	printk(KERN_INFO "TLAN %d.%d:  %s irq=%2d io=%04x, %s, Rev. %d\n",
-		TLanVersionMajor,
-		TLanVersionMinor,
+	printk(KERN_INFO "TLAN 1.3b: %s irq=%2d io=%04x, %s, Rev. %d\n",
 		dev->name, 
 		(int) irq, 
 		io_base,
@@ -691,7 +699,7 @@
 	dev->stop = &TLan_Close;
 	dev->get_stats = &TLan_GetStats;
 	dev->set_multicast_list = &TLan_SetMulticastList;
-
+	dev->do_ioctl = &TLan_ioctl;
 
 	return 0;
 
@@ -726,12 +734,8 @@
 	
 	MOD_INC_USE_COUNT;
 
-	if ( priv->sa_int ) {
-		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Using SA_INTERRUPT\n" ); 
-		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
-	} else {
-		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
-	}
+	err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+	
 	if ( err ) {
 		printk( "TLAN:  Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
 		return -EAGAIN;
@@ -754,6 +758,33 @@
 } /* TLan_Open */
 
 
+static int TLan_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+	u16 *data = (u16 *)&rq->ifr_data;
+	u32 phy = priv->phy[priv->phyNum];
+	
+	if (!priv->phyOnline)
+		return -EAGAIN;
+
+	switch(cmd) {
+		case SIOCDEVPRIVATE:
+			data[0] = phy;
+
+                case SIOCDEVPRIVATE+1: /* Read MII register */
+                        TLan_MiiReadReg(dev, data[0], data[1], &data[3]);
+                        return 0;
+
+                case SIOCDEVPRIVATE+2: /* Write MII register */
+                        if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			TLan_MiiWriteReg(dev, data[0], data[1], data[2]);
+			return 0;
+	       default:
+			return -EOPNOTSUPP;
+       }
+} /* tlan_ioctl */
+
 
 
 	/***************************************************************
@@ -783,6 +814,7 @@
 	TLanList	*tail_list;
 	u8		*tail_buffer;
 	int		pad;
+	unsigned long   flags;
 
 	if ( ! priv->phyOnline ) {
 		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s PHY is not ready\n", dev->name );
@@ -823,7 +855,7 @@
 		tail_list->buffer[1].address = 0;
 	}
 
-	cli();
+	spin_lock_irqsave(&priv->lock, flags);
 	tail_list->cStat = TLAN_CSTAT_READY;
 	if ( ! priv->txInProgress ) {
 		priv->txInProgress = 1;
@@ -839,7 +871,7 @@
 			( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
 		}
 	}
-	sti();
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 	CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
 
@@ -883,14 +915,11 @@
 	u32		host_cmd;
 	u16		host_int;
 	int		type;
+	TLanPrivateInfo *priv;
 
 	dev = (struct device *) dev_id;
-
-	cli();
-	if ( dev->interrupt ) {
-		printk( "TLAN:   Re-entering interrupt handler for %s: %ld.\n" , dev->name, dev->interrupt );
-	}
-	dev->interrupt++;
+	priv = (TLanPrivateInfo *) dev->priv;
+	spin_lock(&priv->lock);
 
 	host_int = inw( dev->base_addr + TLAN_HOST_INT );
 	outw( host_int, dev->base_addr + TLAN_HOST_INT );
@@ -905,7 +934,7 @@
 	}
 
 	dev->interrupt--;
-	sti();
+	spin_unlock(&priv->lock);
 
 } /* TLan_HandleInterrupts */
 
@@ -1092,7 +1121,7 @@
 u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
 {
 	host_int = 0;
-	/* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */
+	printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); 
 	return 0;
 
 } /* TLan_HandleInvalid */
@@ -1163,7 +1192,12 @@
 	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
 		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
 		if ( priv->timer.function == NULL ) {
-			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+			priv->timer.function = &TLan_Timer;
+			priv->timer.data = (unsigned long) dev;
+			priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+			priv->timerSetAt = jiffies;
+			priv->timerType = TLAN_TIMER_ACTIVITY;
+			add_timer(&priv->timer);
 		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
 			priv->timerSetAt = jiffies;
 		}
@@ -1319,7 +1353,12 @@
 	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
 		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
 		if ( priv->timer.function == NULL )  {
-			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+			priv->timer.function = &TLan_Timer;
+			priv->timer.data = (unsigned long) dev;
+			priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+			priv->timerSetAt = jiffies;
+			priv->timerType = TLAN_TIMER_ACTIVITY;
+			add_timer(&priv->timer);
 		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
 			priv->timerSetAt = jiffies;
 		}
@@ -1567,6 +1606,7 @@
 	struct device	*dev = (struct device *) data;
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
 	u32		elapsed;
+	unsigned long   flags = 0;
 
 	priv->timer.function = NULL;
 
@@ -1590,7 +1630,7 @@
 			TLan_FinishReset( dev );
 			break;
 		case TLAN_TIMER_ACTIVITY:
-			cli();
+			spin_lock_irqsave(&priv->lock, flags);
 			if ( priv->timer.function == NULL ) {
 				elapsed = jiffies - priv->timerSetAt;
 				if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
@@ -1598,11 +1638,11 @@
 				} else  {
 					priv->timer.function = &TLan_Timer;
 					priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
-					sti();
+					spin_unlock_irqrestore(&priv->lock, flags);
 					add_timer( &priv->timer );
 				}
 			}
-			sti();
+			spin_unlock_irqrestore(&priv->lock, flags);
 			break;
 		default:
 			break;
@@ -2224,6 +2264,7 @@
 	TLan_MiiSync( dev->base_addr );
 	value = MII_GC_LOOPBK;
 	TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+	TLan_MiiSync(dev->base_addr);  
 
 	/* Wait for 500 ms and reset the
 	 * tranceiver.  The TLAN docs say both 50 ms and
@@ -2252,7 +2293,6 @@
 	while ( value & MII_GC_RESET ) {
 		TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
 	}
-	TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
 
 	/* Wait for 500 ms and initialize.
 	 * I don't remember why I wait this long.
@@ -2275,39 +2315,46 @@
 	u16		tctl;
 
 	phy = priv->phy[priv->phyNum];
-
 	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Trying to activate link.\n", dev->name );
 	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
-	if ( ( status & MII_GS_AUTONEG ) && 
-	     ( priv->duplex == TLAN_DUPLEX_DEFAULT ) && 
-	     ( priv->speed == TLAN_SPEED_DEFAULT ) &&
-	     ( ! priv->aui ) ) {
+	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &ability );
+
+	if ( ( status & MII_GS_AUTONEG ) &&
+	     ( !priv->aui ) ) {
+
 		ability = status >> 11;
+		if ( priv->speed  == TLAN_SPEED_10 &&
+		     priv->duplex == TLAN_DUPLEX_HALF ) {
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x0000);
+		} else if ( priv->speed  == TLAN_SPEED_10 &&
+			    priv->duplex == TLAN_DUPLEX_FULL ) {
+			priv->tlanFullDuplex = TRUE;
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x0100);
+		} else if ( priv->speed  == TLAN_SPEED_100 &&
+			    priv->duplex == TLAN_DUPLEX_HALF ) {
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x2000);
+		} else if ( priv->speed  == TLAN_SPEED_100 &&
+			    priv->duplex == TLAN_DUPLEX_FULL ) {
+			priv->tlanFullDuplex = TRUE;
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x2100);
+		} else {
 
-		if ( priv->speed == TLAN_SPEED_10 ) {
-			ability &= 0x0003;
-		} else if ( priv->speed == TLAN_SPEED_100 ) {
-			ability &= 0x001C;
-		}
-
-		if ( priv->duplex == TLAN_DUPLEX_FULL ) {
-			ability &= 0x000A;
-		} else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
-			ability &= 0x0005;
-		}
-
-		TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
-       		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
-		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
-
-		/* Wait for 4 sec for autonegotiation
-		 * to complete.  The max spec time is less than this
-		 * but the card need additional time to start AN.
-		 * .5 sec should be plenty extra.
-		 */
-		printk(KERN_INFO "TLAN:  %s: Starting autonegotiation.\n", dev->name );
-		TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_PHY_FINISH_AN );
-		return;
+			/* Set Auto-Neg advertisement */
+			TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
+       			/* Enable Auto-Neg */
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+			/* Restart Auto-Neg */
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+
+			/* Wait for 4 sec for autonegotiation
+		 	* to complete.  The max spec time is less than this
+		 	* but the card need additional time to start AN.
+		 	* .5 sec should be plenty extra.
+		 	*/
+			printk(KERN_INFO "TLAN:  %s: Starting autonegotiation.\n", dev->name );
+			TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_PHY_FINISH_AN );
+			return;
+		}
 	}
 
 	if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
@@ -2338,7 +2385,7 @@
 	/* Wait for 1 sec to give the tranceiver time
 	 * to establish link.
 	 */
-	TLan_SetTimer( dev, HZ, TLAN_TIMER_FINISH_RESET );
+	TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_FINISH_RESET );
 
 } /* TLan_PhyStartLink */
 
@@ -2447,14 +2494,15 @@
 	u32	i;
 	int	err;
 	int	minten;
+	TLanPrivateInfo *priv;
+	unsigned long flags = 0;
 
 	err = FALSE;
 	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
 	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
-
-	if ( dev->interrupt == 0 )
-		cli();
-	dev->interrupt++;
+	
+	priv = (TLanPrivateInfo *) dev->priv;
+	spin_lock_irqsave(&priv->lock, flags);
 
 	TLan_MiiSync(dev->base_addr);
 
@@ -2501,9 +2549,7 @@
 
 	*val = tmp;
 
-	dev->interrupt--;
-	if ( dev->interrupt == 0 )
-		sti();
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return err;
 
@@ -2615,13 +2661,14 @@
 {
 	u16	sio;
 	int	minten;
+	unsigned long flags;
+	TLanPrivateInfo *priv; 
 
 	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
 	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
-
-	if ( dev->interrupt == 0 )
-		cli();
-	dev->interrupt++;
+	
+	priv = (TLanPrivateInfo *) dev->priv;
+	spin_lock_irqsave(&priv->lock, flags);
 
 	TLan_MiiSync( dev->base_addr );
 
@@ -2643,9 +2690,7 @@
 	if ( minten )
 		TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
 
-	dev->interrupt--;
-	if ( dev->interrupt == 0 )
-		sti();
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 } /* TLan_MiiWriteReg */
 
@@ -2843,29 +2888,38 @@
 int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
 {
 	int err;
+	TLanPrivateInfo *priv;
+	unsigned long flags = 0;
+	int ret = 0;
 
-	if ( dev->interrupt == 0 )
-		cli();
-	dev->interrupt++;
-
+	priv = (TLanPrivateInfo *) dev->priv;
+	spin_lock_irqsave(&priv->lock, flags);
+	
 	TLan_EeSendStart( dev->base_addr );
 	err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
 	if (err)
-		return 1;
+	{
+		ret=1;
+		goto fail;
+	}
 	err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
 	if (err)
-		return 2;
+	{
+		ret=2;
+		goto fail;
+	}
 	TLan_EeSendStart( dev->base_addr );
 	err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
 	if (err)
-		return 3;
+	{
+		ret=3;
+		goto fail;
+	}
 	TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
 
-	dev->interrupt--;
-	if ( dev->interrupt == 0 )
-		sti();
-
-	return 0;
+fail:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return ret;
 
 } /* TLan_EeReadByte */
 

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