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

📄 fec.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * * This version of the driver is specific to the FADS implementation, * since the board contains control registers external to the processor * for the control of the LevelOne LXT970 transceiver.  The MPC860T manual * describes connections using the internal parallel port I/O, which * is basically all of Port D. * * Right now, I am very watseful with the buffers.  I allocate memory * pages and then divide them into 2K frame buffers.  This way I know I * have buffers large enough to hold one frame within one buffer descriptor. * Once I get this working, I will use 64 or 128 byte CPM buffers, which * will be much more memory efficient and will easily handle lots of * small packets. * * Much better multiple PHY support by Magnus Damm. * Copyright (c) 2000 Ericsson Radio Systems AB. * *//* List of PHYs we wish to support.*/#define CONFIG_FEC_LXT970#define CONFIG_FEC_LXT971#define CONFIG_FEC_QS6612#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/malloc.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>#ifdef CONFIG_FEC_PACKETHOOK#include <linux/pkthook.h>#endif#include <asm/8xx_immap.h>#include <asm/pgtable.h>#include <asm/mpc8xx.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include "commproc.h"/* Forward declarations of some structures to support different PHYs*/typedef struct {	uint mii_data;	void (*funct)(uint mii_reg, struct net_device *dev);} phy_cmd_t;typedef struct {	uint id;	char *name;	const phy_cmd_t *config;	const phy_cmd_t *startup;	const phy_cmd_t *ack_int;	const phy_cmd_t *shutdown;} phy_info_t;/* The number of Tx and Rx buffers.  These are allocated from the page * pool.  The code may assume these are power of two, so it it best * to keep them that size. * We don't need to allocate pages for the transmitter.  We just use * the skbuffer directly. */#if 1#define FEC_ENET_RX_PAGES	4#define FEC_ENET_RX_FRSIZE	2048#define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)#define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)#define TX_RING_SIZE		8	/* Must be power of two */#define TX_RING_MOD_MASK	7	/*   for this to work */#else#define FEC_ENET_RX_PAGES	16#define FEC_ENET_RX_FRSIZE	2048#define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)#define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)#define TX_RING_SIZE		16	/* Must be power of two */#define TX_RING_MOD_MASK	15	/*   for this to work */#endif/* Interrupt events/masks.*/#define FEC_ENET_HBERR	((uint)0x80000000)	/* Heartbeat error */#define FEC_ENET_BABR	((uint)0x40000000)	/* Babbling receiver */#define FEC_ENET_BABT	((uint)0x20000000)	/* Babbling transmitter */#define FEC_ENET_GRA	((uint)0x10000000)	/* Graceful stop complete */#define FEC_ENET_TXF	((uint)0x08000000)	/* Full frame transmitted */#define FEC_ENET_TXB	((uint)0x04000000)	/* A buffer was transmitted */#define FEC_ENET_RXF	((uint)0x02000000)	/* Full frame received */#define FEC_ENET_RXB	((uint)0x01000000)	/* A buffer was received */#define FEC_ENET_MII	((uint)0x00800000)	/* MII interrupt */#define FEC_ENET_EBERR	((uint)0x00400000)	/* SDMA bus error *//* The FEC stores dest/src/type, data, and checksum for receive packets. */#define PKT_MAXBUF_SIZE		1518#define PKT_MINBUF_SIZE		64#define PKT_MAXBLR_SIZE		1520/* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and * tx_bd_base always point to the base of the buffer descriptors.  The * cur_rx and cur_tx point to the currently available buffer. * The dirty_tx tracks the current buffer that is being sent by the * controller.  The cur_tx and dirty_tx are equal under both completely * empty and completely full conditions.  The empty/ready indicator in * the buffer descriptor determines the actual condition. */struct fec_enet_private {	/* The saved address of a sent-in-place packet/buffer, for skfree(). */	struct	sk_buff* tx_skbuff[TX_RING_SIZE];	ushort	skb_cur;	ushort	skb_dirty;	/* CPM dual port RAM relative addresses.	*/	cbd_t	*rx_bd_base;		/* Address of Rx and Tx buffers. */	cbd_t	*tx_bd_base;	cbd_t	*cur_rx, *cur_tx;		/* The next free ring entry */	cbd_t	*dirty_tx;	/* The ring entries to be free()ed. */	scc_t	*sccp;	struct	net_device_stats stats;	uint	tx_full;	spinlock_t lock;	uint	phy_id;	uint	phy_id_done;	uint	phy_status;	uint	phy_speed;	phy_info_t	*phy;	struct tq_struct phy_task;	uint	sequence_done;	uint	phy_addr;	int	link;	int	old_link;	int	full_duplex;#ifdef CONFIG_FEC_PACKETHOOK	unsigned long	ph_lock;	fec_ph_func	*ph_rxhandler;	fec_ph_func	*ph_txhandler;	__u16		ph_proto;	volatile __u32	*ph_regaddr;	void 		*ph_priv;#endif};static int fec_enet_open(struct net_device *dev);static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);static void fec_enet_mii(struct net_device *dev);static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);#ifdef CONFIG_FEC_PACKETHOOKstatic void  fec_enet_tx(struct net_device *dev, __u32 regval);static void  fec_enet_rx(struct net_device *dev, __u32 regval);#elsestatic void  fec_enet_tx(struct net_device *dev);static void  fec_enet_rx(struct net_device *dev);#endifstatic int fec_enet_close(struct net_device *dev);static struct net_device_stats *fec_enet_get_stats(struct net_device *dev);static void set_multicast_list(struct net_device *dev);static void fec_restart(struct net_device *dev, int duplex);static void fec_stop(struct net_device *dev);static	ushort	my_enet_addr[3];/* MII processing.  We keep this as simple as possible.  Requests are * placed on the list (if there is room).  When the request is finished * by the MII, an optional function may be called. */typedef struct mii_list {	uint	mii_regval;	void	(*mii_func)(uint val, struct net_device *dev);	struct	mii_list *mii_next;} mii_list_t;#define		NMII	20mii_list_t	mii_cmds[NMII];mii_list_t	*mii_free;mii_list_t	*mii_head;mii_list_t	*mii_tail;static int	mii_queue(struct net_device *dev, int request, 				void (*func)(uint, struct net_device *));/* Make MII read/write commands for the FEC.*/#define mk_mii_read(REG)	(0x60020000 | ((REG & 0x1f) << 18))#define mk_mii_write(REG, VAL)	(0x50020000 | ((REG & 0x1f) << 18) | \						(VAL & 0xffff))#define mk_mii_end	0/* Transmitter timeout.*/#define TX_TIMEOUT (2*HZ)/* Register definitions for the PHY.*/#define MII_REG_CR          0  /* Control Register                         */#define MII_REG_SR          1  /* Status Register                          */#define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */#define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */#define MII_REG_ANAR        4  /* A-N Advertisement Register               */ #define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */#define MII_REG_ANER        6  /* A-N Expansion Register                   */#define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */#define MII_REG_ANLPRNPR    8  /* A-N Link Partner Received Next Page Reg. *//* values for phy_status */#define PHY_CONF_ANE	0x0001  /* 1 auto-negotiation enabled */#define PHY_CONF_LOOP	0x0002  /* 1 loopback mode enabled */#define PHY_CONF_SPMASK	0x00f0  /* mask for speed */#define PHY_CONF_10HDX	0x0010  /* 10 Mbit half duplex supported */#define PHY_CONF_10FDX	0x0020  /* 10 Mbit full duplex supported */ #define PHY_CONF_100HDX	0x0040  /* 100 Mbit half duplex supported */#define PHY_CONF_100FDX	0x0080  /* 100 Mbit full duplex supported */ #define PHY_STAT_LINK	0x0100  /* 1 up - 0 down */#define PHY_STAT_FAULT	0x0200  /* 1 remote fault */#define PHY_STAT_ANC	0x0400  /* 1 auto-negotiation complete	*/#define PHY_STAT_SPMASK	0xf000  /* mask for speed */#define PHY_STAT_10HDX	0x1000  /* 10 Mbit half duplex selected	*/#define PHY_STAT_10FDX	0x2000  /* 10 Mbit full duplex selected	*/ #define PHY_STAT_100HDX	0x4000  /* 100 Mbit half duplex selected */#define PHY_STAT_100FDX	0x8000  /* 100 Mbit full duplex selected */ #ifdef CONFIG_FEC_PACKETHOOKintfec_register_ph(struct net_device *dev, fec_ph_func *rxfun, fec_ph_func *txfun,		__u16 proto, volatile __u32 *regaddr, void *priv){	struct fec_enet_private *fep;	int retval = 0;	fep = dev->priv;	if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) {		/* Someone is messing with the packet hook */		return -EAGAIN;	}	if (fep->ph_rxhandler != NULL || fep->ph_txhandler != NULL) {		retval = -EBUSY;		goto out;	}	fep->ph_rxhandler = rxfun;	fep->ph_txhandler = txfun;	fep->ph_proto = proto;	fep->ph_regaddr = regaddr;	fep->ph_priv = priv;	out:	fep->ph_lock = 0;	return retval;}intfec_unregister_ph(struct net_device *dev){	struct fec_enet_private *fep;	int retval = 0;	fep = dev->priv;	if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) {		/* Someone is messing with the packet hook */		return -EAGAIN;	}	fep->ph_rxhandler = fep->ph_txhandler = NULL;	fep->ph_proto = 0;	fep->ph_regaddr = NULL;	fep->ph_priv = NULL;		fep->ph_lock = 0;	return retval;}EXPORT_SYMBOL(fec_register_ph);EXPORT_SYMBOL(fec_unregister_ph);#endif /* CONFIG_FEC_PACKETHOOK */static intfec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct fec_enet_private *fep;	volatile fec_t	*fecp;	volatile cbd_t	*bdp;	fep = dev->priv;	fecp = (volatile fec_t*)dev->base_addr;	if (!fep->link) {		/* Link is down or autonegotiation is in progress. */		return 1;	}	/* Fill in a Tx ring entry */	bdp = fep->cur_tx;#ifndef final_version	if (bdp->cbd_sc & BD_ENET_TX_READY) {		/* Ooops.  All transmit buffers are full.  Bail out.		 * This should not happen, since dev->tbusy should be set.		 */		printk("%s: tx queue full!.\n", dev->name);		return 1;	}#endif	/* Clear all of the status flags.	 */	bdp->cbd_sc &= ~BD_ENET_TX_STATS;	/* Set buffer length and buffer pointer.	*/	bdp->cbd_bufaddr = __pa(skb->data);	bdp->cbd_datlen = skb->len;	/* Save skb pointer.	*/	fep->tx_skbuff[fep->skb_cur] = skb;	fep->stats.tx_bytes += skb->len;	fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK;		/* Push the data cache so the CPM does not get stale memory	 * data.	 */	flush_dcache_range((unsigned long)skb->data,			   (unsigned long)skb->data + skb->len);	spin_lock_irq(&fep->lock);	/* Send it on its way.  Tell FEC its ready, interrupt when done,	 * its the last BD of the frame, and to put the CRC on the end.	 */	bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR			| BD_ENET_TX_LAST | BD_ENET_TX_TC);	dev->trans_start = jiffies;	/* Trigger transmission start */	fecp->fec_x_des_active = 0x01000000;	/* If this was the last BD in the ring, start at the beginning again.	*/	if (bdp->cbd_sc & BD_ENET_TX_WRAP) {		bdp = fep->tx_bd_base;	} else {		bdp++;	}	if (bdp->cbd_sc & BD_ENET_TX_READY)		netif_stop_queue(dev);	fep->cur_tx = (cbd_t *)bdp;	spin_unlock_irq(&fep->lock);	return 0;}static voidfec_timeout(struct net_device *dev){	struct fec_enet_private *fep = dev->priv;	printk("%s: transmit timed out.\n", dev->name);	fep->stats.tx_errors++;#ifndef final_version	{	int	i;	cbd_t	*bdp;	printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n",	       (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "",	       (unsigned long)fep->dirty_tx,	       (unsigned long)fep->cur_rx);	bdp = fep->tx_bd_base;	printk(" tx: %u buffers\n",  TX_RING_SIZE);	for (i = 0 ; i < TX_RING_SIZE; i++) {		printk("  %08x: %04x %04x %08x\n", 		       (uint) bdp,		       bdp->cbd_sc,		       bdp->cbd_datlen,		       bdp->cbd_bufaddr);		bdp++;	}	bdp = fep->rx_bd_base;	printk(" rx: %lu buffers\n",  RX_RING_SIZE);	for (i = 0 ; i < RX_RING_SIZE; i++) {		printk("  %08x: %04x %04x %08x\n",		       (uint) bdp,		       bdp->cbd_sc,		       bdp->cbd_datlen,		       bdp->cbd_bufaddr);		bdp++;	}	}#endif	if (!fep->tx_full)		netif_wake_queue(dev);}/* The interrupt handler. * This is called from the MPC core interrupt. */static	voidfec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs){	struct	net_device *dev = dev_id;	volatile fec_t	*fecp;	uint	int_events;#ifdef CONFIG_FEC_PACKETHOOK	struct	fec_enet_private *fep = dev->priv;	__u32 regval;	if (fep->ph_regaddr) regval = *fep->ph_regaddr;#endif	fecp = (volatile fec_t*)dev->base_addr;	/* Get the interrupt events that caused us to be here.	*/	while ((int_events = fecp->fec_ievent) != 0) {		fecp->fec_ievent = int_events;		if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR |				   FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) {			printk("FEC ERROR %x\n", int_events);		}		/* Handle receive event in its own function.		 */		if (int_events & FEC_ENET_RXF) {#ifdef CONFIG_FEC_PACKETHOOK			fec_enet_rx(dev, regval);#else			fec_enet_rx(dev);#endif		}		/* Transmit OK, or non-fatal error. Update the buffer		   descriptors. FEC handles all errors, we just discover		   them as part of the transmit process.		*/		if (int_events & FEC_ENET_TXF) {#ifdef CONFIG_FEC_PACKETHOOK			fec_enet_tx(dev, regval);#else			fec_enet_tx(dev);#endif		}		if (int_events & FEC_ENET_MII) {			fec_enet_mii(dev);		}		}}static void#ifdef CONFIG_FEC_PACKETHOOKfec_enet_tx(struct net_device *dev, __u32 regval)#elsefec_enet_tx(struct net_device *dev)#endif{	struct	fec_enet_private *fep;	volatile cbd_t	*bdp;	struct	sk_buff	*skb;	fep = dev->priv;	spin_lock(&fep->lock);	bdp = fep->dirty_tx;	while ((bdp->cbd_sc&BD_ENET_TX_READY) == 0) {		if (bdp == fep->cur_tx && fep->tx_full == 0) break;		skb = fep->tx_skbuff[fep->skb_dirty];		/* Check for errors. */		if (bdp->cbd_sc & (BD_ENET_TX_HB | BD_ENET_TX_LC |				   BD_ENET_TX_RL | BD_ENET_TX_UN |				   BD_ENET_TX_CSL)) {			fep->stats.tx_errors++;			if (bdp->cbd_sc & BD_ENET_TX_HB)  /* No heartbeat */				fep->stats.tx_heartbeat_errors++;			if (bdp->cbd_sc & BD_ENET_TX_LC)  /* Late collision */				fep->stats.tx_window_errors++;			if (bdp->cbd_sc & BD_ENET_TX_RL)  /* Retrans limit */				fep->stats.tx_aborted_errors++;			if (bdp->cbd_sc & BD_ENET_TX_UN)  /* Underrun */				fep->stats.tx_fifo_errors++;			if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */				fep->stats.tx_carrier_errors++;		} else {#ifdef CONFIG_FEC_PACKETHOOK			/* Packet hook ... */			if (fep->ph_txhandler &&			    ((struct ethhdr *)skb->data)->h_proto			    == fep->ph_proto) {				fep->ph_txhandler((__u8*)skb->data, skb->len,						  regval, fep->ph_priv);			}#endif			fep->stats.tx_packets++;		}#ifndef final_version		if (bdp->cbd_sc & BD_ENET_TX_READY)			printk("HEY! Enet xmit interrupt and TX_READY.\n");#endif		/* Deferred means some collisions occurred during transmit,		 * but we eventually sent the packet OK.		 */		if (bdp->cbd_sc & BD_ENET_TX_DEF)			fep->stats.collisions++;	    		/* Free the sk buffer associated with this last transmit.		 */#if 0printk("TXI: %x %x %x\n", bdp, skb, fep->skb_dirty);#endif		dev_kfree_skb(skb/*, FREE_WRITE*/);		fep->tx_skbuff[fep->skb_dirty] = NULL;		fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK;	    		/* Update pointer to next buffer descriptor to be transmitted.		 */		if (bdp->cbd_sc & BD_ENET_TX_WRAP)			bdp = fep->tx_bd_base;		else			bdp++;	    		/* Since we have freed up a buffer, the ring is no longer		 * full.		 */		if (fep->tx_full) {			fep->tx_full = 0;			if (netif_queue_stopped(dev))				netif_wake_queue(dev);		}#ifdef CONFIG_FEC_PACKETHOOK		/* Re-read register. Not exactly guaranteed to be correct,		   but... */		if (fep->ph_regaddr) regval = *fep->ph_regaddr;#endif	}	fep->dirty_tx = (cbd_t *)bdp;	spin_unlock(&fep->lock);}/* During a receive, the cur_rx points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has * not been given to the system, we just set the empty indicator, * effectively tossing the packet. */static void#ifdef CONFIG_FEC_PACKETHOOKfec_enet_rx(struct net_device *dev, __u32 regval)#elsefec_enet_rx(struct net_device *dev)#endif{	struct	fec_enet_private *fep;	volatile fec_t	*fecp;	volatile cbd_t *bdp;	struct	sk_buff	*skb;	ushort	pkt_len;	__u8 *data;	fep = dev->priv;	fecp = (volatile fec_t*)dev->base_addr;

⌨️ 快捷键说明

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