⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sis900.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.   Copyright 1999 Silicon Integrated System Corporation    Revision:	1.07.07	Nov. 29 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.   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.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/malloc.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/init.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <asm/processor.h>      /* Processor type for cache alignment. */#include <asm/bitops.h>#include <asm/io.h>#include <linux/delay.h>#include "sis900.h"static const char *version ="sis900.c: v1.07.07  11/29/2000\n";static int max_interrupt_work = 20;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 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 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);} 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},	{0,},};struct mii_phy {	struct mii_phy * next;	struct mii_chip_info * chip_info;	int phy_addr;	u16 status;};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;	unsigned int cur_phy;	struct timer_list timer;			/* Link status detection timer. */	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[NUM_TX_DESC];	BufferDesc rx_ring[NUM_RX_DESC];	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_PARM(multicast_filter_limit, "i");MODULE_PARM(max_interrupt_work, "i");MODULE_PARM(debug, "i");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);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);/** *	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, &reg);	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;}/** *	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;	long ioaddr = pci_resource_start(pci_dev, 0);	struct net_device *net_dev;	int irq = pci_dev->irq;	int i, ret = 0;	u8 revision;	char *card_name = card_names[pci_id->driver_data];	if (!pci_dma_supported(pci_dev, SIS900_DMA_MASK)) {		printk(KERN_ERR "sis900.c: architecture does not support "		       "32bit PCI busmaster DMA\n");		return -ENODEV;	}	/* setup various bits in PCI command register */	if (pci_enable_device (pci_dev))		return -ENODEV;	pci_set_master(pci_dev);	net_dev = init_etherdev(NULL, sizeof(struct sis900_private));	if (!net_dev)		return -ENOMEM;	if (!request_region(ioaddr, SIS900_TOTAL_SIZE, net_dev->name)) {		printk(KERN_ERR "sis900.c: can't allocate I/O space at 0x%lX\n", ioaddr);		ret = -EBUSY;		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;	}	/* print some information about our NIC */	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name,	       card_name, ioaddr, 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]);	sis_priv = net_dev->priv;	/* We do a request_region() to register /proc/ioports info. */	net_dev->base_addr = ioaddr;	net_dev->irq = 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_dev->driver_data = net_dev;	pci_dev->dma_mask = SIS900_DMA_MASK;	/* 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;	return 0;err_out_region:	release_region(ioaddr, SIS900_TOTAL_SIZE);err_out:	unregister_netdev(net_dev);	kfree(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 = (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				       "%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 */				break;			}	}	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);	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);	if (revision == 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;}/* 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 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(EECLK, 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();	}	outb(EECS, ee_addr);	eeprom_delay();	/* read the 16-bits data in */	for (i = 16; i > 0; i--) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -