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

📄 sis900.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		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();	outl(EECLK, ee_addr);	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 seperately */#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_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 intsis900_open(struct net_device *net_dev){	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;	long ioaddr = net_dev->base_addr;	u8  revision;	MOD_INC_USE_COUNT;	/* Soft reset the chip. */	sis900_reset(net_dev);	/* Equalizer workaround Rule */	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);	if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||	    revision == SIS630A_900_REV)		sis630_set_eq(net_dev,revision);	if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {		MOD_DEC_USE_COUNT;		return -EAGAIN;	}	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);	/* Enable all known interrupts by setting the interrupt mask. */	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);	outl(RxENA, 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 voidsis900_init_rxfilter (struct net_device * net_dev){	long ioaddr = net_dev->base_addr;	u32 rfcrSave;	u32 i;	rfcrSave = inl(rfcr + ioaddr);	/* disable packet filtering before setting filter */	outl(rfcrSave & ~RFEN, rfcr);	/* 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 (sis900_debug > 2) {			printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n",			       net_dev->name, i, inl(ioaddr + rfdr));		}	}	/* enable packet filitering */	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 voidsis900_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;	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;		sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]);		sis_priv->tx_ring[i].cmdsts = 0;		sis_priv->tx_ring[i].bufptr = 0;	}	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);	if (sis900_debug > 2)		printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n",		       net_dev->name, inl(ioaddr + txdp));}/** *	sis900_init_rx_ring: - Initialize the Rx descriptor ring *	@net_dev: the net device to initialize for * *	Initialize the Rx descriptor ring,  *	and pre-allocate recevie buffers (socket buffer) */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;	int i;	sis_priv->cur_rx = 0;	sis_priv->dirty_rx = 0;	/* init RX descriptor */	for (i = 0; i < NUM_RX_DESC; i++) {		sis_priv->rx_skbuff[i] = NULL;		sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]);		sis_priv->rx_ring[i].cmdsts = 0;		sis_priv->rx_ring[i].bufptr = 0;	}	sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]);	/* allocate sock buffers */	for (i = 0; i < NUM_RX_DESC; i++) {		struct sk_buff *skb;		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			   buffer */			break;		}		skb->dev = net_dev;		sis_priv->rx_skbuff[i] = skb;		sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;		sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail);	}	sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);	/* 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",		       net_dev->name, inl(ioaddr + rxdp));}/** *	sis630_set_eq: - set phy equalizer value for 630 LAN *	@net_dev: the net device to set equalizer value *	@revision: 630 LAN revision number * *	630E equalizer workaround rule(Cyrus Huang 08/15) *	PHY register 14h(Test) *	Bit 14: 0 -- Automatically dectect (default) *		1 -- Manually set Equalizer filter *	Bit 13: 0 -- (Default) *		1 -- Speed up convergence of equalizer setting *	Bit 9 : 0 -- (Default) *		1 -- Disable Baseline Wander *	Bit 3~7   -- Equalizer filter setting *	Link ON: Set Bit 9, 13 to 1, Bit 14 to 0 *	Then calculate equalizer value *	Then set equalizer value, and set Bit 14 to 1, Bit 9 to 0 *	Link Off:Set Bit 13 to 1, Bit 14 to 0 *	Calculate Equalizer value: *	When Link is ON and Bit 14 is 0, SIS900PHY will auto-dectect proper equalizer value. *	When the equalizer is stable, this value is not a fixed value. It will be within *	a small range(eg. 7~9). Then we get a minimum and a maximum value(eg. min=7, max=9) *	0 <= max <= 4  --> set equalizer to max *	5 <= max <= 14 --> set equalizer to max+1 or set equalizer to max+2 if max == min *	max >= 15      --> set equalizer to max+5 or set equalizer to max+6 if max == min */static void sis630_set_eq(struct net_device *net_dev, u8 revision){	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;	u16 reg14h, eq_value, max_value=0, min_value=0;	u8 host_bridge_rev;	int i, maxcount=10;	struct pci_dev *dev=NULL;	if ((dev = pci_find_device(SIS630_DEVICE_ID, SIS630_VENDOR_ID, dev)))		pci_read_config_byte(dev, PCI_CLASS_REVISION, &host_bridge_rev);	if (netif_carrier_ok(net_dev)) {		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF);		for (i=0; i < maxcount; i++) {			eq_value=(0x00F8 & mdio_read(net_dev, sis_priv->cur_phy, MII_RESV)) >> 3;			if (i == 0)				max_value=min_value=eq_value;			max_value=(eq_value > max_value) ? eq_value : max_value;			min_value=(eq_value < min_value) ? eq_value : min_value;		}		/* 630E rule to determine the equalizer value */		if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV) {			if (max_value < 5)				eq_value=max_value;			else if (max_value >= 5 && max_value < 15)				eq_value=(max_value == min_value) ? max_value+2 : max_value+1;			else if (max_value >= 15)				eq_value=(max_value == min_value) ? max_value+6 : max_value+5;		}		/* 630A0 rule to determine the equalizer value */		if (revision == SIS630A_900_REV && host_bridge_rev == SIS630A0) {			if (max_value < 5)				eq_value=max_value+3;			else if (max_value >= 5)				eq_value=max_value+5;		}		/* 630B0&B1 rule to determine the equalizer value */		if (revision == SIS630A_900_REV && 		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) {			if (max_value == 0)				eq_value=3;			else				eq_value=(max_value+min_value+1)/2;		}		/* write equalizer value and setting */		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);		reg14h=(reg14h & 0xFF07) | ((eq_value << 3) & 0x00F8);		reg14h=(reg14h | 0x6000) & 0xFDFF;		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, reg14h);	}	else {		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);	}	return;}/** *	sis900_timer: - sis900 timer routine *	@data: pointer to sis900 net device * *	On each timer ticks we check two things,  *	link status (ON/OFF) and link mode (10/100/Full/Half) */static void sis900_timer(unsigned long data){	struct net_device *net_dev = (struct net_device *)data;	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;	struct mii_phy *mii_phy = sis_priv->mii;	static int next_tick = 5*HZ;	u16 status;	u8 revision;	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) {			if (netif_carrier_ok(net_dev)) {				/* link stat change from ON to OFF */				next_tick = HZ;				netif_carrier_off(net_dev);				/* Equalizer workaround Rule */				pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);				if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||				    revision == SIS630A_900_REV)					sis630_set_eq(net_dev, revision);				printk(KERN_INFO "%s: Media Link Off\n",				       net_dev->name);			}			sis_priv->timer.expires = jiffies + next_tick;			add_timer(&sis_priv->timer);			return;		}		mii_phy = mii_phy->next;		status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);	}	if (!netif_carrier_ok(net_dev)) {		/* link stat change forn OFF to ON, read and report link mode */		netif_carrier_on(net_dev);		next_tick = 5*HZ;		/* Equalizer workaround Rule */		pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);		if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||

⌨️ 快捷键说明

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