patch-2.4.4 linux/drivers/net/sis900.c

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

diff -u --recursive --new-file v2.4.3/linux/drivers/net/sis900.c linux/drivers/net/sis900.c
@@ -1,6 +1,6 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:	1.07.08	Jan. 8 2001
+   Revision:	1.07.11	Apr. 10 2001
    
    Modified from the driver which is originally written by Donald Becker.
    
@@ -18,6 +18,8 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
 
+   Rev 1.07.11 Apr.  2 2001 Hui-Fen Hsu  updates PCI drivers to use the new pci_set_dma_mask for kernel 2.4.3
+   Rev 1.07.10 Mar.  1 2001 Hui-Fen Hsu <hfhsu@sis.com.tw> some bug fix & 635M/B support 
    Rev 1.07.09 Feb.  9 2001 Dave Jones <davej@suse.de> PCI enable cleanup
    Rev 1.07.08 Jan.  8 2001 Lei-Chun Chang added RTL8201 PHY support
    Rev 1.07.07 Nov. 29 2000 Lei-Chun Chang added kernel-doc extractable documentation and 630 workaround fix
@@ -60,10 +62,10 @@
 
 #include "sis900.h"
 
-static const char *version =
-"sis900.c: v1.07.09  2/9/2001\n";
+static char version[] __devinitdata =
+KERN_INFO "sis900.c: v1.07.11  4/10/2001\n";
 
-static int max_interrupt_work = 20;
+static int max_interrupt_work = 40;
 static int multicast_filter_limit = 128;
 
 #define sis900_debug debug
@@ -91,31 +93,33 @@
 };
 MODULE_DEVICE_TABLE (pci, sis900_pci_tbl);
 
-static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void rtl8201_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex);
 
 static struct mii_chip_info {
 	const char * name;
 	u16 phy_id0;
 	u16 phy_id1;
-	void (*read_mode) (struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+	u8  phy_types;
+#define	HOME 	0x0001
+#define LAN	0x0002
+#define MIX	0x0003
 } mii_chip_table[] = {
-	{"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
-	{"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
-	{"AMD 79C901 10BASE-T PHY",  0x0000, 0x35b9, amd79c901_read_mode},
-	{"AMD 79C901 HomePNA PHY",   0x0000, 0x35c8, amd79c901_read_mode},
-	{"ICS 1893 Integrated PHYceiver"   , 0x0015, 0xf441,ics1893_read_mode},
-	{"RTL 8201 10/100Mbps Phyceiver"   , 0x0000, 0x8201,rtl8201_read_mode},
+	{ "SiS 900 Internal MII PHY", 		0x001d, 0x8000, LAN },
+	{ "SiS 7014 Physical Layer Solution", 	0x0016, 0xf830, LAN },
+	{ "AMD 79C901 10BASE-T PHY",  		0x0000, 0x6B70, LAN },
+	{ "AMD 79C901 HomePNA PHY",		0x0000, 0x6B90, HOME},
+	{ "ICS LAN PHY",			0x0015, 0xF440, LAN },
+	{ "NS 83851 PHY",			0x2000, 0x5C20, MIX },
 	{0,},
 };
 
 struct mii_phy {
 	struct mii_phy * next;
-	struct mii_chip_info * chip_info;
 	int phy_addr;
+	u16 phy_id0;
+	u16 phy_id1;
 	u16 status;
+	u8  phy_types;
 };
 
 typedef struct _BufferDesc {
@@ -131,11 +135,13 @@
 	spinlock_t lock;
 
 	struct mii_phy * mii;
+	struct mii_phy * first_mii; /* record the first mii structure */
 	unsigned int cur_phy;
 
-	struct timer_list timer;			/* Link status detection timer. */
+	struct timer_list timer; /* Link status detection timer. */
+	u8     autong_complete; /* 1: auto-negotiate complete  */
 
-	unsigned int cur_rx, dirty_rx;		/* producer/comsumer pointers for Tx/Rx ring */
+	unsigned int cur_rx, dirty_rx; /* producer/comsumer pointers for Tx/Rx ring */
 	unsigned int cur_tx, dirty_tx;
 
 	/* The saved address of a sent/receive-in-place packet buffer */
@@ -171,11 +177,16 @@
 static int sis900_close(struct net_device *net_dev);
 static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd);
 static struct net_device_stats *sis900_get_stats(struct net_device *net_dev);
-static u16 sis900_compute_hashtable_index(u8 *addr);
+static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision);
 static void set_rx_mode(struct net_device *net_dev);
 static void sis900_reset(struct net_device *net_dev);
 static void sis630_set_eq(struct net_device *net_dev, u8 revision);
 static int sis900_set_config(struct net_device *dev, struct ifmap *map);
+static u16 sis900_default_phy(struct net_device * net_dev);
+static void sis900_set_capability( struct net_device *net_dev ,struct mii_phy *phy);
+static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr);
+static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr);
+static void sis900_set_mode (long ioaddr, int speed, int duplex);
 
 /**
  *	sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model
@@ -239,6 +250,44 @@
 	return 1;
 }
 
+
+/**
+ *	sis635_get_mac_addr: - Get MAC address for SIS635 model
+ *	@pci_dev: the sis900 pci device
+ *	@net_dev: the net device to get address for 
+ *
+ *	SiS635 model, set MAC Reload Bit to load Mac address from APC
+ *	to rfdr. rfdr is accessed through rfcr. MAC address is read into 
+ *	@net_dev->dev_addr.
+ */
+
+static int __devinit sis635_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
+{
+	long ioaddr = net_dev->base_addr;
+	u32 rfcrSave;
+	u32 i;
+
+	rfcrSave = inl(rfcr + ioaddr);
+
+	outl(rfcrSave | RELOAD, ioaddr + cr);
+	outl(0, ioaddr + cr);
+
+	/* disable packet filtering before setting filter */
+	outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+	/* load MAC addr to filter data register */
+	for (i = 0 ; i < 3 ; i++) {
+		outl((i << RFADDR_shift), ioaddr + rfcr);
+		*( ((u16 *)net_dev->dev_addr) + i) = inw(ioaddr + rfdr);
+	}
+
+	/* enable packet filitering */
+	outl(rfcrSave | RFEN, rfcr + ioaddr);
+
+	return 1;
+}
+
+
 /**
  *	sis900_probe: - Probe for sis900 device
  *	@pci_dev: the sis900 pci device
@@ -253,64 +302,48 @@
 static int __devinit sis900_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
 {
 	struct sis900_private *sis_priv;
-	long ioaddr;
 	struct net_device *net_dev;
-	int irq;
+	long ioaddr;
 	int i, ret;
 	u8 revision;
 	char *card_name = card_names[pci_id->driver_data];
 
-	/* setup various bits in PCI command register */
-	ret = pci_enable_device (pci_dev);
-	if (ret) return ret;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
 
+	/* setup various bits in PCI command register */
+	ret = pci_enable_device(pci_dev);
+	if(ret) return ret;
+	
 	i = pci_set_dma_mask(pci_dev, SIS900_DMA_MASK);
-	if (i) {
-		printk(KERN_ERR "sis900.c: architecture does not support "
-		       "32bit PCI busmaster DMA\n");
+	if(i){
+		printk(KERN_ERR "sis900.c: architecture does not support"
+			"32bit PCI busmaster DMA\n");
 		return i;
 	}
-
+	
 	pci_set_master(pci_dev);
-
-	irq = pci_dev->irq;
-	ioaddr = pci_resource_start(pci_dev, 0);
-
+	
 	net_dev = alloc_etherdev(sizeof(struct sis900_private));
 	if (!net_dev)
 		return -ENOMEM;
 	SET_MODULE_OWNER(net_dev);
 
+	/* We do a request_region() to register /proc/ioports info. */
+	ioaddr = pci_resource_start(pci_dev, 0);	
 	ret = pci_request_regions(pci_dev, "sis900");
 	if (ret)
 		goto err_out;
 
-	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
-	if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV)
-		ret = sis630e_get_mac_addr(pci_dev, net_dev);
-	else if (revision == SIS630S_900_REV)
-		ret = sis630e_get_mac_addr(pci_dev, net_dev);
-	else
-		ret = sis900_get_mac_addr(pci_dev, net_dev);
-
-	if (ret == 0) {
-		ret = -ENODEV;
-		goto err_out_region;
-	}
-
 	sis_priv = net_dev->priv;
-
-	/* We do a request_region() to register /proc/ioports info. */
 	net_dev->base_addr = ioaddr;
-	net_dev->irq = irq;
+	net_dev->irq = pci_dev->irq;
 	sis_priv->pci_dev = pci_dev;
 	spin_lock_init(&sis_priv->lock);
-	
-	/* probe for mii transciver */
-	if (sis900_mii_probe(net_dev) == 0) {
-		ret = -ENODEV;
-		goto err_out_region;
-	}
 
 	pci_set_drvdata(pci_dev, net_dev);
 
@@ -324,25 +357,47 @@
 	net_dev->do_ioctl = &mii_ioctl;
 	net_dev->tx_timeout = sis900_tx_timeout;
 	net_dev->watchdog_timeo = TX_TIMEOUT;
-
+	
 	ret = register_netdev(net_dev);
 	if (ret)
 		goto err_out_cleardev;
+		
+	/* Get Mac address according to the chip revision */
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+	ret = 0;
+
+	if (revision == SIS630E_900_REV)
+		ret = sis630e_get_mac_addr(pci_dev, net_dev);
+	else if ((revision > 0x81) && (revision <= 0x90) )
+		ret = sis635_get_mac_addr(pci_dev, net_dev);
+	else
+		ret = sis900_get_mac_addr(pci_dev, net_dev);
+
+	if (ret == 0) {
+		ret = -ENODEV;
+		goto err_out_region;
+	}
+
+	/* probe for mii transciver */
+	if (sis900_mii_probe(net_dev) == 0) {
+		ret = -ENODEV;
+		goto err_out_region;
+	}
 
 	/* print some information about our NIC */
 	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name,
-	       card_name, ioaddr, irq);
+	       card_name, ioaddr, net_dev->irq);
 	for (i = 0; i < 5; i++)
 		printk("%2.2x:", (u8)net_dev->dev_addr[i]);
 	printk("%2.2x.\n", net_dev->dev_addr[i]);
 
 	return 0;
 
-err_out_cleardev:
-	pci_set_drvdata(pci_dev, NULL);
-err_out_region:
+ err_out_cleardev:
+ 	pci_set_drvdata(pci_dev, NULL);
+ err_out_region:
 	pci_release_regions(pci_dev);
-err_out:
+ err_out:
 	kfree(net_dev);
 	return ret;
 }
@@ -350,7 +405,7 @@
 /**
  *	sis900_mii_probe: - Probe MII PHY for sis900
  *	@net_dev: the net device to probe for
- *
+ *	
  *	Search for total of 32 possible mii phy addresses.
  *	Identify and set current phy if found one,
  *	return error if it failed to found.
@@ -359,59 +414,83 @@
 static int __init sis900_mii_probe (struct net_device * net_dev)
 {
 	struct sis900_private * sis_priv = net_dev->priv;
+	u16 poll_bit = MII_STAT_LINK, status = 0;
+	unsigned int timeout = jiffies + 5 * HZ;
 	int phy_addr;
 	u8 revision;
 
 	sis_priv->mii = NULL;
 
 	/* search for total of 32 possible mii phy addresses */
-	for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+	for (phy_addr = 0; phy_addr < 32; phy_addr++) {	
+		struct mii_phy * mii_phy = NULL;
 		u16 mii_status;
-		u16 phy_id0, phy_id1;
 		int i;
 
-		mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+		mii_phy = NULL;
+		for(i = 0; i < 2; i++)
+			mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
 		if (mii_status == 0xffff || mii_status == 0x0000)
 			/* the mii is not accessable, try next one */
 			continue;
+		
+		if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) == NULL) {
+			printk(KERN_INFO "Cannot allocate mem for struct mii_phy\n");
+			return 0;
+		}
+		
+		mii_phy->phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
+		mii_phy->phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);		
+		mii_phy->phy_addr = phy_addr;
+		mii_phy->status = mii_status;
+		mii_phy->next = sis_priv->mii;
+		sis_priv->mii = mii_phy;
+		sis_priv->first_mii = mii_phy;
 
-		phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
-		phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);
-
-		/* search our mii table for the current mii */ 
 		for (i = 0; mii_chip_table[i].phy_id1; i++)
-			if (phy_id0 == mii_chip_table[i].phy_id0 &&
-			    phy_id1 == mii_chip_table[i].phy_id1) {
-				struct mii_phy * mii_phy;
-
-				printk(KERN_INFO
-				       "%s: %s transceiver found at address %d.\n",
-				       net_dev->name, mii_chip_table[i].name,
-				       phy_addr);
-				if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
-					mii_phy->chip_info = mii_chip_table+i;
-					mii_phy->phy_addr = phy_addr;
-					mii_phy->status = mdio_read(net_dev, phy_addr,
-								    MII_STATUS);
-					mii_phy->next = sis_priv->mii;
-					sis_priv->mii = mii_phy;
-				}
-				/* the current mii is on our mii_info_table,
-				   try next address */
+			if ((mii_phy->phy_id0 == mii_chip_table[i].phy_id0 ) &&
+			    ((mii_phy->phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){
+				mii_phy->phy_types = mii_chip_table[i].phy_types;
+				if (mii_chip_table[i].phy_types == MIX)
+					mii_phy->phy_types =
+						(mii_status & (MII_STAT_CAN_TX_FDX | MII_STAT_CAN_TX)) ? LAN : HOME;
+				printk(KERN_INFO "%s: %s transceiver found at address %d.\n",
+				       net_dev->name, mii_chip_table[i].name, phy_addr);
 				break;
 			}
+			
+		if( !mii_chip_table[i].phy_id1 )
+			printk(KERN_INFO "%s: Unknown PHY transceiver found at address %d.\n",
+			       net_dev->name, phy_addr);			
 	}
-
+	
 	if (sis_priv->mii == NULL) {
 		printk(KERN_INFO "%s: No MII transceivers found!\n",
 		       net_dev->name);
 		return 0;
 	}
 
-	/* arbitrary choose that last PHY as current PHY */
-	sis_priv->cur_phy = sis_priv->mii->phy_addr;
-	printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
-	       sis_priv->mii->chip_info->name);
+	/* select default PHY for mac */
+	sis_priv->mii = NULL;
+	sis900_default_phy( net_dev );
+
+	/* Reset phy if default phy is internal sis900 */
+        if ((sis_priv->mii->phy_id0 == 0x001D) &&
+	    ((sis_priv->mii->phy_id1&0xFFF0) == 0x8000))
+        	status = sis900_reset_phy(net_dev, sis_priv->cur_phy);
+
+	if(status & MII_STAT_LINK){
+		while (poll_bit) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(0);
+			poll_bit ^= (mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS) & poll_bit);
+			if (jiffies >= timeout) {
+				printk(KERN_WARNING "%s: reset phy and link down now\n", net_dev->name);
+				return -ETIME;
+			}
+		}
+	}
 
 	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
 	if (revision == SIS630E_900_REV) {
@@ -420,7 +499,7 @@
 		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG1, 0x22);
 		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG2, 0xff00);
 		mdio_write(net_dev, sis_priv->cur_phy, MII_MASK, 0xffc0);
-		mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	
+		//mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	
 	}
 
 	if (sis_priv->mii->status & MII_STAT_LINK)
@@ -431,6 +510,86 @@
 	return 1;
 }
 
+/**
+ *	sis900_default_phy: - Select default PHY for sis900 mac.
+ *	@net_dev: the net device to probe for
+ *
+ *	Select first detected PHY with link as default.
+ *	If no one is link on, select PHY whose types is HOME as default.
+ *	If HOME doesn't exist, select LAN.
+ */
+
+static u16 sis900_default_phy(struct net_device * net_dev)
+{
+	struct sis900_private * sis_priv = net_dev->priv;
+ 	struct mii_phy *phy = NULL, *phy_home = NULL, *default_phy = NULL;
+	u16 status;
+
+        for( phy=sis_priv->first_mii; phy; phy=phy->next ){
+		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+
+		/* Link ON & Not select deafalut PHY */
+		 if ( (status & MII_STAT_LINK) && !(default_phy) )
+		 	default_phy = phy;
+		 else{
+			status = mdio_read(net_dev, phy->phy_addr, MII_CONTROL);
+			mdio_write(net_dev, phy->phy_addr, MII_CONTROL,
+				status | MII_CNTL_AUTO | MII_CNTL_ISOLATE);
+			if( phy->phy_types == HOME )
+				phy_home = phy;
+		 }
+	}
+
+	if( (!default_phy) && phy_home )
+		default_phy = phy_home;
+	else if(!default_phy)
+		default_phy = sis_priv->first_mii;
+
+	if( sis_priv->mii != default_phy ){
+		sis_priv->mii = default_phy;
+		sis_priv->cur_phy = default_phy->phy_addr;
+		printk(KERN_INFO "%s: Using transceiver found at address %d as default\n", net_dev->name,sis_priv->cur_phy);
+	}
+	
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
+	status &= (~MII_CNTL_ISOLATE);
+
+	mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, status);	
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+
+	return status;	
+}
+
+
+/**
+ * 	sis900_set_capability: - set the media capability of network adapter.
+ *	@net_dev : the net device to probe for
+ *	@mii_phy : default PHY
+ *
+ *	Set the media capability of network adapter according to
+ *	mii status register. It's necessary before auto-negotiate.
+ */
+ 
+static void sis900_set_capability( struct net_device *net_dev , struct mii_phy *phy )
+{
+	u16 cap;
+	u16 status;
+	
+	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+	
+	cap = MII_NWAY_CSMA_CD |
+		((phy->status & MII_STAT_CAN_TX_FDX)? MII_NWAY_TX_FDX:0) |
+		((phy->status & MII_STAT_CAN_TX)    ? MII_NWAY_TX:0) |
+		((phy->status & MII_STAT_CAN_T_FDX) ? MII_NWAY_T_FDX:0)|
+		((phy->status & MII_STAT_CAN_T)     ? MII_NWAY_T:0);
+
+	mdio_write(net_dev, phy->phy_addr, MII_ANADV, cap);
+}
+
+
 /* Delay between EEPROM clock transitions. */
 #define eeprom_delay()  inl(ee_addr)
 
@@ -605,6 +764,30 @@
 	return;
 }
 
+
+/**
+ *	sis900_reset_phy: - reset sis900 mii phy.
+ *	@net_dev: the net device to write
+ *	@phy_addr: default phy address
+ *
+ *	Some specific phy can't work properly without reset.
+ *	This function will be called during initialization and
+ *	link status change from ON to DOWN.
+ */
+
+static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr)
+{
+	int i = 0;
+	u16 status;
+
+	while (i++ < 2)
+		status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
+	mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET );
+	
+	return status;
+}
+
 /**
  *	sis900_open: - open sis900 device
  *	@net_dev: the net device to open
@@ -626,9 +809,7 @@
 
 	/* Equalizer workaround Rule */
 	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-	if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-	    revision == SIS630A_900_REV)
-		sis630_set_eq(net_dev,revision);
+	sis630_set_eq(net_dev, revision);
 
 	ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev);
 	if (ret)
@@ -679,7 +860,7 @@
 	rfcrSave = inl(rfcr + ioaddr);
 
 	/* disable packet filtering before setting filter */
-	outl(rfcrSave & ~RFEN, rfcr);
+	outl(rfcrSave & ~RFEN, rfcr + ioaddr);
 
 	/* load MAC addr to filter data register */
 	for (i = 0 ; i < 3 ; i++) {
@@ -820,7 +1001,11 @@
 	int i, maxcount=10;
 	struct pci_dev *dev=NULL;
 
-	if ((dev = pci_find_device(SIS630_DEVICE_ID, SIS630_VENDOR_ID, dev)))
+	if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
+	       revision == SIS630A_900_REV) )
+		return;
+
+	if ((dev = pci_find_device(SIS630_VENDOR_ID, SIS630_DEVICE_ID, dev)))
 		pci_read_config_byte(dev, PCI_CLASS_REVISION, &host_bridge_rev);
 
 	if (netif_carrier_ok(net_dev)) {
@@ -842,13 +1027,6 @@
 			else if (max_value >= 15)
 				eq_value=(max_value == min_value) ? max_value+6 : max_value+5;
 		}
-		/* 630A0 rule to determine the equalizer value */
-		if (revision == SIS630A_900_REV && host_bridge_rev == SIS630A0) {
-			if (max_value < 5)
-				eq_value=max_value+3;
-			else if (max_value >= 5)
-				eq_value=max_value+5;
-		}
 		/* 630B0&B1 rule to determine the equalizer value */
 		if (revision == SIS630A_900_REV && 
 		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) {
@@ -865,7 +1043,11 @@
 	}
 	else {
 		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);
-		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);
+		if (revision == SIS630A_900_REV && 
+		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) 
+			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2200) & 0xBFFF);
+		else
+			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);
 	}
 	return;
 }
@@ -887,60 +1069,52 @@
 	u16 status;
 	u8 revision;
 
-	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
-	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
-
-	/* current mii phy is failed to link, try another one */
-	while (!(status & MII_STAT_LINK)) {
-		if (mii_phy->next == NULL) {
-			if (netif_carrier_ok(net_dev)) {
-				/* link stat change from ON to OFF */
-				next_tick = HZ;
-				netif_carrier_off(net_dev);
-
-				/* Equalizer workaround Rule */
-				pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-				if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-				    revision == SIS630A_900_REV)
-					sis630_set_eq(net_dev, revision);
+	if (!sis_priv->autong_complete){
+		int speed, duplex = 0;
 
-				printk(KERN_INFO "%s: Media Link Off\n",
-				       net_dev->name);
-			}
-			sis_priv->timer.expires = jiffies + next_tick;
-			add_timer(&sis_priv->timer);
-			return;
+		sis900_read_mode(net_dev, &speed, &duplex);
+		if (duplex){
+			sis900_set_mode(net_dev->base_addr, speed, duplex);
+			pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+			sis630_set_eq(net_dev, revision);
 		}
-		mii_phy = mii_phy->next;
-		status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);
-	}
 
-	if (!netif_carrier_ok(net_dev)) {
-		/* link stat change forn OFF to ON, read and report link mode */
-		netif_carrier_on(net_dev);
-		next_tick = 5*HZ;
+		sis_priv->timer.expires = jiffies + HZ;
+		add_timer(&sis_priv->timer);
+		return;
+	}
 
-		/* Equalizer workaround Rule */
-		pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-		if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-		    revision == SIS630A_900_REV)
-			sis630_set_eq(net_dev, revision);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
 
-		/* change what cur_phy means */
-		if (mii_phy->phy_addr != sis_priv->cur_phy) {
-			printk(KERN_INFO "%s: Changing transceiver to %s\n",
-			       net_dev->name, mii_phy->chip_info->name);
-			/* disable previous PHY */
-			status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
-			mdio_write(net_dev, sis_priv->cur_phy,
-				   MII_CONTROL, status | MII_CNTL_ISOLATE);
-			/* enable next PHY */
-			status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
-			mdio_write(net_dev, mii_phy->phy_addr,
-				   MII_CONTROL, status & ~MII_CNTL_ISOLATE);
-			sis_priv->cur_phy = mii_phy->phy_addr;
+	/* Link OFF -> ON */
+	if (!netif_carrier_ok(net_dev)) {
+	LookForLink:
+		/* Search for new PHY */
+		status = sis900_default_phy(net_dev);
+		mii_phy = sis_priv->mii;
+
+		if (status & MII_STAT_LINK){
+			sis900_check_mode(net_dev, mii_phy);
+			netif_carrier_on(net_dev);
 		}
-		sis900_check_mode(net_dev, mii_phy);
+	}
+	/* Link ON -> OFF */
+	else {
+                if (!(status & MII_STAT_LINK)){
+                	netif_carrier_off(net_dev);
+                	printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+
+                	/* Change mode issue */
+                	if ((mii_phy->phy_id0 == 0x001D) && 
+			    ((mii_phy->phy_id1 & 0xFFF0) == 0x8000))
+               			sis900_reset_phy(net_dev,  sis_priv->cur_phy);
+  
+                	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+			sis630_set_eq(net_dev, revision);
+  
+                	goto LookForLink;
+                }
 	}
 
 	sis_priv->timer.expires = jiffies + next_tick;
@@ -952,8 +1126,11 @@
  *	@net_dev: the net device to be checked
  *	@mii_phy: the mii phy
  *
- *	call mii_phy->chip_info->read_mode function
- *	to check the speed and duplex mode for sis900
+ *	Older driver gets the media mode from mii status output
+ *	register. Now we set our media capability and auto-negotiate
+ *	to get the upper bound of speed and duplex between two ends.
+ *	If the types of mii phy is HOME, it doesn't need to auto-negotiate
+ *	and autong_complete should be set to 1.
  */
 
 static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy)
@@ -961,12 +1138,44 @@
 	struct sis900_private *sis_priv = net_dev->priv;
 	long ioaddr = net_dev->base_addr;
 	int speed, duplex;
-	u32 tx_flags = 0, rx_flags = 0;
 
-	mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex);
+	if( mii_phy->phy_types == LAN  ){
+		outl( ~EXD & inl( ioaddr + cfg ), ioaddr + cfg);
+		sis900_set_capability(net_dev , mii_phy);
+		sis900_auto_negotiate(net_dev, sis_priv->cur_phy);
+	}else{
+		outl(EXD | inl( ioaddr + cfg ), ioaddr + cfg);
+		speed = HW_SPEED_HOME;
+		duplex = FDX_CAPABLE_HALF_SELECTED;
+		sis900_set_mode(ioaddr, speed, duplex);
+		sis_priv->autong_complete = 1;
+	}
+}
 
-	tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
-	rx_flags = RX_DMA_BURST << RxMXDMA_shift;
+/**
+ *	sis900_set_mode: - Set the media mode of mac register.
+ *	@speed : the transmit speed to be determined
+ *	@duplex: the duplex mode to be determined
+ *
+ *	Set the media mode of mac register txcfg/rxcfg according to
+ *	speed and duplex of phy. Bit EDB_MASTER_EN indicates the EDB
+ *	bus is used instead of PCI bus. When this bit is set 1, the
+ *	Max DMA Burst Size for TX/RX DMA should be no larger than 16
+ *	double words.
+ */
+
+static void sis900_set_mode (long ioaddr, int speed, int duplex)
+{
+	u32 tx_flags = 0, rx_flags = 0;
+
+	if( inl(ioaddr + cfg) & EDB_MASTER_EN ){
+		tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+		rx_flags = DMA_BURST_64 << RxMXDMA_shift;
+	}
+	else{
+		tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+		rx_flags = DMA_BURST_512 << RxMXDMA_shift;
+	}
 
 	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {
 		rx_flags |= (RxDRNT_10 << RxDRNT_shift);
@@ -987,184 +1196,86 @@
 }
 
 /**
- *	sis900_read_mode: - read media mode for sis900 internal phy
+ *	sis900_auto_negotiate:  Set the Auto-Negotiation Enable/Reset bit.
  *	@net_dev: the net device to read mode for
  *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
  *
- *	read MII_STSOUT register from sis900 internal phy
- *	to determine the speed and duplex mode for sis900
+ *	If the adapter is link-on, set the auto-negotiate enable/reset bit.
+ *	autong_complete should be set to 0 when starting auto-negotiation.
+ *	autong_complete should be set to 1 if we didn't start auto-negotiation.
+ *	sis900_timer will wait for link on again if autong_complete = 0.
  */
 
-static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
+static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr)
 {
+	struct sis900_private *sis_priv = net_dev->priv;
 	int i = 0;
 	u32 status;
-
-	/* STSOUT register is Latched on Transition, read operation updates it */
+	
 	while (i++ < 2)
-		status = mdio_read(net_dev, phy_addr, MII_STSOUT);
-
-	if (status & MII_STSOUT_SPD)
-		*speed = HW_SPEED_100_MBPS;
-	else
-		*speed = HW_SPEED_10_MBPS;
-
-	if (status & MII_STSOUT_DPLX)
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	else
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
+		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
-	if (status & MII_STSOUT_LINK_FAIL)
+	if (!(status & MII_STAT_LINK)){
 		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	else
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
+		sis_priv->autong_complete = 1;
+		netif_carrier_off(net_dev);
+		return;
+	}
+
+	/* (Re)start AutoNegotiate */
+	mdio_write(net_dev, phy_addr, MII_CONTROL,
+		   MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
+	sis_priv->autong_complete = 0;
 }
 
+
 /**
- *	amd79c901_read_mode: - read media mode for amd79c901 phy
+ *	sis900_read_mode: - read media mode for sis900 internal phy
  *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
+ *	@speed  : the transmit speed to be determined
+ *	@duplex : the duplex mode to be determined
  *
- *	read MII_STATUS register from amd79c901 phy
- *	to determine the speed and duplex mode for sis900
+ *	The capability of remote end will be put in mii register autorec
+ *	after auto-negotiation. Use AND operation to get the upper bound
+ *	of speed and duplex between two ends.
  */
 
-static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
+static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex)
 {
-	int i;
-	u16 status;
+	struct sis900_private *sis_priv = net_dev->priv;
+	int phy_addr = sis_priv->cur_phy;
+	u32 status;
+	u16 autoadv, autorec;
+	int i = 0;
 
-	for (i = 0; i < 2; i++)
+	while (i++ < 2)
 		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
-	if (status & MII_STAT_CAN_AUTO) {
-		/* 10BASE-T PHY */
-		for (i = 0; i < 2; i++)
-			status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY);
-		if (status & MII_STSSUM_SPD)
-			*speed = HW_SPEED_100_MBPS;
-		else
-			*speed = HW_SPEED_10_MBPS;
-		if (status & MII_STSSUM_DPLX)
-			*duplex = FDX_CAPABLE_FULL_SELECTED;
-		else
-			*duplex = FDX_CAPABLE_HALF_SELECTED;
-
-		if (status & MII_STSSUM_LINK)
-			printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-			       net_dev->name,
-			       *speed == HW_SPEED_100_MBPS ?
-			       "100mbps" : "10mbps",
-			       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-			       "full" : "half");
-		else
-			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	}
-	else {
-		/* HomePNA */
-		*speed = HW_SPEED_HOME;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-		if (status & MII_STAT_LINK)
-			printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n",
-			       net_dev->name);
-		else
-			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	}
-}
-
-/**
- *	ics1893_read_mode: - read media mode for ICS1893 PHY
- *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
- *
- *	ICS1893 PHY use Quick Poll Detailed Status register
- *	to determine the speed and duplex mode for sis900
- */
-
-static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
-{
-	int i = 0;
-	u32 status;
+	if (!(status & MII_STAT_LINK))
+		return;
 
-	/* MII_QPDSTS is Latched, read twice in succession will reflect the current state */
-	for (i = 0; i < 2; i++)
-		status = mdio_read(net_dev, phy_addr, MII_QPDSTS);
+	/* AutoNegotiate completed */
+	autoadv = mdio_read(net_dev, phy_addr, MII_ANADV);
+	autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR);
+	status = autoadv & autorec;
 
-	if (status & MII_STSICS_SPD)
+	if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))
 		*speed = HW_SPEED_100_MBPS;
 	else
 		*speed = HW_SPEED_10_MBPS;
-
-	if (status & MII_STSICS_DPLX)
+	if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))
 		*duplex = FDX_CAPABLE_FULL_SELECTED;
 	else
 		*duplex = FDX_CAPABLE_HALF_SELECTED;
 
-	if (status & MII_STSICS_LINKSTS)
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
-	else
-		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-}
-
-/**
- *	rtl8201_read_mode: - read media mode for rtl8201 phy
- *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
- *
- *	read MII_STATUS register from rtl8201 phy
- *	to determine the speed and duplex mode for sis900
- */
-
-static void rtl8201_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
-{
-	u32 status;
-
-	status = mdio_read(net_dev, phy_addr, MII_STATUS);
-
-	if (status & MII_STAT_CAN_TX_FDX) {
-		*speed = HW_SPEED_100_MBPS;
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_TX) {
-		*speed = HW_SPEED_100_MBPS;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_T_FDX) {
-		*speed = HW_SPEED_10_MBPS;
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_T) {
-		*speed = HW_SPEED_10_MBPS;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-	}
-
-	if (status & MII_STAT_LINK)
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
-	else
-		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+	sis_priv->autong_complete = 1;
+	
+	printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+	       net_dev->name,
+	       *speed == HW_SPEED_100_MBPS ?
+	       "100mbps" : "10mbps",
+	       *duplex == FDX_CAPABLE_FULL_SELECTED ?
+	       "full" : "half");
 }
 
 /**
@@ -1628,83 +1739,81 @@
         
 	u16 status;
 
-	/* 
-	   )*/    
 	if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
-        /* we switch on the ifmap->port field. I couldn't find anything
-           like a definition or standard for the values of that field.
-           I think the meaning of those values is device specific. But
-           since I would like to change the media type via the ifconfig
-           command I use the definition from linux/netdevice.h 
-           (which seems to be different from the ifport(pcmcia) definition) 
-        */
+		/* we switch on the ifmap->port field. I couldn't find anything
+		   like a definition or standard for the values of that field.
+		   I think the meaning of those values is device specific. But
+		   since I would like to change the media type via the ifconfig
+		   command I use the definition from linux/netdevice.h 
+		   (which seems to be different from the ifport(pcmcia) definition) 
+		*/
 		switch(map->port){
-			case IF_PORT_UNKNOWN: /* use auto here */   
-                		dev->if_port = map->port;
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+		case IF_PORT_UNKNOWN: /* use auto here */   
+			dev->if_port = map->port;
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
                 
-                		/* read current state */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			/* read current state */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
                 
-                		/* enable auto negotiation and reset the negotioation
-                		(I dont really know what the auto negatiotiation reset
-                		really means, but it sounds for me right to do one here)*/
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, status | MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
+			/* enable auto negotiation and reset the negotioation
+			   (I dont really know what the auto negatiotiation reset
+			   really means, but it sounds for me right to do one here)*/
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, status | MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
 
-            			break;
+			break;
             
-            		case IF_PORT_10BASET: /* 10BaseT */         
-                		dev->if_port = map->port;
+		case IF_PORT_10BASET: /* 10BaseT */         
+			dev->if_port = map->port;
                 
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
         
-                		/* set Speed to 10Mbps */
-                		/* read current state */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			/* set Speed to 10Mbps */
+			/* read current state */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
                 
-                		/* disable auto negotiation and force 10MBit mode*/
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, status & ~(MII_CNTL_SPEED | MII_CNTL_AUTO));
-            			break;
+			/* disable auto negotiation and force 10MBit mode*/
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, status & ~(MII_CNTL_SPEED | MII_CNTL_AUTO));
+			break;
             
-            		case IF_PORT_100BASET: /* 100BaseT */
-            		case IF_PORT_100BASETX: /* 100BaseTx */ 
-                		dev->if_port = map->port;
+		case IF_PORT_100BASET: /* 100BaseT */
+		case IF_PORT_100BASETX: /* 100BaseTx */ 
+			dev->if_port = map->port;
                 
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
                 
-                		/* set Speed to 100Mbps */
-                		/* disable auto negotiation and enable 100MBit Mode */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, (status & ~MII_CNTL_SPEED) | MII_CNTL_SPEED);
+			/* set Speed to 100Mbps */
+			/* disable auto negotiation and enable 100MBit Mode */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, (status & ~MII_CNTL_SPEED) | MII_CNTL_SPEED);
                 
-            			break;
+			break;
             
-            		case IF_PORT_10BASE2: /* 10Base2 */
-            		case IF_PORT_AUI: /* AUI */
-            		case IF_PORT_100BASEFX: /* 100BaseFx */
+		case IF_PORT_10BASE2: /* 10Base2 */
+		case IF_PORT_AUI: /* AUI */
+		case IF_PORT_100BASEFX: /* 100BaseFx */
                 	/* These Modes are not supported (are they?)*/
-                		printk(KERN_INFO "Not supported");
-                		return -EOPNOTSUPP;
-            			break;
+			printk(KERN_INFO "Not supported");
+			return -EOPNOTSUPP;
+			break;
             
-            		default:
-                		printk(KERN_INFO "Invalid");
-                		return -EINVAL;
+		default:
+			printk(KERN_INFO "Invalid");
+			return -EINVAL;
 		}
 	}
 	return 0;
@@ -1713,12 +1822,15 @@
 /**
  *	sis900_compute_hashtable_index: - compute hashtable index 
  *	@addr: multicast address
+ *	@revision: revision id of chip
  *
  *	SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast
  *	hash table, which makes this function a little bit different from other drivers
+ *	SiS 900 B0 & 635 M/B uses the most significat 8 bits to index 256 bits
+ *   	multicast hash table. 
  */
 
-static u16 sis900_compute_hashtable_index(u8 *addr)
+static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision)
 {
 
 /* what is the correct value of the POLYNOMIAL ??
@@ -1741,51 +1853,63 @@
 			byte >>= 1;
 		}
 	}
-	/* leave 7 most siginifant bits */ 
-	return ((int)(crc >> 25));
+
+	/* leave 8 or 7 most siginifant bits */
+	if ((revision == SIS635A_900_REV) || (revision == SIS900B_900_REV))
+		return ((int)(crc >> 24));
+	else
+		return ((int)(crc >> 25));
 }
 
 /**
  *	set_rx_mode: - Set SiS900 receive mode 
  *	@net_dev: the net device to be set
  *
- *	Set SiS900 receive mode for promiscuous, multicast, or broadcast mode. 
+ *	Set SiS900 receive mode for promiscuous, multicast, or broadcast mode.
  *	And set the appropriate multicast filter.
+ *	Multicast hash table changes from 128 to 256 bits for 635M/B & 900B0.
  */
 
 static void set_rx_mode(struct net_device *net_dev)
 {
 	long ioaddr = net_dev->base_addr;
-	u16 mc_filter[8];			/* 128 bits multicast hash table */
-	int i;
+	struct sis900_private * sis_priv = net_dev->priv;
+	u16 mc_filter[16] = {0};	/* 256/128 bits multicast hash table */
+	int i, table_entries;
 	u32 rx_mode;
+	u8 revision;
+
+	/* 635 Hash Table entires = 256(2^16) */
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if((revision == SIS635A_900_REV) || (revision == SIS900B_900_REV))
+		table_entries = 16;
+	else
+		table_entries = 8;
 
 	if (net_dev->flags & IFF_PROMISC) {
 		/* Accept any kinds of packets */
 		rx_mode = RFPromiscuous;
-		for (i = 0; i < 8; i++)
+		for (i = 0; i < table_entries; i++)
 			mc_filter[i] = 0xffff;
 	} else if ((net_dev->mc_count > multicast_filter_limit) ||
 		   (net_dev->flags & IFF_ALLMULTI)) {
 		/* too many multicast addresses or accept all multicast packet */
 		rx_mode = RFAAB | RFAAM;
-		for (i = 0; i < 8; i++)
+		for (i = 0; i < table_entries; i++)
 			mc_filter[i] = 0xffff;
 	} else {
 		/* Accept Broadcast packet, destination address matchs our MAC address,
 		   use Receive Filter to reject unwanted MCAST packet */
 		struct dev_mc_list *mclist;
 		rx_mode = RFAAB;
-		for (i = 0; i < 8; i++)
-			mc_filter[i]=0;
 		for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
 		     i++, mclist = mclist->next)
-			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr, revision),
 				mc_filter);
 	}
 
 	/* update Multicast Hash Table in Receive Filter */
-	for (i = 0; i < 8; i++) {
+	for (i = 0; i < table_entries; i++) {
                 /* why plus 0x04 ??, That makes the correct value for hash table. */
 		outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
 		outl(mc_filter[i], ioaddr + rfdr);
@@ -1804,7 +1928,7 @@
 		outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg);
 		/* restore cr */
 		outl(cr_saved, ioaddr + cr);
-	}		
+	}
 
 	return;
 }
@@ -1815,13 +1939,16 @@
  *
  *	reset sis900 MAC and wait until finished
  *	reset through command register
+ *	change backoff algorithm for 900B0 & 635 M/B
  */
 
 static void sis900_reset(struct net_device *net_dev)
 {
+	struct sis900_private * sis_priv = net_dev->priv;
 	long ioaddr = net_dev->base_addr;
 	int i = 0;
 	u32 status = TxRCMP | RxRCMP;
+	u8  revision;
 
 	outl(0, ioaddr + ier);
 	outl(0, ioaddr + imr);
@@ -1834,7 +1961,11 @@
 		status ^= (inl(isr + ioaddr) & status);
 	}
 
-	outl(PESEL, ioaddr + cfg);
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if( (revision == SIS635A_900_REV) || (revision == SIS900B_900_REV) )
+		outl(PESEL | RND_CNT, ioaddr + cfg);
+	else
+		outl(PESEL, ioaddr + cfg);
 }
 
 /**
@@ -1847,7 +1978,15 @@
 static void __devexit sis900_remove(struct pci_dev *pci_dev)
 {
 	struct net_device *net_dev = pci_get_drvdata(pci_dev);
-		
+	struct sis900_private * sis_priv = net_dev->priv;
+	struct mii_phy *phy = NULL;
+
+	while (sis_priv->first_mii) {
+		phy = sis_priv->first_mii;
+		sis_priv->first_mii = phy->next;
+		kfree(phy);
+	}
+
 	unregister_netdev(net_dev);
 	kfree(net_dev);
 	pci_release_regions(pci_dev);
@@ -1865,7 +2004,10 @@
 
 static int __init sis900_init_module(void)
 {
-	printk(KERN_INFO "%s", version);
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 
 	return pci_module_init(&sis900_pci_driver);
 }

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