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

📄 gianfar.c

📁 Powerpc网络处理器MPC85xx增强型三速以太控制器驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
		if (bdp->status & RXBD_WRAP)			bdp = priv->rx_bd_base;		else			bdp++;		/* update to point at the next skb */		priv->skb_currx =		    (priv->skb_currx +		     1) & RX_RING_MOD_MASK(priv->rx_ring_size);	}	/* Update the current rxbd pointer to be the next one */	priv->cur_rx = bdp;	return howmany;}#ifdef CONFIG_GFAR_NAPIstatic int gfar_poll(struct net_device *dev, int *budget){	int howmany;	struct gfar_private *priv = netdev_priv(dev);	int rx_work_limit = *budget;	unsigned long int flags;  	if (rx_work_limit > dev->quota)		rx_work_limit = dev->quota;    //取*budget 和dev->quota中的较小值    // *budget : MAX number of packets that the current CPU can receive from all interface    // dev->quota: per interface value usually = dev->weight 	howmany = gfar_clean_rx_ring(dev, rx_work_limit);	dev->quota -= howmany;	rx_work_limit -= howmany;	*budget -= howmany;	/* Lock priv */	spin_lock_irqsave(&priv->txlock, flags);	gfar_clean_tx_ring(dev);	spin_unlock_irqrestore(&priv->txlock, flags);	if (rx_work_limit > 0) { 		netif_rx_complete(dev); 		//将NIC从queue->poll_list 中删除		/* Clear the halt bit in RSTAT */		gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);		gfar_write(&priv->regs->imask, IMASK_DEFAULT);        // 重新打开收发中断		/* If we are coalescing interrupts, update the timer */		/* Otherwise, clear it */		if (likely(priv->rxcoalescing)) {			gfar_write(&priv->regs->rxic, 0);			gfar_write(&priv->regs->rxic,				   mk_ic_value(priv->rxcount, priv->rxtime));		}	}	/* Return 1 if there's more work to do */	return (rx_work_limit > 0) ? 0 : 1;}#endif#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 gfar_netpoll(struct net_device *dev){	struct gfar_private *priv = netdev_priv(dev);	/* If the device has multiple interrupts, run tx/rx */	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {		disable_irq(priv->interruptTransmit);		disable_irq(priv->interruptReceive);		disable_irq(priv->interruptError);		gfar_interrupt(priv->interruptTransmit, dev);		enable_irq(priv->interruptError);		enable_irq(priv->interruptReceive);		enable_irq(priv->interruptTransmit);	} else {		disable_irq(priv->interruptTransmit);		gfar_interrupt(priv->interruptTransmit, dev);		enable_irq(priv->interruptTransmit);	}}#endif/* The interrupt handler for devices with one interrupt */static irqreturn_t gfar_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct gfar_private *priv = netdev_priv(dev);	/* Save ievent for future reference */	u32 events = gfar_read(&priv->regs->ievent);	/* Check for reception */	if (events & IEVENT_RX_MASK)		gfar_receive(irq, dev_id);	/* Check for transmit completion */	if (events & IEVENT_TX_MASK)		gfar_transmit(irq, dev_id);	/* Check for errors */	if (events & IEVENT_ERR_MASK)		gfar_error(irq, dev_id);	return IRQ_HANDLED;}/* Called every time the controller might need to be made * aware of new link state.  The PHY code conveys this * information through variables in the phydev structure, and this * function converts those variables into the appropriate * register values, and can bring down the device if needed. */static void adjust_link(struct net_device *dev){	struct gfar_private *priv = netdev_priv(dev);	struct gfar __iomem *regs = priv->regs;	unsigned long flags;	struct phy_device *phydev = priv->phydev;	int new_state = 0;	spin_lock_irqsave(&priv->txlock, flags);	if (phydev->link) {		u32 tempval = gfar_read(&regs->maccfg2);		u32 ecntrl = gfar_read(&regs->ecntrl);		/* Now we make sure that we can be in full duplex mode.		 * If not, we operate in half-duplex mode. */		if (phydev->duplex != priv->oldduplex) {			new_state = 1;			if (!(phydev->duplex))				tempval &= ~(MACCFG2_FULL_DUPLEX);			else				tempval |= MACCFG2_FULL_DUPLEX;			priv->oldduplex = phydev->duplex;		}		if (phydev->speed != priv->oldspeed) {			new_state = 1;			switch (phydev->speed) {			case 1000:				tempval =				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);				break;			case 100:			case 10:				tempval =				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);				/* Reduced mode distinguishes				 * between 10 and 100 */				if (phydev->speed == SPEED_100)					ecntrl |= ECNTRL_R100;				else					ecntrl &= ~(ECNTRL_R100);				break;			default:				if (netif_msg_link(priv))					printk(KERN_WARNING						"%s: Ack!  Speed (%d) is not 10/100/1000!\n",						dev->name, phydev->speed);				break;			}			priv->oldspeed = phydev->speed;		}		gfar_write(&regs->maccfg2, tempval);		gfar_write(&regs->ecntrl, ecntrl);		if (!priv->oldlink) {			new_state = 1;			priv->oldlink = 1;			netif_schedule(dev);		}	} else if (priv->oldlink) {		new_state = 1;		priv->oldlink = 0;		priv->oldspeed = 0;		priv->oldduplex = -1;	}	if (new_state && netif_msg_link(priv))		phy_print_status(phydev);	spin_unlock_irqrestore(&priv->txlock, flags);}/* Update the hash table based on the current list of multicast * addresses we subscribe to.  Also, change the promiscuity of * the device based on the flags (this function is called * whenever dev->flags is changed */static void gfar_set_multi(struct net_device *dev){	struct dev_mc_list *mc_ptr;	struct gfar_private *priv = netdev_priv(dev);	struct gfar __iomem *regs = priv->regs;	u32 tempval;	if(dev->flags & IFF_PROMISC) {		/* Set RCTRL to PROM */		tempval = gfar_read(&regs->rctrl);		tempval |= RCTRL_PROM;		gfar_write(&regs->rctrl, tempval);	} else {		/* Set RCTRL to not PROM */		tempval = gfar_read(&regs->rctrl);		tempval &= ~(RCTRL_PROM);		gfar_write(&regs->rctrl, tempval);	}	if(dev->flags & IFF_ALLMULTI) {		/* Set the hash to rx all multicast frames */		gfar_write(&regs->igaddr0, 0xffffffff);		gfar_write(&regs->igaddr1, 0xffffffff);		gfar_write(&regs->igaddr2, 0xffffffff);		gfar_write(&regs->igaddr3, 0xffffffff);		gfar_write(&regs->igaddr4, 0xffffffff);		gfar_write(&regs->igaddr5, 0xffffffff);		gfar_write(&regs->igaddr6, 0xffffffff);		gfar_write(&regs->igaddr7, 0xffffffff);		gfar_write(&regs->gaddr0, 0xffffffff);		gfar_write(&regs->gaddr1, 0xffffffff);		gfar_write(&regs->gaddr2, 0xffffffff);		gfar_write(&regs->gaddr3, 0xffffffff);		gfar_write(&regs->gaddr4, 0xffffffff);		gfar_write(&regs->gaddr5, 0xffffffff);		gfar_write(&regs->gaddr6, 0xffffffff);		gfar_write(&regs->gaddr7, 0xffffffff);	} else {		int em_num;		int idx;		/* zero out the hash */		gfar_write(&regs->igaddr0, 0x0);		gfar_write(&regs->igaddr1, 0x0);		gfar_write(&regs->igaddr2, 0x0);		gfar_write(&regs->igaddr3, 0x0);		gfar_write(&regs->igaddr4, 0x0);		gfar_write(&regs->igaddr5, 0x0);		gfar_write(&regs->igaddr6, 0x0);		gfar_write(&regs->igaddr7, 0x0);		gfar_write(&regs->gaddr0, 0x0);		gfar_write(&regs->gaddr1, 0x0);		gfar_write(&regs->gaddr2, 0x0);		gfar_write(&regs->gaddr3, 0x0);		gfar_write(&regs->gaddr4, 0x0);		gfar_write(&regs->gaddr5, 0x0);		gfar_write(&regs->gaddr6, 0x0);		gfar_write(&regs->gaddr7, 0x0);		/* If we have extended hash tables, we need to		 * clear the exact match registers to prepare for		 * setting them */		if (priv->extended_hash) {			em_num = GFAR_EM_NUM + 1;			gfar_clear_exact_match(dev);			idx = 1;		} else {			idx = 0;			em_num = 0;		}		if(dev->mc_count == 0)			return;		/* Parse the list, and set the appropriate bits */		for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {			if (idx < em_num) {				gfar_set_mac_for_addr(dev, idx,						mc_ptr->dmi_addr);				idx++;			} else				gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);		}	}	return;}/* Clears each of the exact match registers to zero, so they * don't interfere with normal reception */static void gfar_clear_exact_match(struct net_device *dev){	int idx;	u8 zero_arr[MAC_ADDR_LEN] = {0,0,0,0,0,0};	for(idx = 1;idx < GFAR_EM_NUM + 1;idx++)		gfar_set_mac_for_addr(dev, idx, (u8 *)zero_arr);}/* Set the appropriate hash bit for the given addr *//* The algorithm works like so: * 1) Take the Destination Address (ie the multicast address), and * do a CRC on it (little endian), and reverse the bits of the * result. * 2) Use the 8 most significant bits as a hash into a 256-entry * table.  The table is controlled through 8 32-bit registers: * gaddr0-7.  gaddr0's MSB is entry 0, and gaddr7's LSB is * gaddr7.  This means that the 3 most significant bits in the * hash index which gaddr register to use, and the 5 other bits * indicate which bit (assuming an IBM numbering scheme, which * for PowerPC (tm) is usually the case) in the register holds * the entry. */static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr){	u32 tempval;	struct gfar_private *priv = netdev_priv(dev);	u32 result = ether_crc(MAC_ADDR_LEN, addr);	int width = priv->hash_width;	u8 whichbit = (result >> (32 - width)) & 0x1f;	u8 whichreg = result >> (32 - width + 5);	u32 value = (1 << (31-whichbit));	tempval = gfar_read(priv->hash_regs[whichreg]);	tempval |= value;	gfar_write(priv->hash_regs[whichreg], tempval);	return;}/* There are multiple MAC Address register pairs on some controllers * This function sets the numth pair to a given address */static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr){	struct gfar_private *priv = netdev_priv(dev);	int idx;	char tmpbuf[MAC_ADDR_LEN];	u32 tempval;	u32 __iomem *macptr = &priv->regs->macstnaddr1;	macptr += num*2;	/* Now copy it into the mac registers backwards, cuz */	/* little endian is silly */	for (idx = 0; idx < MAC_ADDR_LEN; idx++)		tmpbuf[MAC_ADDR_LEN - 1 - idx] = addr[idx];	gfar_write(macptr, *((u32 *) (tmpbuf)));	tempval = *((u32 *) (tmpbuf + 4));	gfar_write(macptr+1, tempval);}/* GFAR error interrupt handler */static irqreturn_t gfar_error(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct gfar_private *priv = netdev_priv(dev);	/* Save ievent for future reference */	u32 events = gfar_read(&priv->regs->ievent);	/* Clear IEVENT */	gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);	/* Magic Packet is not an error. */	if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&	    (events & IEVENT_MAG))		events &= ~IEVENT_MAG;	/* Hmm... */	if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))		printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",		       dev->name, events, gfar_read(&priv->regs->imask));	/* Update the error counters */	if (events & IEVENT_TXE) {		priv->stats.tx_errors++;		if (events & IEVENT_LC)			priv->stats.tx_window_errors++;		if (events & IEVENT_CRL)			priv->stats.tx_aborted_errors++;		if (events & IEVENT_XFUN) {			if (netif_msg_tx_err(priv))				printk(KERN_DEBUG "%s: TX FIFO underrun, "				       "packet dropped.\n", dev->name);			priv->stats.tx_dropped++;			priv->extra_stats.tx_underrun++;			/* Reactivate the Tx Queues */			gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);		}		if (netif_msg_tx_err(priv))			printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);	}	if (events & IEVENT_BSY) {		priv->stats.rx_errors++;		priv->extra_stats.rx_bsy++;		gfar_receive(irq, dev_id);#ifndef CONFIG_GFAR_NAPI		/* Clear the halt bit in RSTAT */		gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);#endif		if (netif_msg_rx_err(priv))			printk(KERN_DEBUG "%s: busy error (rstat: %x)\n",			       dev->name, gfar_read(&priv->regs->rstat));	}	if (events & IEVENT_BABR) {		priv->stats.rx_errors++;		priv->extra_stats.rx_babr++;		if (netif_msg_rx_err(priv))			printk(KERN_DEBUG "%s: babbling RX error\n", dev->name);	}	if (events & IEVENT_EBERR) {		priv->extra_stats.eberr++;		if (netif_msg_rx_err(priv))			printk(KERN_DEBUG "%s: bus error\n", dev->name);	}	if ((events & IEVENT_RXC) && netif_msg_rx_status(priv))		printk(KERN_DEBUG "%s: control frame\n", dev->name);	if (events & IEVENT_BABT) {		priv->extra_stats.tx_babt++;		if (netif_msg_tx_err(priv))			printk(KERN_DEBUG "%s: babbling TX error\n", dev->name);	}	return IRQ_HANDLED;}/* Structure for a device driver */static struct platform_driver gfar_driver = {	.probe = gfar_probe,	.remove = gfar_remove,	.suspend = gfar_suspend,	.resume = gfar_resume,	.driver	= {		.name = "fsl-gianfar",	},};static int __init gfar_init(void){	int err = gfar_mdio_init();	if (err)		return err;	err = platform_driver_register(&gfar_driver);	if (err)		gfar_mdio_exit();	return err;}static void __exit gfar_exit(void){	platform_driver_unregister(&gfar_driver);	gfar_mdio_exit();}module_init(gfar_init);module_exit(gfar_exit);

⌨️ 快捷键说明

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