patch-2.4.0-test6 linux/drivers/net/sis900.c
Next file: linux/drivers/net/sis900.h
Previous file: linux/drivers/net/pppoe.c
Back to the patch index
Back to the overall index
- Lines: 609
- Date:
Wed Aug 9 14:24:09 2000
- Orig file:
v2.4.0-test5/linux/drivers/net/sis900.c
- Orig date:
Fri Jul 14 12:12:11 2000
diff -u --recursive --new-file v2.4.0-test5/linux/drivers/net/sis900.c linux/drivers/net/sis900.c
@@ -1,8 +1,8 @@
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
Copyright 1999 Silicon Integrated System Corporation
- Revision: 1.07 Mar. 07 2000
-
- Modified from the driver which is originally written by Donald Becker.
+ Revision: 1.07.01 Aug. 08 2000
+
+ Modified from the driver which is originally written by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License (GPL), incorporated herein by reference.
@@ -18,13 +18,14 @@
preliminary Rev. 1.0 Jan. 18, 1998
http://www.sis.com.tw/support/databook.htm
+ Rev 1.07.01 Aug. 08 2000 Ollie Lho minor update fro SiS 630E and SiS 630E A1
Rev 1.07 Mar. 07 2000 Ollie Lho bug fix in Rx buffer ring
Rev 1.06.04 Feb. 11 2000 Jeff Garzik <jgarzik@mandrakesoft.com> softnet and init for kernel 2.4
Rev 1.06.03 Dec. 23 1999 Ollie Lho Third release
- Rev 1.06.02 Nov. 23 1999 Ollie Lho bug in mac probing fixed
+ Rev 1.06.02 Nov. 23 1999 Ollie Lho bug in mac probing fixed
Rev 1.06.01 Nov. 16 1999 Ollie Lho CRC calculation provide by Joseph Zbiciak (im14u2c@primenet.com)
Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
- Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx
+ Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx
Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release
*/
@@ -53,7 +54,7 @@
#include "sis900.h"
static const char *version =
-"sis900.c: v1.07 03/07/2000\n";
+"sis900.c: v1.07.01 08/08/2000\n";
static int max_interrupt_work = 20;
static int multicast_filter_limit = 128;
@@ -109,23 +110,23 @@
};
typedef struct _BufferDesc {
- u32 link;
- u32 cmdsts;
- u32 bufptr;
+ u32 link;
+ u32 cmdsts;
+ u32 bufptr;
} BufferDesc;
struct sis900_private {
struct net_device_stats stats;
struct pci_dev * pci_dev;
-
+
spinlock_t lock;
struct mii_phy * mii;
unsigned int cur_phy;
- struct timer_list timer; /* Link status detection timer. */
+ struct timer_list timer; /* Link status detection timer. */
- unsigned int cur_rx, dirty_rx;
+ 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 */
@@ -196,32 +197,102 @@
return 0;
}
-static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, char * card_name)
+/* older SiS900 and friends, use EEPROM to store MAC address */
+static int sis900_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
{
- struct sis900_private *sis_priv;
long ioaddr = pci_resource_start(pci_dev, 0);
- struct net_device *net_dev = NULL;
- int irq = pci_dev->irq;
u16 signature;
int i;
- if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
- return NULL;
-
/* check to see if we have sane EEPROM */
signature = (u16) read_eeprom(ioaddr, EEPROMSignature);
if (signature == 0xffff || signature == 0x0000) {
printk (KERN_INFO "%s: Error EERPOM read %x\n",
net_dev->name, signature);
+ return 0;
+ }
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+
+ return 1;
+}
+
+/* SiS630E model, use APC CMOS RAM to store MAC address */
+static int sis630e_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
+{
+ struct pci_dev *isa_bridge = NULL;
+ u8 reg;
+ int i;
+
+ if ((isa_bridge = pci_find_device(0x1039, 0x0008, isa_bridge)) == NULL) {
+ printk("%s: Can not find ISA bridge\n", net_dev->name);
+ return 0;
+ }
+ pci_read_config_byte(isa_bridge, 0x48, ®);
+ pci_write_config_byte(isa_bridge, 0x48, reg | 0x40);
+
+ for (i = 0; i < 6; i++) {
+ outb(0x09 + i, 0x70);
+ ((u8 *)(net_dev->dev_addr))[i] = inb(0x71);
+ }
+ pci_write_config_byte(isa_bridge, 0x48, reg & ~0x40);
+
+ return 1;
+}
+
+/* SiS630E A1, The Mac address is hardcoded in the RFCR register so it is actually not necessary to
+ probe the MAC address */
+static int sis630ea1_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
+{
+ long ioaddr = pci_resource_start(pci_dev, 0);
+ u32 reg;
+ int i;
+
+ /* reload MAC address */
+ reg = inl(ioaddr + cr);
+ outl(reg | RELOAD, ioaddr + cr);
+
+ reg = inl(ioaddr + cr);
+ outl(reg & ~RELOAD, ioaddr + cr);
+
+ for (i = 0; i < 3; i++) {
+ outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+ ((u16 *)(net_dev->dev_addr))[i] = inl(ioaddr + rfdr);
+ }
+
+ return 1;
+}
+
+static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, char * card_name)
+{
+ struct sis900_private *sis_priv;
+ long ioaddr = pci_resource_start(pci_dev, 0);
+ struct net_device *net_dev = NULL;
+ int irq = pci_dev->irq;
+ int i, ret = 0;
+ u8 revision;
+
+ if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
+ return NULL;
+
+ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+ if (revision == SIS630E_REV)
+ ret = sis630e_get_mac_addr(pci_dev, net_dev);
+ else if (revision == SIS630EA1_REV)
+ ret = sis630ea1_get_mac_addr(pci_dev, net_dev);
+ else
+ ret = sis900_get_mac_addr(pci_dev, net_dev);
+
+ if (ret == 0) {
+ unregister_netdevice(net_dev);
return NULL;
}
+ /* print some information about our NIC */
printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name,
card_name, ioaddr, irq);
-
- /* get MAC address from EEPROM */
- for (i = 0; i < 3; i++)
- ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
for (i = 0; i < 5; i++)
printk("%2.2x:", (u8)net_dev->dev_addr[i]);
printk("%2.2x.\n", net_dev->dev_addr[i]);
@@ -269,58 +340,69 @@
{
struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv;
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++) {
u16 mii_status;
u16 phy_id0, phy_id1;
int 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;
-
+
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) {
struct mii_phy * mii_phy;
-
- printk(KERN_INFO
+
+ printk(KERN_INFO
"%s: %s transceiver found at address %d.\n",
- net_dev->name, mii_chip_table[i].name,
+ 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_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,
+ /* the current mii is on our mii_info_table,
try next address */
break;
}
}
-
+
if (sis_priv->mii == NULL) {
- printk(KERN_INFO "%s: No MII transceivers found!\n",
+ printk(KERN_INFO "%s: No MII transceivers found!\n",
net_dev->name);
return 0;
}
- /* arbitrary choose that last PHY and current PHY */
+ /* 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);
- if (sis_priv->mii->status & MII_STAT_LINK)
+ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+ if (revision == SIS630E_REV) {
+ /* SiS 630E has some bugs on default value of PHY registers */
+ mdio_write(net_dev, sis_priv->cur_phy, MII_ANADV, 0x05e1);
+ 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);
+ }
+
+ if (sis_priv->mii->status & MII_STAT_LINK)
sis_priv->LinkOn = TRUE;
else
sis_priv->LinkOn = FALSE;
@@ -365,7 +447,7 @@
retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
eeprom_delay();
}
-
+
/* Terminate the EEPROM access. */
outl(0, ee_addr);
eeprom_delay();
@@ -426,6 +508,8 @@
outl(MDC, mdio_addr);
mdio_delay();
}
+ outl(0x00, mdio_addr);
+
return retval;
}
@@ -454,10 +538,10 @@
outl(dataval, mdio_addr);
mdio_delay();
outl(dataval | MDC, mdio_addr);
- mdio_delay();
+ mdio_delay();
}
mdio_delay();
-
+
/* Clear out extra bits. */
for (i = 2; i > 0; i--) {
outb(0, mdio_addr);
@@ -465,6 +549,8 @@
outb(MDC, mdio_addr);
mdio_delay();
}
+ outl(0x00, mdio_addr);
+
return;
}
@@ -518,7 +604,7 @@
long ioaddr = net_dev->base_addr;
u32 rfcrSave;
u32 i;
-
+
rfcrSave = inl(rfcr + ioaddr);
/* disable packet filtering before setting filter */
@@ -547,12 +633,12 @@
sis900_init_tx_ring(struct net_device *net_dev)
{
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
- long ioaddr = net_dev->base_addr;
+ long ioaddr = net_dev->base_addr;
int i;
-
+
sis_priv->tx_full = 0;
sis_priv->dirty_tx = sis_priv->cur_tx = 0;
-
+
for (i = 0; i < NUM_TX_DESC; i++) {
sis_priv->tx_skbuff[i] = NULL;
@@ -563,21 +649,21 @@
sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]);
/* load Transmit Descriptor Register */
- outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp);
+ outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp);
if (sis900_debug > 2)
- printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n",
+ printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n",
net_dev->name, inl(ioaddr + txdp));
}
-/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */
+/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */
static void
-sis900_init_rx_ring(struct net_device *net_dev)
-{
- struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
- long ioaddr = net_dev->base_addr;
+sis900_init_rx_ring(struct net_device *net_dev)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
int i;
-
- sis_priv->cur_rx = 0;
+
+ sis_priv->cur_rx = 0;
sis_priv->dirty_rx = 0;
/* init RX descriptor */
@@ -596,8 +682,8 @@
if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
/* not enough memory for skbuff, this makes a "hole"
- on the buffer ring, it is not clear how the
- hardware will react to this kind of degenerated
+ on the buffer ring, it is not clear how the
+ hardware will react to this kind of degenerated
buffer */
break;
}
@@ -611,7 +697,7 @@
/* load Receive Descriptor Register */
outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
if (sis900_debug > 2)
- printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n",
+ printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n",
net_dev->name, inl(ioaddr + rxdp));
}
/* on each timer ticks we check two things, Link Status (ON/OFF) and
@@ -626,10 +712,11 @@
u16 status;
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) {
+ while (!(status & MII_STAT_LINK)) {
+ if (mii_phy->next == NULL) {
if (sis_priv->LinkOn) {
/* link stat change from ON to OFF */
next_tick = HZ;
@@ -653,11 +740,13 @@
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,
+ 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,
+ mdio_write(net_dev, mii_phy->phy_addr,
MII_CONTROL, status & ~MII_CNTL_ISOLATE);
sis_priv->cur_phy = mii_phy->phy_addr;
}
@@ -700,7 +789,7 @@
{
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);
@@ -718,9 +807,9 @@
if (status & MII_STSOUT_LINK_FAIL)
printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
else
- printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+ printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
net_dev->name,
- *speed == HW_SPEED_100_MBPS ?
+ *speed == HW_SPEED_100_MBPS ?
"100mbps" : "10mbps",
*duplex == FDX_CAPABLE_FULL_SELECTED ?
"full" : "half");
@@ -729,7 +818,7 @@
{
int i;
u16 status;
-
+
for (i = 0; i < 2; i++)
status = mdio_read(net_dev, phy_addr, MII_STATUS);
@@ -747,22 +836,21 @@
*duplex = FDX_CAPABLE_HALF_SELECTED;
if (status & MII_STSSUM_LINK)
- printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+ printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
net_dev->name,
- *speed == HW_SPEED_100_MBPS ?
+ *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",
+ 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);
@@ -777,7 +865,7 @@
printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
-
+
/* Disable interrupts by clearing the interrupt mask. */
outl(0x0000, ioaddr + imr);
@@ -864,7 +952,7 @@
do {
status = inl(ioaddr + isr);
-
+
if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0)
/* nothing intresting happened */
break;
@@ -891,7 +979,7 @@
break;
}
} while (1);
-
+
if (sis900_debug > 3)
printk(KERN_INFO "%s: exiting interrupt, "
"interrupt status = 0x%#8.8x.\n",
@@ -909,17 +997,17 @@
long ioaddr = net_dev->base_addr;
unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
-
+
if (sis900_debug > 3)
printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
"status:0x%8.8x\n",
sis_priv->cur_rx, sis_priv->dirty_rx, rx_status);
-
+
while (rx_status & OWN) {
unsigned int rx_size;
-
+
rx_size = (rx_status & DSIZE) - CRC_SIZE;
-
+
if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
/* corrupted packet received */
if (sis900_debug > 3)
@@ -939,15 +1027,15 @@
sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
} else {
struct sk_buff * skb;
-
+
/* This situation should never happen, but due to
some unknow bugs, it is possible that
- we are working on NULL sk_buff :-( */
+ we are working on NULL sk_buff :-( */
if (sis_priv->rx_skbuff[entry] == NULL) {
printk(KERN_INFO "%s: NULL pointer "
"encountered in Rx ring, skipping\n",
net_dev->name);
- break;
+ break;
}
/* gvie the socket buffer to upper layers */
@@ -990,14 +1078,14 @@
entry = sis_priv->cur_rx % NUM_RX_DESC;
rx_status = sis_priv->rx_ring[entry].cmdsts;
} // while
-
+
/* refill the Rx buffer, what if the rate of refilling is slower than
consuming ?? */
for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
struct sk_buff *skb;
-
+
entry = sis_priv->dirty_rx % NUM_RX_DESC;
-
+
if (sis_priv->rx_skbuff[entry] == NULL) {
if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
/* not enough memory for skbuff, this makes a "hole"
@@ -1018,7 +1106,7 @@
}
/* re-enable the potentially idle receive state matchine */
outl(RxENA , ioaddr + cr );
-
+
return 0;
}
@@ -1027,21 +1115,21 @@
static void sis900_finish_xmit (struct net_device *net_dev)
{
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-
+
for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) {
unsigned int entry;
u32 tx_status;
-
+
entry = sis_priv->dirty_tx % NUM_TX_DESC;
tx_status = sis_priv->tx_ring[entry].cmdsts;
-
+
if (tx_status & OWN) {
/* The packet is not transmited yet (owned by hardware) !
Note: the interrupt is generated only when Tx Machine
is idle, so this is an almost impossible case */
break;
}
-
+
if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
/* packet unsuccessfully transmited */
if (sis900_debug > 3)
@@ -1069,8 +1157,8 @@
sis_priv->tx_ring[entry].bufptr = 0;
sis_priv->tx_ring[entry].cmdsts = 0;
}
-
- if (sis_priv->tx_full && netif_queue_stopped(net_dev) &&
+
+ if (sis_priv->tx_full && netif_queue_stopped(net_dev) &&
sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
/* The ring is no longer full, clear tx_full and schedule more transmission
by netif_wake_queue(net_dev) */
@@ -1085,7 +1173,7 @@
long ioaddr = net_dev->base_addr;
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
int i;
-
+
netif_stop_queue(net_dev);
/* Disable interrupts by clearing the interrupt mask. */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)