fec.c

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

C
2,492
字号
	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);	struct net_device *dev = fep->netdev;	int duplex;	/*	** When we get here, phy_task is already removed from	** the workqueue.  It is thus safe to allow to reuse it.	*/	fep->mii_phy_task_queued = 0;	fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;	mii_display_status(dev);	fep->old_link = fep->link;	if (fep->link) {		duplex = 0;		if (fep->phy_status		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))			duplex = 1;		fec_restart(dev, duplex);	} else		fec_stop(dev);#if 0	enable_irq(fep->mii_irq);#endif}/* mii_queue_relink is called in interrupt context from mii_link_interrupt */static void mii_queue_relink(uint mii_reg, struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	/*	** We cannot queue phy_task twice in the workqueue.  It	** would cause an endless loop in the workqueue.	** Fortunately, if the last mii_relink entry has not yet been	** executed now, it will do the job for the current interrupt,	** which is just what we want.	*/	if (fep->mii_phy_task_queued)		return;	fep->mii_phy_task_queued = 1;	INIT_WORK(&fep->phy_task, mii_relink);	schedule_work(&fep->phy_task);}/* mii_queue_config is called in interrupt context from fec_enet_mii */static void mii_queue_config(uint mii_reg, struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	if (fep->mii_phy_task_queued)		return;	fep->mii_phy_task_queued = 1;	INIT_WORK(&fep->phy_task, mii_display_config);	schedule_work(&fep->phy_task);}phy_cmd_t const phy_cmd_relink[] = {	{ mk_mii_read(MII_REG_CR), mii_queue_relink },	{ mk_mii_end, }	};phy_cmd_t const phy_cmd_config[] = {	{ mk_mii_read(MII_REG_CR), mii_queue_config },	{ mk_mii_end, }	};/* Read remainder of PHY ID.*/static voidmii_discover_phy3(uint mii_reg, struct net_device *dev){	struct fec_enet_private *fep;	int i;	fep = netdev_priv(dev);	fep->phy_id |= (mii_reg & 0xffff);	printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);	for(i = 0; phy_info[i]; i++) {		if(phy_info[i]->id == (fep->phy_id >> 4))			break;	}	if (phy_info[i])		printk(" -- %s\n", phy_info[i]->name);	else		printk(" -- unknown PHY!\n");	fep->phy = phy_info[i];	fep->phy_id_done = 1;}/* Scan all of the MII PHY addresses looking for someone to respond * with a valid ID.  This usually happens quickly. */static voidmii_discover_phy(uint mii_reg, struct net_device *dev){	struct fec_enet_private *fep;	volatile fec_t *fecp;	uint phytype;	fep = netdev_priv(dev);	fecp = fep->hwp;	if (fep->phy_addr < 32) {		if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {			/* Got first part of ID, now get remainder.			*/			fep->phy_id = phytype << 16;			mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),							mii_discover_phy3);		} else {			fep->phy_addr++;			mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),							mii_discover_phy);		}	} else {		printk("FEC: No PHY device found.\n");		/* Disable external MII interface */		fecp->fec_mii_speed = fep->phy_speed = 0;		fec_disable_phy_intr();	}}/* This interrupt occurs when the PHY detects a link change.*/#ifdef CONFIG_RPXCLASSICstatic voidmii_link_interrupt(void *dev_id)#elsestatic irqreturn_tmii_link_interrupt(int irq, void * dev_id)#endif{	struct	net_device *dev = dev_id;	struct fec_enet_private *fep = netdev_priv(dev);	fec_phy_ack_intr();#if 0	disable_irq(fep->mii_irq);  /* disable now, enable later */#endif	mii_do_cmd(dev, fep->phy->ack_int);	mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */	return IRQ_HANDLED;}static intfec_enet_open(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	/* I should reset the ring buffers here, but I don't yet know	 * a simple way to do that.	 */	fec_set_mac_address(dev);	fep->sequence_done = 0;	fep->link = 0;	if (fep->phy) {		mii_do_cmd(dev, fep->phy->ack_int);		mii_do_cmd(dev, fep->phy->config);		mii_do_cmd(dev, phy_cmd_config);  /* display configuration */		/* Poll until the PHY tells us its configuration		 * (not link state).		 * Request is initiated by mii_do_cmd above, but answer		 * comes by interrupt.		 * This should take about 25 usec per register at 2.5 MHz,		 * and we read approximately 5 registers.		 */		while(!fep->sequence_done)			schedule();		mii_do_cmd(dev, fep->phy->startup);		/* Set the initial link state to true. A lot of hardware		 * based on this device does not implement a PHY interrupt,		 * so we are never notified of link change.		 */		fep->link = 1;	} else {		fep->link = 1; /* lets just try it and see */		/* no phy,  go full duplex,  it's most likely a hub chip */		fec_restart(dev, 1);	}	netif_start_queue(dev);	fep->opened = 1;	return 0;		/* Success */}static intfec_enet_close(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	/* Don't know what to do yet.	*/	fep->opened = 0;	netif_stop_queue(dev);	fec_stop(dev);	return 0;}/* Set or clear the multicast filter for this adaptor. * Skeleton taken from sunlance driver. * The CPM Ethernet implementation allows Multicast as well as individual * MAC address filtering.  Some of the drivers check to make sure it is * a group multicast address, and discard those that are not.  I guess I * will do the same for now, but just remove the test if you want * individual filtering as well (do the upper net layers want or support * this kind of feature?). */#define HASH_BITS	6		/* #bits in hash */#define CRC32_POLY	0xEDB88320static void set_multicast_list(struct net_device *dev){	struct fec_enet_private *fep;	volatile fec_t *ep;	struct dev_mc_list *dmi;	unsigned int i, j, bit, data, crc;	unsigned char hash;	fep = netdev_priv(dev);	ep = fep->hwp;	if (dev->flags&IFF_PROMISC) {		ep->fec_r_cntrl |= 0x0008;	} else {		ep->fec_r_cntrl &= ~0x0008;		if (dev->flags & IFF_ALLMULTI) {			/* Catch all multicast addresses, so set the			 * filter to all 1's.			 */			ep->fec_hash_table_high = 0xffffffff;			ep->fec_hash_table_low = 0xffffffff;		} else {			/* Clear filter and add the addresses in hash register.			*/			ep->fec_hash_table_high = 0;			ep->fec_hash_table_low = 0;			dmi = dev->mc_list;			for (j = 0; j < dev->mc_count; j++, dmi = dmi->next)			{				/* Only support group multicast for now.				*/				if (!(dmi->dmi_addr[0] & 1))					continue;				/* calculate crc32 value of mac address				*/				crc = 0xffffffff;				for (i = 0; i < dmi->dmi_addrlen; i++)				{					data = dmi->dmi_addr[i];					for (bit = 0; bit < 8; bit++, data >>= 1)					{						crc = (crc >> 1) ^						(((crc ^ data) & 1) ? CRC32_POLY : 0);					}				}				/* only upper 6 bits (HASH_BITS) are used				   which point to specific bit in he hash registers				*/				hash = (crc >> (32 - HASH_BITS)) & 0x3f;				if (hash > 31)					ep->fec_hash_table_high |= 1 << (hash - 32);				else					ep->fec_hash_table_low |= 1 << hash;			}		}	}}/* Set a MAC change in hardware. */static voidfec_set_mac_address(struct net_device *dev){	volatile fec_t *fecp;	fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp;	/* Set station address. */	fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) |		(dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24);	fecp->fec_addr_high = (dev->dev_addr[5] << 16) |		(dev->dev_addr[4] << 24);}/* Initialize the FEC Ethernet on 860T (or ColdFire 5272). */ /*  * XXX:  We need to clean up on failure exits here.  */int __init fec_enet_init(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	unsigned long	mem_addr;	volatile cbd_t	*bdp;	cbd_t		*cbd_base;	volatile fec_t	*fecp;	int 		i, j;	static int	index = 0;	/* Only allow us to be probed once. */	if (index >= FEC_MAX_PORTS)		return -ENXIO;	/* Allocate memory for buffer descriptors.	*/	mem_addr = __get_free_page(GFP_KERNEL);	if (mem_addr == 0) {		printk("FEC: allocate descriptor memory failed?\n");		return -ENOMEM;	}	/* Create an Ethernet device instance.	*/	fecp = (volatile fec_t *) fec_hw[index];	fep->index = index;	fep->hwp = fecp;	fep->netdev = dev;	/* Whack a reset.  We should wait for this.	*/	fecp->fec_ecntrl = 1;	udelay(10);	/* Set the Ethernet address.  If using multiple Enets on the 8xx,	 * this needs some work to get unique addresses.	 *	 * This is our default MAC address unless the user changes	 * it via eth_mac_addr (our dev->set_mac_addr handler).	 */	fec_get_mac(dev);	cbd_base = (cbd_t *)mem_addr;	/* XXX: missing check for allocation failure */	fec_uncache(mem_addr);	/* Set receive and transmit descriptor base.	*/	fep->rx_bd_base = cbd_base;	fep->tx_bd_base = cbd_base + RX_RING_SIZE;	fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;	fep->cur_rx = fep->rx_bd_base;	fep->skb_cur = fep->skb_dirty = 0;	/* Initialize the receive buffer descriptors.	*/	bdp = fep->rx_bd_base;	for (i=0; i<FEC_ENET_RX_PAGES; i++) {		/* Allocate a page.		*/		mem_addr = __get_free_page(GFP_KERNEL);		/* XXX: missing check for allocation failure */		fec_uncache(mem_addr);		/* Initialize the BD for every fragment in the page.		*/		for (j=0; j<FEC_ENET_RX_FRPPG; j++) {			bdp->cbd_sc = BD_ENET_RX_EMPTY;			bdp->cbd_bufaddr = __pa(mem_addr);			mem_addr += FEC_ENET_RX_FRSIZE;			bdp++;		}	}	/* Set the last buffer to wrap.	*/	bdp--;	bdp->cbd_sc |= BD_SC_WRAP;	/* ...and the same for transmmit.	*/	bdp = fep->tx_bd_base;	for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i++) {		if (j >= FEC_ENET_TX_FRPPG) {			mem_addr = __get_free_page(GFP_KERNEL);			j = 1;		} else {			mem_addr += FEC_ENET_TX_FRSIZE;			j++;		}		fep->tx_bounce[i] = (unsigned char *) mem_addr;		/* Initialize the BD for every fragment in the page.		*/		bdp->cbd_sc = 0;		bdp->cbd_bufaddr = 0;		bdp++;	}	/* Set the last buffer to wrap.	*/	bdp--;	bdp->cbd_sc |= BD_SC_WRAP;	/* Set receive and transmit descriptor base.	*/	fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));	fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));	/* Install our interrupt handlers. This varies depending on	 * the architecture.	*/	fec_request_intrs(dev);	fecp->fec_hash_table_high = 0;	fecp->fec_hash_table_low = 0;	fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;	fecp->fec_ecntrl = 2;	fecp->fec_r_des_active = 0;	dev->base_addr = (unsigned long)fecp;	/* The FEC Ethernet specific entries in the device structure. */	dev->open = fec_enet_open;	dev->hard_start_xmit = fec_enet_start_xmit;	dev->tx_timeout = fec_timeout;	dev->watchdog_timeo = TX_TIMEOUT;	dev->stop = fec_enet_close;	dev->set_multicast_list = set_multicast_list;	for (i=0; i<NMII-1; i++)		mii_cmds[i].mii_next = &mii_cmds[i+1];	mii_free = mii_cmds;	/* setup MII interface */	fec_set_mii(dev, fep);	/* Clear and enable interrupts */	fecp->fec_ievent = 0xffc00000;	fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |		FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);	/* Queue up command to detect the PHY and initialize the	 * remainder of the interface.	 */	fep->phy_id_done = 0;	fep->phy_addr = 0;	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);	index++;	return 0;}/* This function is called to start or restart the FEC during a link * change.  This only happens when switching between half and full * duplex. */static voidfec_restart(struct net_device *dev, int duplex){	struct fec_enet_private *fep;	volatile cbd_t *bdp;	volatile fec_t *fecp;	int i;	fep = netdev_priv(dev);	fecp = fep->hwp;	/* Whack a reset.  We should wait for this.	*/	fecp->fec_ecntrl = 1;	udelay(10);	/* Clear any outstanding inter

⌨️ 快捷键说明

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