sis900.c

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

C
2,188
字号
	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_reset_phy - reset sis900 mii phy. *	@net_dev: the net device to write *	@phy_addr: default phy address * *	Some specific phy can't work properly without reset. *	This function will be called during initialization and *	link status change from ON to DOWN. */static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr){	int i;	u16 status;	for (i = 0; i < 2; i++)		status = mdio_read(net_dev, phy_addr, MII_STATUS);	mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET );	return status;}#ifdef CONFIG_NET_POLL_CONTROLLER/* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing.*/static void sis900_poll(struct net_device *dev){	disable_irq(dev->irq);	sis900_interrupt(dev->irq, dev);	enable_irq(dev->irq);}#endif/** *	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 = net_dev->priv;	long ioaddr = net_dev->base_addr;	int ret;	/* Soft reset the chip. */	sis900_reset(net_dev);	/* Equalizer workaround Rule */	sis630_set_eq(net_dev, sis_priv->chipset_rev);	ret = request_irq(net_dev->irq, &sis900_interrupt, IRQF_SHARED,						net_dev->name, net_dev);	if (ret)		return ret;	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);	/* Workaround for EDB */	sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED);	/* Enable all known interrupts by setting the interrupt mask. */	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);	outl(RxENA | inl(ioaddr + cr), 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){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	u32 rfcrSave;	u32 i;	rfcrSave = inl(rfcr + ioaddr);	/* disable packet filtering before setting filter */	outl(rfcrSave & ~RFEN, rfcr + ioaddr);	/* 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 (netif_msg_hw(sis_priv)) {			printk(KERN_DEBUG "%s: Receive Filter Addrss[%d]=%x\n",			       net_dev->name, i, inl(ioaddr + rfdr));		}	}	/* enable packet filtering */	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 = 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 = sis_priv->tx_ring_dma +			((i+1)%NUM_TX_DESC)*sizeof(BufferDesc);		sis_priv->tx_ring[i].cmdsts = 0;		sis_priv->tx_ring[i].bufptr = 0;	}	/* load Transmit Descriptor Register */	outl(sis_priv->tx_ring_dma, ioaddr + txdp);	if (netif_msg_hw(sis_priv))		printk(KERN_DEBUG "%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 voidsis900_init_rx_ring(struct net_device *net_dev){	struct sis900_private *sis_priv = 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 = sis_priv->rx_ring_dma +			((i+1)%NUM_RX_DESC)*sizeof(BufferDesc);		sis_priv->rx_ring[i].cmdsts = 0;		sis_priv->rx_ring[i].bufptr = 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;		}		sis_priv->rx_skbuff[i] = skb;		sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;                sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev,                        skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE);	}	sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);	/* load Receive Descriptor Register */	outl(sis_priv->rx_ring_dma, ioaddr + rxdp);	if (netif_msg_hw(sis_priv))		printk(KERN_DEBUG "%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 = net_dev->priv;	u16 reg14h, eq_value=0, max_value=0, min_value=0;	int i, maxcount=10;	if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||	       revision == SIS630A_900_REV || revision ==  SIS630ET_900_REV) )		return;	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 ||		    revision == SIS630ET_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;		}		/* 630B0&B1 rule to determine the equalizer value */		if (revision == SIS630A_900_REV &&		    (sis_priv->host_bridge_rev == SIS630B0 ||		     sis_priv->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);		if (revision == SIS630A_900_REV &&		    (sis_priv->host_bridge_rev == SIS630B0 ||		     sis_priv->host_bridge_rev == SIS630B1))			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV,						(reg14h | 0x2200) & 0xBFFF);		else			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 = net_dev->priv;	struct mii_phy *mii_phy = sis_priv->mii;	static const int next_tick = 5*HZ;	u16 status;	if (!sis_priv->autong_complete){		int speed, duplex = 0;		sis900_read_mode(net_dev, &speed, &duplex);		if (duplex){			sis900_set_mode(net_dev->base_addr, speed, duplex);			sis630_set_eq(net_dev, sis_priv->chipset_rev);			netif_start_queue(net_dev);		}		sis_priv->timer.expires = jiffies + HZ;		add_timer(&sis_priv->timer);		return;	}	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);	/* Link OFF -> ON */	if (!netif_carrier_ok(net_dev)) {	LookForLink:		/* Search for new PHY */		status = sis900_default_phy(net_dev);		mii_phy = sis_priv->mii;		if (status & MII_STAT_LINK){			sis900_check_mode(net_dev, mii_phy);			netif_carrier_on(net_dev);		}	} else {	/* Link ON -> OFF */                if (!(status & MII_STAT_LINK)){                	netif_carrier_off(net_dev);			if(netif_msg_link(sis_priv))                		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);

⌨️ 快捷键说明

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