/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
Copyright 1999 Silicon Integrated System Corporation
Revision: 1.08.09 Sep. 19 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.09 Sep. 19 2005 Daniele Venzano add Wake on LAN support
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 <linux/dma-mapping.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.09 Sep. 19 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)
enum {
SIS_900 = 0,
SIS_7016
};
static const 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 const 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 },
{ "ADM 7001 LAN PHY", 0x002e, 0xcc60, 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 mii_if_info mii_info;
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 int 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;
}