diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/sis900.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/sis900.c')
-rw-r--r-- | drivers/net/sis900.c | 2370 |
1 files changed, 2370 insertions, 0 deletions
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c new file mode 100644 index 00000000000..3e9d9aab058 --- /dev/null +++ b/drivers/net/sis900.c @@ -0,0 +1,2370 @@ +/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. + Copyright 1999 Silicon Integrated System Corporation + Revision: 1.08.08 Jan. 22 2005 + + 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 General Public License (GPL), incorporated herein by reference. + Drivers based on this skeleton fall under the GPL and must retain + the authorship (implicit copyright) notice. + + References: + SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + preliminary Rev. 1.0 Jan. 14, 1998 + SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + preliminary Rev. 1.0 Nov. 10, 1998 + SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + preliminary Rev. 1.0 Jan. 18, 1998 + + Rev 1.08.08 Jan. 22 2005 Daniele Venzano use netif_msg for debugging messages + Rev 1.08.07 Nov. 2 2003 Daniele Venzano <webvenza@libero.it> add suspend/resume support + Rev 1.08.06 Sep. 24 2002 Mufasa Yang bug fix for Tx timeout & add SiS963 support + Rev 1.08.05 Jun. 6 2002 Mufasa Yang bug fix for read_eeprom & Tx descriptor over-boundary + Rev 1.08.04 Apr. 25 2002 Mufasa Yang <mufasa@sis.com.tw> added SiS962 support + Rev 1.08.03 Feb. 1 2002 Matt Domsch <Matt_Domsch@dell.com> update to use library crc32 function + Rev 1.08.02 Nov. 30 2001 Hui-Fen Hsu workaround for EDB & bug fix for dhcp problem + Rev 1.08.01 Aug. 25 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY + Rev 1.08.00 Jun. 11 2001 Hui-Fen Hsu workaround for RTL8201 PHY and some bug fix + 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 + Rev 1.07.06 Nov. 7 2000 Jeff Garzik <jgarzik@pobox.com> some bug fix and cleaning + Rev 1.07.05 Nov. 6 2000 metapirat<metapirat@gmx.de> contribute media type select by ifconfig + Rev 1.07.04 Sep. 6 2000 Lei-Chun Chang added ICS1893 PHY support + Rev 1.07.03 Aug. 24 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E eqaulizer workaround rule + Rev 1.07.01 Aug. 08 2000 Ollie Lho minor update for 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@pobox.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.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 + 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 +*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/init.h> +#include <linux/mii.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/crc32.h> +#include <linux/bitops.h> + +#include <asm/processor.h> /* Processor type for cache alignment. */ +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> /* User space memory access functions */ + +#include "sis900.h" + +#define SIS900_MODULE_NAME "sis900" +#define SIS900_DRV_VERSION "v1.08.08 Jan. 22 2005" + +static char version[] __devinitdata = +KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n"; + +static int max_interrupt_work = 40; +static int multicast_filter_limit = 128; + +static int sis900_debug = -1; /* Use SIS900_DEF_MSG as value */ + +#define SIS900_DEF_MSG \ + (NETIF_MSG_DRV | \ + NETIF_MSG_LINK | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) +/* SiS 900 is capable of 32 bits BM DMA */ +#define SIS900_DMA_MASK 0xffffffff + +enum { + SIS_900 = 0, + SIS_7016 +}; +static char * card_names[] = { + "SiS 900 PCI Fast Ethernet", + "SiS 7016 PCI Fast Ethernet" +}; +static struct pci_device_id sis900_pci_tbl [] = { + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7016}, + {0,} +}; +MODULE_DEVICE_TABLE (pci, sis900_pci_tbl); + +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; + u8 phy_types; +#define HOME 0x0001 +#define LAN 0x0002 +#define MIX 0x0003 +#define UNKNOWN 0x0 +} mii_chip_table[] = { + { "SiS 900 Internal MII PHY", 0x001d, 0x8000, LAN }, + { "SiS 7014 Physical Layer Solution", 0x0016, 0xf830, LAN }, + { "Altimata AC101LF PHY", 0x0022, 0x5520, 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 }, + { "NS 83847 PHY", 0x2000, 0x5C30, MIX }, + { "Realtek RTL8201 PHY", 0x0000, 0x8200, LAN }, + { "VIA 6103 PHY", 0x0101, 0x8f20, LAN }, + {NULL,}, +}; + +struct mii_phy { + struct mii_phy * next; + int phy_addr; + u16 phy_id0; + u16 phy_id1; + u16 status; + u8 phy_types; +}; + +typedef struct _BufferDesc { + 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; + struct mii_phy * first_mii; /* record the first mii structure */ + unsigned int cur_phy; + + struct timer_list timer; /* Link status detection timer. */ + u8 autong_complete; /* 1: auto-negotiate complete */ + + u32 msg_enable; + + 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 */ + struct sk_buff *tx_skbuff[NUM_TX_DESC]; + struct sk_buff *rx_skbuff[NUM_RX_DESC]; + BufferDesc *tx_ring; + BufferDesc *rx_ring; + + dma_addr_t tx_ring_dma; + dma_addr_t rx_ring_dma; + + unsigned int tx_full; /* The Tx queue is full. */ + u8 host_bridge_rev; + u8 chipset_rev; +}; + +MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>, Ollie Lho <ollie@sis.com.tw>"); +MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); +MODULE_LICENSE("GPL"); + +module_param(multicast_filter_limit, int, 0444); +module_param(max_interrupt_work, int, 0444); +module_param(sis900_debug, int, 0444); +MODULE_PARM_DESC(multicast_filter_limit, "SiS 900/7016 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(max_interrupt_work, "SiS 900/7016 maximum events handled per interrupt"); +MODULE_PARM_DESC(sis900_debug, "SiS 900/7016 bitmapped debugging message level"); + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sis900_poll(struct net_device *dev); +#endif +static int sis900_open(struct net_device *net_dev); +static int sis900_mii_probe (struct net_device * net_dev); +static void sis900_init_rxfilter (struct net_device * net_dev); +static u16 read_eeprom(long ioaddr, int location); +static u16 mdio_read(struct net_device *net_dev, int phy_id, int location); +static void mdio_write(struct net_device *net_dev, int phy_id, int location, int val); +static void sis900_timer(unsigned long data); +static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy); +static void sis900_tx_timeout(struct net_device *net_dev); +static void sis900_init_tx_ring(struct net_device *net_dev); +static void sis900_init_rx_ring(struct net_device *net_dev); +static int sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +static int sis900_rx(struct net_device *net_dev); +static void sis900_finish_xmit (struct net_device *net_dev); +static irqreturn_t sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +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_mcast_bitnr(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); +static struct ethtool_ops sis900_ethtool_ops; + +/** + * sis900_get_mac_addr - Get MAC address for stand alone SiS900 model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * Older SiS900 and friends, use EEPROM to store MAC address. + * MAC address is read from read_eeprom() into @net_dev->dev_addr. + */ + +static int __devinit sis900_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev) +{ + long ioaddr = pci_resource_start(pci_dev, 0); + u16 signature; + int i; + + /* check to see if we have sane EEPROM */ + signature = (u16) read_eeprom(ioaddr, EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printk (KERN_WARNING "%s: Error EERPOM read %x\n", + pci_name(pci_dev), 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_get_mac_addr - Get MAC address for SiS630E model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS630E model, use APC CMOS RAM to store MAC address. + * APC CMOS RAM is accessed through ISA bridge. + * MAC address is read into @net_dev->dev_addr. + */ + +static int __devinit sis630e_get_mac_addr(struct pci_dev * pci_dev, + struct net_device *net_dev) +{ + struct pci_dev *isa_bridge = NULL; + u8 reg; + int i; + + isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, 0x0008, isa_bridge); + if (!isa_bridge) + isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, 0x0018, isa_bridge); + if (!isa_bridge) { + printk(KERN_WARNING "%s: Can not find ISA bridge\n", + pci_name(pci_dev)); + 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); + pci_dev_put(isa_bridge); + + 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 filtering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); + + return 1; +} + +/** + * sis96x_get_mac_addr - Get MAC address for SiS962 or SiS963 model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS962 or SiS963 model, use EEPROM to store MAC address. And EEPROM + * is shared by + * LAN and 1394. When access EEPROM, send EEREQ signal to hardware first + * and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access + * by LAN, otherwise is not. After MAC address is read from EEPROM, send + * EEDONE signal to refuse EEPROM access by LAN. + * The EEPROM map of SiS962 or SiS963 is different to SiS900. + * The signature field in SiS962 or SiS963 spec is meaningless. + * MAC address is read into @net_dev->dev_addr. + */ + +static int __devinit sis96x_get_mac_addr(struct pci_dev * pci_dev, + struct net_device *net_dev) +{ + long ioaddr = net_dev->base_addr; + long ee_addr = ioaddr + mear; + u32 waittime = 0; + int i; + + outl(EEREQ, ee_addr); + while(waittime < 2000) { + if(inl(ee_addr) & EEGNT) { + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr); + + outl(EEDONE, ee_addr); + return 1; + } else { + udelay(1); + waittime ++; + } + } + outl(EEDONE, ee_addr); + return 0; +} + +/** + * sis900_probe - Probe for sis900 device + * @pci_dev: the sis900 pci device + * @pci_id: the pci device ID + * + * Check and probe sis900 net device for @pci_dev. + * Get mac address according to the chip revision, + * and assign SiS900-specific entries in the device structure. + * ie: sis900_open(), sis900_start_xmit(), sis900_close(), etc. + */ + +static int __devinit sis900_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct sis900_private *sis_priv; + struct net_device *net_dev; + struct pci_dev *dev; + dma_addr_t ring_dma; + void *ring_space; + long ioaddr; + int i, ret; + char *card_name = card_names[pci_id->driver_data]; + const char *dev_name = pci_name(pci_dev); + +/* 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"); + return i; + } + + pci_set_master(pci_dev); + + net_dev = alloc_etherdev(sizeof(struct sis900_private)); + if (!net_dev) + return -ENOMEM; + SET_MODULE_OWNER(net_dev); + SET_NETDEV_DEV(net_dev, &pci_dev->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; + + sis_priv = net_dev->priv; + net_dev->base_addr = ioaddr; + net_dev->irq = pci_dev->irq; + sis_priv->pci_dev = pci_dev; + spin_lock_init(&sis_priv->lock); + + pci_set_drvdata(pci_dev, net_dev); + + ring_space = pci_alloc_consistent(pci_dev, TX_TOTAL_SIZE, &ring_dma); + if (!ring_space) { + ret = -ENOMEM; + goto err_out_cleardev; + } + sis_priv->tx_ring = (BufferDesc *)ring_space; + sis_priv->tx_ring_dma = ring_dma; + + ring_space = pci_alloc_consistent(pci_dev, RX_TOTAL_SIZE, &ring_dma); + if (!ring_space) { + ret = -ENOMEM; + goto err_unmap_tx; + } + sis_priv->rx_ring = (BufferDesc *)ring_space; + sis_priv->rx_ring_dma = ring_dma; + + /* The SiS900-specific entries in the device structure. */ + net_dev->open = &sis900_open; + net_dev->hard_start_xmit = &sis900_start_xmit; + net_dev->stop = &sis900_close; + net_dev->get_stats = &sis900_get_stats; + net_dev->set_config = &sis900_set_config; + net_dev->set_multicast_list = &set_rx_mode; + net_dev->do_ioctl = &mii_ioctl; + net_dev->tx_timeout = sis900_tx_timeout; + net_dev->watchdog_timeo = TX_TIMEOUT; + net_dev->ethtool_ops = &sis900_ethtool_ops; + +#ifdef CONFIG_NET_POLL_CONTROLLER + net_dev->poll_controller = &sis900_poll; +#endif + + if (sis900_debug > 0) + sis_priv->msg_enable = sis900_debug; + else + sis_priv->msg_enable = SIS900_DEF_MSG; + + /* Get Mac address according to the chip revision */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &(sis_priv->chipset_rev)); + if(netif_msg_probe(sis_priv)) + printk(KERN_DEBUG "%s: detected revision %2.2x, " + "trying to get MAC address...\n", + dev_name, sis_priv->chipset_rev); + + ret = 0; + if (sis_priv->chipset_rev == SIS630E_900_REV) + ret = sis630e_get_mac_addr(pci_dev, net_dev); + else if ((sis_priv->chipset_rev > 0x81) && (sis_priv->chipset_rev <= 0x90) ) + ret = sis635_get_mac_addr(pci_dev, net_dev); + else if (sis_priv->chipset_rev == SIS96x_900_REV) + ret = sis96x_get_mac_addr(pci_dev, net_dev); + else + ret = sis900_get_mac_addr(pci_dev, net_dev); + + if (ret == 0) { + printk(KERN_WARNING "%s: Cannot read MAC address.\n", dev_name); + ret = -ENODEV; + goto err_unmap_rx; + } + + /* 630ET : set the mii access mode as software-mode */ + if (sis_priv->chipset_rev == SIS630ET_900_REV) + outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr); + + /* probe for mii transceiver */ + if (sis900_mii_probe(net_dev) == 0) { + printk(KERN_WARNING "%s: Error probing MII device.\n", + dev_name); + ret = -ENODEV; + goto err_unmap_rx; + } + + /* save our host bridge revision */ + dev = pci_get_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, NULL); + if (dev) { + pci_read_config_byte(dev, PCI_CLASS_REVISION, &sis_priv->host_bridge_rev); + pci_dev_put(dev); + } + + ret = register_netdev(net_dev); + if (ret) + goto err_unmap_rx; + + /* print some information about our NIC */ + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, + 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_unmap_rx: + pci_free_consistent(pci_dev, RX_TOTAL_SIZE, sis_priv->rx_ring, + sis_priv->rx_ring_dma); + err_unmap_tx: + pci_free_consistent(pci_dev, TX_TOTAL_SIZE, sis_priv->tx_ring, + sis_priv->tx_ring_dma); + err_out_cleardev: + pci_set_drvdata(pci_dev, NULL); + pci_release_regions(pci_dev); + err_out: + free_netdev(net_dev); + return ret; +} + +/** + * 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. + */ + +static int __init sis900_mii_probe(struct net_device * net_dev) +{ + struct sis900_private * sis_priv = net_dev->priv; + const char *dev_name = pci_name(sis_priv->pci_dev); + u16 poll_bit = MII_STAT_LINK, status = 0; + unsigned long timeout = jiffies + 5 * HZ; + int phy_addr; + + sis_priv->mii = NULL; + + /* search for total of 32 possible mii phy addresses */ + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + struct mii_phy * mii_phy = NULL; + u16 mii_status; + int i; + + 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) { + if (netif_msg_probe(sis_priv)) + printk(KERN_DEBUG "%s: MII at address %d" + " not accessible\n", + dev_name, phy_addr); + continue; + } + + if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING "Cannot allocate mem for struct mii_phy\n"); + mii_phy = sis_priv->first_mii; + while (mii_phy) { + struct mii_phy *phy; + phy = mii_phy; + mii_phy = mii_phy->next; + kfree(phy); + } + 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; + + for (i = 0; mii_chip_table[i].phy_id1; i++) + 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", + 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", + dev_name, phy_addr); + mii_phy->phy_types = UNKNOWN; + } + } + + if (sis_priv->mii == NULL) { + printk(KERN_INFO "%s: No MII transceivers found!\n", dev_name); + return 0; + } + + /* 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); + + /* workaround for ICS1893 PHY */ + if ((sis_priv->mii->phy_id0 == 0x0015) && + ((sis_priv->mii->phy_id1&0xFFF0) == 0xF440)) + mdio_write(net_dev, sis_priv->cur_phy, 0x0018, 0xD200); + + if(status & MII_STAT_LINK){ + while (poll_bit) { + yield(); + + poll_bit ^= (mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS) & poll_bit); + if (time_after_eq(jiffies, timeout)) { + printk(KERN_WARNING "%s: reset phy and link down now\n", + dev_name); + return -ETIME; + } + } + } + + if (sis_priv->chipset_rev == SIS630E_900_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) + netif_carrier_on(net_dev); + else + netif_carrier_off(net_dev); + + 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, *phy_lan = 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 default PHY & not ghost PHY */ + if ((status & MII_STAT_LINK) && !default_phy && + (phy->phy_types != UNKNOWN)) + 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; + else if(phy->phy_types == LAN) + phy_lan = phy; + } + } + + if (!default_phy && phy_home) + default_phy = phy_home; + else if (!default_phy && phy_lan) + default_phy = phy_lan; + 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", + pci_name(sis_priv->pci_dev), 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 + * @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) + +/** + * read_eeprom - Read Serial EEPROM + * @ioaddr: base i/o address + * @location: the EEPROM location to read + * + * Read Serial EEPROM through EEPROM Access Register. + * Note that location is in word (16 bits) unit + */ + +static u16 __devinit read_eeprom(long ioaddr, int location) +{ + int i; + u16 retval = 0; + long ee_addr = ioaddr + mear; + u32 read_cmd = location | EEread; + + outl(0, ee_addr); + eeprom_delay(); + outl(EECS, ee_addr); + eeprom_delay(); + + /* Shift the read command (9) bits out. */ + for (i = 8; i >= 0; i--) { + u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; + outl(dataval, ee_addr); + eeprom_delay(); + outl(dataval | EECLK, ee_addr); + eeprom_delay(); + } + outl(EECS, ee_addr); + eeprom_delay(); + + /* read the 16-bits data in */ + for (i = 16; i > 0; i--) { + outl(EECS, ee_addr); + eeprom_delay(); + outl(EECS | EECLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(0, ee_addr); + eeprom_delay(); + + return (retval); +} + +/* Read and write the MII management registers using software-generated + serial MDIO protocol. Note that the command bits and data bits are + send out separately */ +#define mdio_delay() inl(mdio_addr) + +static void mdio_idle(long mdio_addr) +{ + outl(MDIO | MDDIR, mdio_addr); + mdio_delay(); + outl(MDIO | MDDIR | MDC, mdio_addr); +} + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_reset(long mdio_addr) +{ + int i; + + for (i = 31; i >= 0; i--) { + outl(MDDIR | MDIO, mdio_addr); + mdio_delay(); + outl(MDDIR | MDIO | MDC, mdio_addr); + mdio_delay(); + } + return; +} + +/** + * mdio_read - read MII PHY register + * @net_dev: the net device to read + * @phy_id: the phy address to read + * @location: the phy regiester id to read + * + * Read MII registers through MDIO and MDC + * using MDIO management frame structure and protocol(defined by ISO/IEC). + * Please see SiS7014 or ICS spec + */ + +static u16 mdio_read(struct net_device *net_dev, int phy_id, int location) +{ + long mdio_addr = net_dev->base_addr + mear; + int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift); + u16 retval = 0; + int i; + + mdio_reset(mdio_addr); + mdio_idle(mdio_addr); + + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + mdio_delay(); + outl(dataval | MDC, mdio_addr); + mdio_delay(); + } + + /* Read the 16 data bits. */ + for (i = 16; i > 0; i--) { + outl(0, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); + outl(MDC, mdio_addr); + mdio_delay(); + } + outl(0x00, mdio_addr); + + return retval; +} + +/** + * mdio_write - write MII PHY register + * @net_dev: the net device to write + * @phy_id: the phy address to write + * @location: the phy regiester id to write + * @value: the register value to write with + * + * Write MII registers with @value through MDIO and MDC + * using MDIO management frame structure and protocol(defined by ISO/IEC) + * please see SiS7014 or ICS spec + */ + +static void mdio_write(struct net_device *net_dev, int phy_id, int location, + int value) +{ + long mdio_addr = net_dev->base_addr + mear; + int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift); + int i; + + mdio_reset(mdio_addr); + mdio_idle(mdio_addr); + + /* Shift the command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outb(dataval, mdio_addr); + mdio_delay(); + outb(dataval | MDC, mdio_addr); + mdio_delay(); + } + mdio_delay(); + + /* Shift the value bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + mdio_delay(); + outl(dataval | MDC, mdio_addr); + mdio_delay(); + } + mdio_delay(); + + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outb(0, mdio_addr); + mdio_delay(); + outb(MDC, mdio_addr); + mdio_delay(); + } + outl(0x00, mdio_addr); + + 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; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. +*/ +static void sis900_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + sis900_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + +/** + * sis900_open - open sis900 device + * @net_dev: the net device to open + * + * Do some initialization and start net interface. + * enable interrupts and set sis900 timer. + */ + +static int +sis900_open(struct net_device *net_dev) +{ + struct sis900_private *sis_priv = net_dev->priv; + long ioaddr = net_dev->base_addr; + int ret; + + /* Soft reset the chip. */ + sis900_reset(net_dev); + + /* Equalizer workaround Rule */ + sis630_set_eq(net_dev, sis_priv->chipset_rev); + + ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, + net_dev->name, net_dev); + if (ret) + return ret; + + sis900_init_rxfilter(net_dev); + + sis900_init_tx_ring(net_dev); + sis900_init_rx_ring(net_dev); + + set_rx_mode(net_dev); + + netif_start_queue(net_dev); + + /* Workaround for EDB */ + sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED); + + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); + outl(RxENA | inl(ioaddr + cr), ioaddr + cr); + outl(IE, ioaddr + ier); + + sis900_check_mode(net_dev, sis_priv->mii); + + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&sis_priv->timer); + sis_priv->timer.expires = jiffies + HZ; + sis_priv->timer.data = (unsigned long)net_dev; + sis_priv->timer.function = &sis900_timer; + add_timer(&sis_priv->timer); + + return 0; +} + +/** + * sis900_init_rxfilter - Initialize the Rx filter + * @net_dev: the net device to initialize for + * + * Set receive filter address to our MAC address + * and enable packet filtering. + */ + +static void +sis900_init_rxfilter (struct net_device * net_dev) +{ + struct sis900_private *sis_priv = net_dev->priv; + long ioaddr = net_dev->base_addr; + u32 rfcrSave; + u32 i; + + rfcrSave = inl(rfcr + ioaddr); + + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr + ioaddr); + + /* load MAC addr to filter data register */ + for (i = 0 ; i < 3 ; i++) { + u32 w; + + w = (u32) *((u16 *)(net_dev->dev_addr)+i); + outl((i << RFADDR_shift), ioaddr + rfcr); + outl(w, ioaddr + rfdr); + + if (netif_msg_hw(sis_priv)) { + printk(KERN_DEBUG "%s: Receive Filter Addrss[%d]=%x\n", + net_dev->name, i, inl(ioaddr + rfdr)); + } + } + + /* enable packet filtering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); +} + +/** + * sis900_init_tx_ring - Initialize the Tx descriptor ring + * @net_dev: the net device to initialize for + * + * Initialize the Tx descriptor ring, + */ + +static void +sis900_init_tx_ring(struct net_device *net_dev) +{ + struct sis900_private *sis_priv = net_dev->priv; + 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; |