sis900.c

来自「linux 内核源代码」· C语言 代码 · 共 2,188 行 · 第 1/5 页

C
2,188
字号
                	/* Change mode issue */                	if ((mii_phy->phy_id0 == 0x001D) &&			    ((mii_phy->phy_id1 & 0xFFF0) == 0x8000))               			sis900_reset_phy(net_dev,  sis_priv->cur_phy);			sis630_set_eq(net_dev, sis_priv->chipset_rev);                	goto LookForLink;                }	}	sis_priv->timer.expires = jiffies + next_tick;	add_timer(&sis_priv->timer);}/** *	sis900_check_mode - check the media mode for sis900 *	@net_dev: the net device to be checked *	@mii_phy: the mii phy * *	Older driver gets the media mode from mii status output *	register. Now we set our media capability and auto-negotiate *	to get the upper bound of speed and duplex between two ends. *	If the types of mii phy is HOME, it doesn't need to auto-negotiate *	and autong_complete should be set to 1. */static void sis900_check_mode(struct net_device *net_dev, struct mii_phy *mii_phy){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	int speed, duplex;	if (mii_phy->phy_types == LAN) {		outl(~EXD & inl(ioaddr + cfg), ioaddr + cfg);		sis900_set_capability(net_dev , mii_phy);		sis900_auto_negotiate(net_dev, sis_priv->cur_phy);	} else {		outl(EXD | inl(ioaddr + cfg), ioaddr + cfg);		speed = HW_SPEED_HOME;		duplex = FDX_CAPABLE_HALF_SELECTED;		sis900_set_mode(ioaddr, speed, duplex);		sis_priv->autong_complete = 1;	}}/** *	sis900_set_mode - Set the media mode of mac register. *	@ioaddr: the address of the device *	@speed : the transmit speed to be determined *	@duplex: the duplex mode to be determined * *	Set the media mode of mac register txcfg/rxcfg according to *	speed and duplex of phy. Bit EDB_MASTER_EN indicates the EDB *	bus is used instead of PCI bus. When this bit is set 1, the *	Max DMA Burst Size for TX/RX DMA should be no larger than 16 *	double words. */static void sis900_set_mode (long ioaddr, int speed, int duplex){	u32 tx_flags = 0, rx_flags = 0;	if (inl(ioaddr + cfg) & EDB_MASTER_EN) {		tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) |					(TX_FILL_THRESH << TxFILLT_shift);		rx_flags = DMA_BURST_64 << RxMXDMA_shift;	} else {		tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) |					(TX_FILL_THRESH << TxFILLT_shift);		rx_flags = DMA_BURST_512 << RxMXDMA_shift;	}	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) {		rx_flags |= (RxDRNT_10 << RxDRNT_shift);		tx_flags |= (TxDRNT_10 << TxDRNT_shift);	} else {		rx_flags |= (RxDRNT_100 << RxDRNT_shift);		tx_flags |= (TxDRNT_100 << TxDRNT_shift);	}	if (duplex == FDX_CAPABLE_FULL_SELECTED) {		tx_flags |= (TxCSI | TxHBI);		rx_flags |= RxATX;	}#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)	/* Can accept Jumbo packet */	rx_flags |= RxAJAB;#endif	outl (tx_flags, ioaddr + txcfg);	outl (rx_flags, ioaddr + rxcfg);}/** *	sis900_auto_negotiate - Set the Auto-Negotiation Enable/Reset bit. *	@net_dev: the net device to read mode for *	@phy_addr: mii phy address * *	If the adapter is link-on, set the auto-negotiate enable/reset bit. *	autong_complete should be set to 0 when starting auto-negotiation. *	autong_complete should be set to 1 if we didn't start auto-negotiation. *	sis900_timer will wait for link on again if autong_complete = 0. */static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr){	struct sis900_private *sis_priv = net_dev->priv;	int i = 0;	u32 status;	for (i = 0; i < 2; i++)		status = mdio_read(net_dev, phy_addr, MII_STATUS);	if (!(status & MII_STAT_LINK)){		if(netif_msg_link(sis_priv))			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);		sis_priv->autong_complete = 1;		netif_carrier_off(net_dev);		return;	}	/* (Re)start AutoNegotiate */	mdio_write(net_dev, phy_addr, MII_CONTROL,		   MII_CNTL_AUTO | MII_CNTL_RST_AUTO);	sis_priv->autong_complete = 0;}/** *	sis900_read_mode - read media mode for sis900 internal phy *	@net_dev: the net device to read mode for *	@speed  : the transmit speed to be determined *	@duplex : the duplex mode to be determined * *	The capability of remote end will be put in mii register autorec *	after auto-negotiation. Use AND operation to get the upper bound *	of speed and duplex between two ends. */static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex){	struct sis900_private *sis_priv = net_dev->priv;	struct mii_phy *phy = sis_priv->mii;	int phy_addr = sis_priv->cur_phy;	u32 status;	u16 autoadv, autorec;	int i;	for (i = 0; i < 2; i++)		status = mdio_read(net_dev, phy_addr, MII_STATUS);	if (!(status & MII_STAT_LINK))		return;	/* AutoNegotiate completed */	autoadv = mdio_read(net_dev, phy_addr, MII_ANADV);	autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR);	status = autoadv & autorec;	*speed = HW_SPEED_10_MBPS;	*duplex = FDX_CAPABLE_HALF_SELECTED;	if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))		*speed = HW_SPEED_100_MBPS;	if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))		*duplex = FDX_CAPABLE_FULL_SELECTED;	sis_priv->autong_complete = 1;	/* Workaround for Realtek RTL8201 PHY issue */	if ((phy->phy_id0 == 0x0000) && ((phy->phy_id1 & 0xFFF0) == 0x8200)) {		if (mdio_read(net_dev, phy_addr, MII_CONTROL) & MII_CNTL_FDX)			*duplex = FDX_CAPABLE_FULL_SELECTED;		if (mdio_read(net_dev, phy_addr, 0x0019) & 0x01)			*speed = HW_SPEED_100_MBPS;	}	if(netif_msg_link(sis_priv))		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",	       				net_dev->name,	       				*speed == HW_SPEED_100_MBPS ?	       					"100mbps" : "10mbps",	       				*duplex == FDX_CAPABLE_FULL_SELECTED ?	       					"full" : "half");}/** *	sis900_tx_timeout - sis900 transmit timeout routine *	@net_dev: the net device to transmit * *	print transmit timeout status *	disable interrupts and do some tasks */static void sis900_tx_timeout(struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned long flags;	int i;	if(netif_msg_tx_err(sis_priv))		printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",	       		net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));	/* Disable interrupts by clearing the interrupt mask. */	outl(0x0000, ioaddr + imr);	/* use spinlock to prevent interrupt handler accessing buffer ring */	spin_lock_irqsave(&sis_priv->lock, flags);	/* discard unsent packets */	sis_priv->dirty_tx = sis_priv->cur_tx = 0;	for (i = 0; i < NUM_TX_DESC; i++) {		struct sk_buff *skb = sis_priv->tx_skbuff[i];		if (skb) {			pci_unmap_single(sis_priv->pci_dev,				sis_priv->tx_ring[i].bufptr, skb->len,				PCI_DMA_TODEVICE);			dev_kfree_skb_irq(skb);			sis_priv->tx_skbuff[i] = NULL;			sis_priv->tx_ring[i].cmdsts = 0;			sis_priv->tx_ring[i].bufptr = 0;			net_dev->stats.tx_dropped++;		}	}	sis_priv->tx_full = 0;	netif_wake_queue(net_dev);	spin_unlock_irqrestore(&sis_priv->lock, flags);	net_dev->trans_start = jiffies;	/* load Transmit Descriptor Register */	outl(sis_priv->tx_ring_dma, ioaddr + txdp);	/* Enable all known interrupts by setting the interrupt mask. */	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);	return;}/** *	sis900_start_xmit - sis900 start transmit routine *	@skb: socket buffer pointer to put the data being transmitted *	@net_dev: the net device to transmit with * *	Set the transmit buffer descriptor, *	and write TxENA to enable transmit state machine. *	tell upper layer if the buffer is full */static intsis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned int  entry;	unsigned long flags;	unsigned int  index_cur_tx, index_dirty_tx;	unsigned int  count_dirty_tx;	/* Don't transmit data before the complete of auto-negotiation */	if(!sis_priv->autong_complete){		netif_stop_queue(net_dev);		return 1;	}	spin_lock_irqsave(&sis_priv->lock, flags);	/* Calculate the next Tx descriptor entry. */	entry = sis_priv->cur_tx % NUM_TX_DESC;	sis_priv->tx_skbuff[entry] = skb;	/* set the transmit buffer descriptor and enable Transmit State Machine */	sis_priv->tx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev,		skb->data, skb->len, PCI_DMA_TODEVICE);	sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);	outl(TxENA | inl(ioaddr + cr), ioaddr + cr);	sis_priv->cur_tx ++;	index_cur_tx = sis_priv->cur_tx;	index_dirty_tx = sis_priv->dirty_tx;	for (count_dirty_tx = 0; index_cur_tx != index_dirty_tx; index_dirty_tx++)		count_dirty_tx ++;	if (index_cur_tx == index_dirty_tx) {		/* dirty_tx is met in the cycle of cur_tx, buffer full */		sis_priv->tx_full = 1;		netif_stop_queue(net_dev);	} else if (count_dirty_tx < NUM_TX_DESC) {		/* Typical path, tell upper layer that more transmission is possible */		netif_start_queue(net_dev);	} else {		/* buffer full, tell upper layer no more transmission */		sis_priv->tx_full = 1;		netif_stop_queue(net_dev);	}	spin_unlock_irqrestore(&sis_priv->lock, flags);	net_dev->trans_start = jiffies;	if (netif_msg_tx_queued(sis_priv))		printk(KERN_DEBUG "%s: Queued Tx packet at %p size %d "		       "to slot %d.\n",		       net_dev->name, skb->data, (int)skb->len, entry);	return 0;}/** *	sis900_interrupt - sis900 interrupt handler *	@irq: the irq number *	@dev_instance: the client data object *	@regs: snapshot of processor context * *	The interrupt handler does all of the Rx thread work, *	and cleans up after the Tx thread */static irqreturn_t sis900_interrupt(int irq, void *dev_instance){	struct net_device *net_dev = dev_instance;	struct sis900_private *sis_priv = net_dev->priv;	int boguscnt = max_interrupt_work;	long ioaddr = net_dev->base_addr;	u32 status;	unsigned int handled = 0;	spin_lock (&sis_priv->lock);	do {		status = inl(ioaddr + isr);		if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0)			/* nothing intresting happened */			break;		handled = 1;		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */		if (status & (RxORN | RxERR | RxOK))			/* Rx interrupt */			sis900_rx(net_dev);		if (status & (TxURN | TxERR | TxIDLE))			/* Tx interrupt */			sis900_finish_xmit(net_dev);		/* something strange happened !!! */		if (status & HIBERR) {			if(netif_msg_intr(sis_priv))				printk(KERN_INFO "%s: Abnormal interrupt,"					"status %#8.8x.\n", net_dev->name, status);			break;		}		if (--boguscnt < 0) {			if(netif_msg_intr(sis_priv))				printk(KERN_INFO "%s: Too much work at interrupt, "					"interrupt status = %#8.8x.\n",					net_dev->name, status);			break;		}	} while (1);	if(netif_msg_intr(sis_priv))		printk(KERN_DEBUG "%s: exiting interrupt, "		       "interrupt status = 0x%#8.8x.\n",		       net_dev->name, inl(ioaddr + isr));	spin_unlock (&sis_priv->lock);	return IRQ_RETVAL(handled);}/** *	sis900_rx - sis900 receive routine *	@net_dev: the net device which receives data * *	Process receive interrupt events, *	put buffer to higher layer and refill buffer pool *	Note: This function is called by interrupt handler, *	don't do "too much" work here */static int sis900_rx(struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;	u32 rx_status = sis_priv->rx_ring[entry].cmdsts;	int rx_work_limit;	if (netif_msg_rx_status(sis_priv))		printk(KERN_DEBUG "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "		       "status:0x%8.8x\n",		       sis_priv->cur_rx, sis_priv->dirty_rx, rx_status);	rx_work_limit = sis_priv->dirty_rx + NUM_RX_DESC - sis_priv->cur_rx;	while (rx_status & OWN) {		unsigned int rx_size;		unsigned int data_size;		if (--rx_work_limit < 0)			break;		data_size = rx_status & DSIZE;		rx_size = data_size - CRC_SIZE;#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)		/* ``TOOLONG'' flag means jumbo packet recived. */		if ((rx_status & TOOLONG) && data_size <= MAX_FRAME_SIZE)			rx_status &= (~ ((unsigned int)TOOLONG));#endif		if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {			/* corrupted packet received */			if (netif_msg_rx_err(sis_priv))				printk(KERN_DEBUG "%s: Corrupted packet "				       "received, buffer status = 0x%8.8x/%d.\n",				       net_dev->name, rx_status, data_size);			net_dev->stats.rx_errors++;			if (rx_status & OVERRUN)				net_dev->stats.rx_over_errors++;			if (rx_status & (TOOLONG|RUNT))				net_dev->stats.rx_length_errors++;			if (rx_status & (RXISERR | FAERR))				net_dev->stats.rx_frame_errors++;			if (rx_status & CRCERR)				net_dev->stats.rx_crc_errors++;			/* reset buffer descriptor state */			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;		} else {			struct sk_buff * skb;			struct sk_buff * rx_skb;

⌨️ 快捷键说明

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