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

📄 fec_main.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. * * Copyright (c) 2003 Intracom S.A.  *  by Pantelis Antoniou <panto@intracom.gr> * * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com> * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se> * * Released under the GPL */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/bitops.h>#include <asm/8xx_immap.h>#include <asm/pgtable.h>#include <asm/mpc8xx.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/commproc.h>#include <asm/dma-mapping.h>#include "fec_8xx.h"/*************************************************/#define FEC_MAX_MULTICAST_ADDRS	64/*************************************************/static char version[] __devinitdata =    DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")" "\n";MODULE_AUTHOR("Pantelis Antoniou <panto@intracom.gr>");MODULE_DESCRIPTION("Motorola 8xx FEC ethernet driver");MODULE_LICENSE("GPL");MODULE_PARM(fec_8xx_debug, "i");MODULE_PARM_DESC(fec_8xx_debug,		 "FEC 8xx bitmapped debugging message enable value");int fec_8xx_debug = -1;		/* -1 == use FEC_8XX_DEF_MSG_ENABLE as value *//*************************************************//* * Delay to wait for FEC reset command to complete (in us)  */#define FEC_RESET_DELAY		50/*****************************************************************************************/static void fec_whack_reset(fec_t * fecp){	int i;	/*	 * Whack a reset.  We should wait for this.  	 */	FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET);	for (i = 0;	     (FR(fecp, ecntrl) & FEC_ECNTRL_RESET) != 0 && i < FEC_RESET_DELAY;	     i++)		udelay(1);	if (i == FEC_RESET_DELAY)		printk(KERN_WARNING "FEC Reset timeout!\n");}/****************************************************************************//* * Transmitter timeout.   */#define TX_TIMEOUT (2*HZ)/****************************************************************************//* * Returns the CRC needed when filling in the hash table for * multicast group filtering * pAddr must point to a MAC address (6 bytes) */static __u32 fec_mulicast_calc_crc(char *pAddr){	u8 byte;	int byte_count;	int bit_count;	__u32 crc = 0xffffffff;	u8 msb;	for (byte_count = 0; byte_count < 6; byte_count++) {		byte = pAddr[byte_count];		for (bit_count = 0; bit_count < 8; bit_count++) {			msb = crc >> 31;			crc <<= 1;			if (msb ^ (byte & 0x1)) {				crc ^= FEC_CRC_POLY;			}			byte >>= 1;		}	}	return (crc);}/* * 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?). */static void fec_set_multicast_list(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	fec_t *fecp = fep->fecp;	struct dev_mc_list *pmc;	__u32 crc;	int temp;	__u32 csrVal;	int hash_index;	__u32 hthi, htlo;	unsigned long flags;	if ((dev->flags & IFF_PROMISC) != 0) {		spin_lock_irqsave(&fep->lock, flags);		FS(fecp, r_cntrl, FEC_RCNTRL_PROM);		spin_unlock_irqrestore(&fep->lock, flags);		/*		 * Log any net taps. 		 */		printk(KERN_WARNING DRV_MODULE_NAME		       ": %s: Promiscuous mode enabled.\n", dev->name);		return;	}	if ((dev->flags & IFF_ALLMULTI) != 0 ||	    dev->mc_count > FEC_MAX_MULTICAST_ADDRS) {		/*		 * Catch all multicast addresses, set the filter to all 1's.		 */		hthi = 0xffffffffU;		htlo = 0xffffffffU;	} else {		hthi = 0;		htlo = 0;		/*		 * Now populate the hash table 		 */		for (pmc = dev->mc_list; pmc != NULL; pmc = pmc->next) {			crc = fec_mulicast_calc_crc(pmc->dmi_addr);			temp = (crc & 0x3f) >> 1;			hash_index = ((temp & 0x01) << 4) |				     ((temp & 0x02) << 2) |				     ((temp & 0x04)) |				     ((temp & 0x08) >> 2) |				     ((temp & 0x10) >> 4);			csrVal = (1 << hash_index);			if (crc & 1)				hthi |= csrVal;			else				htlo |= csrVal;		}	}	spin_lock_irqsave(&fep->lock, flags);	FC(fecp, r_cntrl, FEC_RCNTRL_PROM);	FW(fecp, hash_table_high, hthi);	FW(fecp, hash_table_low, htlo);	spin_unlock_irqrestore(&fep->lock, flags);}static int fec_set_mac_address(struct net_device *dev, void *addr){	struct sockaddr *mac = addr;	struct fec_enet_private *fep = netdev_priv(dev);	struct fec *fecp = fep->fecp;	int i;	__u32 addrhi, addrlo;	unsigned long flags;	/* Get pointer to SCC area in parameter RAM. */	for (i = 0; i < 6; i++)		dev->dev_addr[i] = mac->sa_data[i];	/*	 * Set station address. 	 */	addrhi = ((__u32) dev->dev_addr[0] << 24) |		 ((__u32) dev->dev_addr[1] << 16) |	   	 ((__u32) dev->dev_addr[2] <<  8) |	    	  (__u32) dev->dev_addr[3];	addrlo = ((__u32) dev->dev_addr[4] << 24) |	    	 ((__u32) dev->dev_addr[5] << 16);	spin_lock_irqsave(&fep->lock, flags);	FW(fecp, addr_low, addrhi);	FW(fecp, addr_high, addrlo);	spin_unlock_irqrestore(&fep->lock, flags);	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. */void fec_restart(struct net_device *dev, int duplex, int speed){#ifdef CONFIG_DUET	immap_t *immap = (immap_t *) IMAP_ADDR;	__u32 cptr;#endif	struct fec_enet_private *fep = netdev_priv(dev);	struct fec *fecp = fep->fecp;	const struct fec_platform_info *fpi = fep->fpi;	cbd_t *bdp;	struct sk_buff *skb;	int i;	__u32 addrhi, addrlo;	fec_whack_reset(fep->fecp);	/*	 * Set station address. 	 */	addrhi = ((__u32) dev->dev_addr[0] << 24) |		 ((__u32) dev->dev_addr[1] << 16) |		 ((__u32) dev->dev_addr[2] <<  8) |		 (__u32) dev->dev_addr[3];	addrlo = ((__u32) dev->dev_addr[4] << 24) |		 ((__u32) dev->dev_addr[5] << 16);	FW(fecp, addr_low, addrhi);	FW(fecp, addr_high, addrlo);	/*	 * Reset all multicast. 	 */	FW(fecp, hash_table_high, 0);	FW(fecp, hash_table_low, 0);	/*	 * Set maximum receive buffer size. 	 */	FW(fecp, r_buff_size, PKT_MAXBLR_SIZE);	FW(fecp, r_hash, PKT_MAXBUF_SIZE);	/*	 * Set receive and transmit descriptor base. 	 */	FW(fecp, r_des_start, iopa((__u32) (fep->rx_bd_base)));	FW(fecp, x_des_start, iopa((__u32) (fep->tx_bd_base)));	fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;	fep->tx_free = fep->tx_ring;	fep->cur_rx = fep->rx_bd_base;	/*	 * Reset SKB receive buffers 	 */	for (i = 0; i < fep->rx_ring; i++) {		if ((skb = fep->rx_skbuff[i]) == NULL)			continue;		fep->rx_skbuff[i] = NULL;		dev_kfree_skb(skb);	}	/*	 * Initialize the receive buffer descriptors. 	 */	for (i = 0, bdp = fep->rx_bd_base; i < fep->rx_ring; i++, bdp++) {		skb = dev_alloc_skb(ENET_RX_FRSIZE);		if (skb == NULL) {			printk(KERN_WARNING DRV_MODULE_NAME			       ": %s Memory squeeze, unable to allocate skb\n",			       dev->name);			fep->stats.rx_dropped++;			break;		}		fep->rx_skbuff[i] = skb;		skb->dev = dev;		CBDW_BUFADDR(bdp, dma_map_single(NULL, skb->data,					 L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),					 DMA_FROM_DEVICE));		CBDW_DATLEN(bdp, 0);	/* zero */		CBDW_SC(bdp, BD_ENET_RX_EMPTY |			((i < fep->rx_ring - 1) ? 0 : BD_SC_WRAP));	}	/*	 * if we failed, fillup remainder 	 */	for (; i < fep->rx_ring; i++, bdp++) {		fep->rx_skbuff[i] = NULL;		CBDW_SC(bdp, (i < fep->rx_ring - 1) ? 0 : BD_SC_WRAP);	}	/*	 * Reset SKB transmit buffers.  	 */	for (i = 0; i < fep->tx_ring; i++) {		if ((skb = fep->tx_skbuff[i]) == NULL)			continue;		fep->tx_skbuff[i] = NULL;		dev_kfree_skb(skb);	}	/*	 * ...and the same for transmit.  	 */	for (i = 0, bdp = fep->tx_bd_base; i < fep->tx_ring; i++, bdp++) {		fep->tx_skbuff[i] = NULL;		CBDW_BUFADDR(bdp, virt_to_bus(NULL));		CBDW_DATLEN(bdp, 0);		CBDW_SC(bdp, (i < fep->tx_ring - 1) ? 0 : BD_SC_WRAP);	}	/*	 * Enable big endian and don't care about SDMA FC. 	 */	FW(fecp, fun_code, 0x78000000);	/*	 * Set MII speed. 	 */	FW(fecp, mii_speed, fep->fec_phy_speed);	/*	 * Clear any outstanding interrupt. 	 */	FW(fecp, ievent, 0xffc0);	FW(fecp, ivec, (fpi->fec_irq / 2) << 29);	/*	 * adjust to speed (only for DUET & RMII) 	 */#ifdef CONFIG_DUET	cptr = in_be32(&immap->im_cpm.cp_cptr);	switch (fpi->fec_no) {	case 0:		/*		 * check if in RMII mode 		 */		if ((cptr & 0x100) == 0)			break;		if (speed == 10)			cptr |= 0x0000010;		else if (speed == 100)			cptr &= ~0x0000010;		break;	case 1:		/*		 * check if in RMII mode 		 */		if ((cptr & 0x80) == 0)			break;		if (speed == 10)			cptr |= 0x0000008;		else if (speed == 100)			cptr &= ~0x0000008;		break;	default:		break;	}	out_be32(&immap->im_cpm.cp_cptr, cptr);#endif	FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE);	/* MII enable */	/*	 * adjust to duplex mode 	 */	if (duplex) {		FC(fecp, r_cntrl, FEC_RCNTRL_DRT);		FS(fecp, x_cntrl, FEC_TCNTRL_FDEN);	/* FD enable */	} else {		FS(fecp, r_cntrl, FEC_RCNTRL_DRT);		FC(fecp, x_cntrl, FEC_TCNTRL_FDEN);	/* FD disable */	}	/*	 * Enable interrupts we wish to service. 	 */	FW(fecp, imask, FEC_ENET_TXF | FEC_ENET_TXB |	   FEC_ENET_RXF | FEC_ENET_RXB);	/*	 * And last, enable the transmit and receive processing. 	 */	FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);	FW(fecp, r_des_active, 0x01000000);}void fec_stop(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	fec_t *fecp = fep->fecp;	struct sk_buff *skb;	int i;	if ((FR(fecp, ecntrl) & FEC_ECNTRL_ETHER_EN) == 0)		return;		/* already down */	FW(fecp, x_cntrl, 0x01);	/* Graceful transmit stop */	for (i = 0; ((FR(fecp, ievent) & 0x10000000) == 0) &&	     i < FEC_RESET_DELAY; i++)		udelay(1);	if (i == FEC_RESET_DELAY)		printk(KERN_WARNING DRV_MODULE_NAME		       ": %s FEC timeout on graceful transmit stop\n",		       dev->name);	/*	 * Disable FEC. Let only MII interrupts. 	 */	FW(fecp, imask, 0);	FW(fecp, ecntrl, ~FEC_ECNTRL_ETHER_EN);	/*	 * Reset SKB transmit buffers.  	 */	for (i = 0; i < fep->tx_ring; i++) {		if ((skb = fep->tx_skbuff[i]) == NULL)			continue;		fep->tx_skbuff[i] = NULL;		dev_kfree_skb(skb);	}	/*	 * Reset SKB receive buffers 	 */	for (i = 0; i < fep->rx_ring; i++) {		if ((skb = fep->rx_skbuff[i]) == NULL)			continue;		fep->rx_skbuff[i] = NULL;		dev_kfree_skb(skb);	}}/* common receive function */static int fec_enet_rx_common(struct net_device *dev, int *budget){	struct fec_enet_private *fep = netdev_priv(dev);	fec_t *fecp = fep->fecp;	const struct fec_platform_info *fpi = fep->fpi;	cbd_t *bdp;	struct sk_buff *skb, *skbn, *skbt;	int received = 0;	__u16 pkt_len, sc;	int curidx;	int rx_work_limit;	if (fpi->use_napi) {		rx_work_limit = min(dev->quota, *budget);		if (!netif_running(dev))			return 0;	}	/*	 * First, grab all of the stats for the incoming packet.	 * These get messed up if we get called due to a busy condition.	 */	bdp = fep->cur_rx;	/* clear RX status bits for napi*/	if (fpi->use_napi)		FW(fecp, ievent, FEC_ENET_RXF | FEC_ENET_RXB);	while (((sc = CBDR_SC(bdp)) & BD_ENET_RX_EMPTY) == 0) {		curidx = bdp - fep->rx_bd_base;		/*		 * Since we have allocated space to hold a complete frame,		 * the last indicator should be set.		 */		if ((sc & BD_ENET_RX_LAST) == 0)			printk(KERN_WARNING DRV_MODULE_NAME			       ": %s rcv is not +last\n",			       dev->name);		/*		 * Check for errors. 		 */		if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_CL |			  BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) {			fep->stats.rx_errors++;			/* Frame too long or too short. */			if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))				fep->stats.rx_length_errors++;			/* Frame alignment */			if (sc & (BD_ENET_RX_NO | BD_ENET_RX_CL))				fep->stats.rx_frame_errors++;			/* CRC Error */			if (sc & BD_ENET_RX_CR)				fep->stats.rx_crc_errors++;			/* FIFO overrun */			if (sc & BD_ENET_RX_OV)				fep->stats.rx_crc_errors++;			skbn = fep->rx_skbuff[curidx];			BUG_ON(skbn == NULL);		} else {			/* napi, got packet but no quota */			if (fpi->use_napi && --rx_work_limit < 0)				break;			skb = fep->rx_skbuff[curidx];			BUG_ON(skb == NULL);			/*			 * Process the incoming frame.			 */			fep->stats.rx_packets++;			pkt_len = CBDR_DATLEN(bdp) - 4;	/* remove CRC */			fep->stats.rx_bytes += pkt_len + 4;			if (pkt_len <= fpi->rx_copybreak) {				/* +2 to make IP header L1 cache aligned */				skbn = dev_alloc_skb(pkt_len + 2);				if (skbn != NULL) {					skb_reserve(skbn, 2);	/* align IP header */					memcpy(skbn->data, skb->data, pkt_len);					/* swap */					skbt = skb;					skb = skbn;					skbn = skbt;				}			} else				skbn = dev_alloc_skb(ENET_RX_FRSIZE);			if (skbn != NULL) {				skb->dev = dev;				skb_put(skb, pkt_len);	/* Make room */				skb->protocol = eth_type_trans(skb, dev);				received++;				if (!fpi->use_napi)					netif_rx(skb);				else					netif_receive_skb(skb);			} else {				printk(KERN_WARNING DRV_MODULE_NAME				       ": %s Memory squeeze, dropping packet.\n",				       dev->name);				fep->stats.rx_dropped++;				skbn = skb;			}		}		fep->rx_skbuff[curidx] = skbn;		CBDW_BUFADDR(bdp, dma_map_single(NULL, skbn->data,						 L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),						 DMA_FROM_DEVICE));		CBDW_DATLEN(bdp, 0);		CBDW_SC(bdp, (sc & ~BD_ENET_RX_STATS) | BD_ENET_RX_EMPTY);		/*		 * Update BD pointer to next entry. 		 */		if ((sc & BD_ENET_RX_WRAP) == 0)			bdp++;		else			bdp = fep->rx_bd_base;		/*		 * Doing this here will keep the FEC running while we process		 * incoming frames.  On a heavily loaded network, we should be		 * able to keep up at the expense of system resources.		 */		FW(fecp, r_des_active, 0x01000000);	}	fep->cur_rx = bdp;	if (fpi->use_napi) {		dev->quota -= received;		*budget -= received;		if (rx_work_limit < 0)			return 1;	/* not done */		/* done */		netif_rx_complete(dev);		/* enable RX interrupt bits */		FS(fecp, imask, FEC_ENET_RXF | FEC_ENET_RXB);	}	return 0;}static void fec_enet_tx(struct net_device *dev){	struct fec_enet_private *fep = netdev_priv(dev);	cbd_t *bdp;	struct sk_buff *skb;	int dirtyidx, do_wake;	__u16 sc;	spin_lock(&fep->lock);	bdp = fep->dirty_tx;	do_wake = 0;	while (((sc = CBDR_SC(bdp)) & BD_ENET_TX_READY) == 0) {

⌨️ 快捷键说明

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