📄 sis900.c
字号:
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. Copyright 1999 Silicon Integrated System Corporation Revision: 1.08.02 Nov. 30 2001 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 http://www.sis.com.tw/support/databook.htm 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@mandrakesoft.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@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.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/version.h>#include <linux/kernel.h>#include <linux/sched.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 <asm/processor.h> /* Processor type for cache alignment. */#include <asm/bitops.h>#include <asm/io.h>#include <asm/uaccess.h> /* User space memory access functions */#include "sis900.h"#define SIS900_MODULE_NAME "sis900"#define SIS900_DRV_VERSION "v1.08.02 11/30/2001"static char version[] __devinitdata =KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";static int max_interrupt_work = 40;static int multicast_filter_limit = 128;#define sis900_debug debugstatic int sis900_debug;/* 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 0xffffffffenum { 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 [] __devinitdata = { {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} mii_chip_table[] = { { "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 }, { "Realtek RTL8201 PHY", 0x0000, 0x8200, LAN }, {0,},};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 */ 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. */};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_PARM(multicast_filter_limit, "i");MODULE_PARM(max_interrupt_work, "i");MODULE_PARM(debug, "i");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(debug, "SiS 900/7016 debug level (2-4)");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 void 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_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 * @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_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_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; 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;}/** * 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 * @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; dma_addr_t ring_dma; void *ring_space; long ioaddr; int i, ret; u8 revision; char *card_name = card_names[pci_id->driver_data];/* 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); /* 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; ret = register_netdev(net_dev); if (ret) goto err_unmap_rx; /* 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_unregister; } /* 630ET : set the mii access mode as software-mode */ if (revision == SIS630ET_900_REV) outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr); /* probe for mii transceiver */ if (sis900_mii_probe(net_dev) == 0) { ret = -ENODEV; goto err_out_unregister; } /* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -