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

📄 fcc_enet.c

📁 自己根据lkd和情境分析
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * BK Id: SCCS/s.fcc_enet.c 1.7 05/17/01 18:14:20 cort *//* * Fast Ethernet Controller (FCC) driver for Motorola MPC8260. * Copyright (c) 2000 MontaVista Software, Inc.   Dan Malek (dmalek@jlc.net) * * This version of the driver is a combination of the 8xx fec and * 8260 SCC Ethernet drivers.  People seem to be choosing common I/O * configurations, so this driver will work on the EST8260 boards and * others yet to be announced. * * 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. * */#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/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 <asm/immap_8260.h>#include <asm/pgtable.h>#include <asm/mpc8260.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include <asm/cpm_8260.h>/* The transmitter timeout */#define TX_TIMEOUT	(2*HZ)/* 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 is best * to keep them that size. * We don't need to allocate pages for the transmitter.  We just use * the skbuffer directly. */#define FCC_ENET_RX_PAGES	16#define FCC_ENET_RX_FRSIZE	2048#define FCC_ENET_RX_FRPPG	(PAGE_SIZE / FCC_ENET_RX_FRSIZE)#define RX_RING_SIZE		(FCC_ENET_RX_FRPPG * FCC_ENET_RX_PAGES)#define TX_RING_SIZE		16	/* Must be power of two */#define TX_RING_MOD_MASK	15	/*   for this to work *//* The FCC stores dest/src/type, data, and checksum for receive packets. */#define PKT_MAXBUF_SIZE		1518#define PKT_MINBUF_SIZE		64/* Maximum input DMA size.  Must be a should(?) be a multiple of 4.*/#define PKT_MAXDMA_SIZE		1520/* Maximum input buffer size.  Must be a multiple of 32.*/#define PKT_MAXBLR_SIZE		1536static int fcc_enet_open(struct net_device *dev);static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);static int fcc_enet_rx(struct net_device *dev);static void fcc_enet_mii(struct net_device *dev);static	void fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);static int fcc_enet_close(struct net_device *dev);static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev);static void set_multicast_list(struct net_device *dev);static void restart_fcc(struct net_device *dev);/* These will be configurable for the FCC choice. * Multiple ports can be configured.  There is little choice among the * I/O pins to the PHY, except the clocks.  We will need some board * dependent clock selection. * Why in the hell did I put these inside #ifdef's?  I dunno, maybe to * help show what pins are used for each device. *//* I/O Pin assignment for FCC1.  I don't yet know the best way to do this, * but there is little variation among the choices. */#define PA1_COL		((uint)0x00000001)#define PA1_CRS		((uint)0x00000002)#define PA1_TXER	((uint)0x00000004)#define PA1_TXEN	((uint)0x00000008)#define PA1_RXDV	((uint)0x00000010)#define PA1_RXER	((uint)0x00000020)#define PA1_TXDAT	((uint)0x00003c00)#define PA1_RXDAT	((uint)0x0003c000)#define PA1_PSORA0	(PA1_RXDAT | PA1_TXDAT)#define PA1_PSORA1	(PA1_COL | PA1_CRS | PA1_TXER | PA1_TXEN | \				PA1_RXDV | PA1_RXER)#define PA1_DIRA0	(PA1_RXDAT | PA1_CRS | PA1_COL | PA1_RXER | PA1_RXDV)#define PA1_DIRA1	(PA1_TXDAT | PA1_TXEN | PA1_TXER)/* CLK12 is receive, CLK11 is transmit.  These are board specific.*/#define PC_F1RXCLK	((uint)0x00000800)#define PC_F1TXCLK	((uint)0x00000400)#define CMX1_CLK_ROUTE	((uint)0x3e000000)#define CMX1_CLK_MASK	((uint)0xff000000)/* I/O Pin assignment for FCC2.  I don't yet know the best way to do this, * but there is little variation among the choices. */#define PB2_TXER	((uint)0x00000001)#define PB2_RXDV	((uint)0x00000002)#define PB2_TXEN	((uint)0x00000004)#define PB2_RXER	((uint)0x00000008)#define PB2_COL		((uint)0x00000010)#define PB2_CRS		((uint)0x00000020)#define PB2_TXDAT	((uint)0x000003c0)#define PB2_RXDAT	((uint)0x00003c00)#define PB2_PSORB0	(PB2_RXDAT | PB2_TXDAT | PB2_CRS | PB2_COL | \				PB2_RXER | PB2_RXDV | PB2_TXER)#define PB2_PSORB1	(PB2_TXEN)#define PB2_DIRB0	(PB2_RXDAT | PB2_CRS | PB2_COL | PB2_RXER | PB2_RXDV)#define PB2_DIRB1	(PB2_TXDAT | PB2_TXEN | PB2_TXER)/* CLK13 is receive, CLK14 is transmit.  These are board dependent.*/#define PC_F2RXCLK	((uint)0x00001000)#define PC_F2TXCLK	((uint)0x00002000)#define CMX2_CLK_ROUTE	((uint)0x00250000)#define CMX2_CLK_MASK	((uint)0x00ff0000)/* I/O Pin assignment for FCC3.  I don't yet know the best way to do this, * but there is little variation among the choices. */#define PB3_RXDV	((uint)0x00004000)#define PB3_RXER	((uint)0x00008000)#define PB3_TXER	((uint)0x00010000)#define PB3_TXEN	((uint)0x00020000)#define PB3_COL		((uint)0x00040000)#define PB3_CRS		((uint)0x00080000)#define PB3_TXDAT	((uint)0x0f000000)#define PB3_RXDAT	((uint)0x00f00000)#define PB3_PSORB0	(PB3_RXDAT | PB3_TXDAT | PB3_CRS | PB3_COL | \				PB3_RXER | PB3_RXDV | PB3_TXER | PB3_TXEN)#define PB3_PSORB1	(0)#define PB3_DIRB0	(PB3_RXDAT | PB3_CRS | PB3_COL | PB3_RXER | PB3_RXDV)#define PB3_DIRB1	(PB3_TXDAT | PB3_TXEN | PB3_TXER)/* CLK15 is receive, CLK16 is transmit.  These are board dependent.*/#define PC_F3RXCLK	((uint)0x00004000)#define PC_F3TXCLK	((uint)0x00008000)#define CMX3_CLK_ROUTE	((uint)0x00003700)#define CMX3_CLK_MASK	((uint)0x0000ff00)/* MII status/control serial interface.*/#define PC_MDIO		((uint)0x00400000)#define PC_MDCK		((uint)0x00200000)/* A table of information for supporting FCCs.  This does two things. * First, we know how many FCCs we have and they are always externally * numbered from zero.  Second, it holds control register and I/O * information that could be different among board designs. */typedef struct fcc_info {	uint	fc_fccnum;	uint	fc_cpmblock;	uint	fc_cpmpage;	uint	fc_proff;	uint	fc_interrupt;	uint	fc_trxclocks;	uint	fc_clockroute;	uint	fc_clockmask;	uint	fc_mdio;	uint	fc_mdck;} fcc_info_t;static fcc_info_t fcc_ports[] = {#ifdef CONFIG_FCC1_ENET	{ 0, CPM_CR_FCC1_SBLOCK, CPM_CR_FCC1_PAGE, PROFF_FCC1, SIU_INT_FCC1,		(PC_F1RXCLK | PC_F1TXCLK), CMX1_CLK_ROUTE, CMX1_CLK_MASK,		PC_MDIO, PC_MDCK },#endif#ifdef CONFIG_FCC2_ENET	{ 1, CPM_CR_FCC2_SBLOCK, CPM_CR_FCC2_PAGE, PROFF_FCC2, SIU_INT_FCC2,		(PC_F2RXCLK | PC_F2TXCLK), CMX2_CLK_ROUTE, CMX2_CLK_MASK,		PC_MDIO, PC_MDCK },#endif#ifdef CONFIG_FCC3_ENET	{ 2, CPM_CR_FCC3_SBLOCK, CPM_CR_FCC3_PAGE, PROFF_FCC3, SIU_INT_FCC3,		(PC_F3RXCLK | PC_F3TXCLK), CMX3_CLK_ROUTE, CMX3_CLK_MASK,		PC_MDIO, PC_MDCK },#endif};/* The FCC 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 fcc_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. */	volatile fcc_t	*fccp;	volatile fcc_enet_t	*ep;	struct	net_device_stats stats;	uint	tx_full;	spinlock_t lock;	uint	phy_address;	uint	phy_type;	uint	phy_duplex;	fcc_info_t	*fip;};static void init_fcc_shutdown(fcc_info_t *fip, struct fcc_enet_private *cep,	volatile immap_t *immap);static void init_fcc_startup(fcc_info_t *fip, struct net_device *dev);static void init_fcc_ioports(fcc_info_t *fip, volatile iop8260_t *io,	volatile immap_t *immap);static void init_fcc_param(fcc_info_t *fip, struct net_device *dev,	volatile immap_t *immap);/* 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	phyaddr;static	uint	phytype;static int	mii_queue(int request, void (*func)(uint, struct net_device *));static void	mii_startup_cmds(void);static uint	mii_send_receive(fcc_info_t *fip, uint cmd);/* Make MII read/write commands for the FCC.*/#define mk_mii_phyaddr(ADDR)	(0x60020000 | ((ADDR) << 23) | (2 << 18))#define mk_mii_read(REG)	(0x60020000 | ((phyaddr << 23) | \						(REG & 0x1f) << 18))#define mk_mii_write(REG, VAL)	(0x50020000 | ((phyaddr << 23) | \						(REG & 0x1f) << 18) | \						(VAL & 0xffff))static intfcc_enet_open(struct net_device *dev){	netif_start_queue(dev);	return 0;					/* Always succeed */}static intfcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv;	volatile cbd_t	*bdp;	/* Fill in a Tx ring entry */	bdp = cep->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 cep->tx_full 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;	/* If the frame is short, tell CPM to pad it.	*/	if (skb->len <= ETH_ZLEN)		bdp->cbd_sc |= BD_ENET_TX_PAD;	else		bdp->cbd_sc &= ~BD_ENET_TX_PAD;	/* Set buffer length and buffer pointer.	*/	bdp->cbd_datlen = skb->len;	bdp->cbd_bufaddr = __pa(skb->data);	/* Save skb pointer.	*/	cep->tx_skbuff[cep->skb_cur] = skb;	cep->stats.tx_bytes += skb->len;	cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;	spin_lock_irq(&cep->lock);	/* Send it on its way.  Tell CPM 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);#if 0	/* Errata says don't do this.	*/	cep->fccp->fcc_ftodr = 0x8000;#endif	dev->trans_start = jiffies;	/* If this was the last BD in the ring, start at the beginning again.	*/	if (bdp->cbd_sc & BD_ENET_TX_WRAP)		bdp = cep->tx_bd_base;	else		bdp++;	if (bdp->cbd_sc & BD_ENET_TX_READY) {		netif_stop_queue(dev);		cep->tx_full = 1;	}	cep->cur_tx = (cbd_t *)bdp;	spin_unlock_irq(&cep->lock);	return 0;}static voidfcc_enet_timeout(struct net_device *dev){	struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv;	printk("%s: transmit timed out.\n", dev->name);	cep->stats.tx_errors++;#ifndef final_version	{		int	i;		cbd_t	*bdp;		printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n",		       cep->cur_tx, cep->tx_full ? " (full)" : "",		       cep->cur_rx);		bdp = cep->tx_bd_base;		printk(" Tx @base %p :\n", bdp);		for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->cbd_sc,			       bdp->cbd_datlen,			       bdp->cbd_bufaddr);		bdp = cep->rx_bd_base;		printk(" Rx @base %p :\n", bdp);		for (i = 0 ; i < RX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->cbd_sc,			       bdp->cbd_datlen,			       bdp->cbd_bufaddr);	}#endif	if (!cep->tx_full)		netif_wake_queue(dev);}/* The interrupt handler. */static voidfcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs){	struct	net_device *dev = dev_id;	volatile struct	fcc_enet_private *cep;	volatile cbd_t	*bdp;	ushort	int_events;	int	must_restart;	cep = (struct fcc_enet_private *)dev->priv;	/* Get the interrupt events that caused us to be here.	*/	int_events = cep->fccp->fcc_fcce;	cep->fccp->fcc_fcce = int_events;	must_restart = 0;	/* Handle receive event in its own function.	*/	if (int_events & FCC_ENET_RXF)		fcc_enet_rx(dev_id);	/* Check for a transmit error.  The manual is a little unclear	 * about this, so the debug code until I get it figured out.  It	 * appears that if TXE is set, then TXB is not set.  However,	 * if carrier sense is lost during frame transmission, the TXE	 * bit is set, "and continues the buffer transmission normally."	 * I don't know if "normally" implies TXB is set when the buffer	 * descriptor is closed.....trial and error :-).	 */	/* Transmit OK, or non-fatal error.  Update the buffer descriptors.	*/	if (int_events & (FCC_ENET_TXE | FCC_ENET_TXB)) {	    spin_lock(&cep->lock);	    bdp = cep->dirty_tx;	    while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) {		if ((bdp==cep->cur_tx) && (cep->tx_full == 0))		    break;		if (bdp->cbd_sc & BD_ENET_TX_HB)	/* No heartbeat */			cep->stats.tx_heartbeat_errors++;		if (bdp->cbd_sc & BD_ENET_TX_LC)	/* Late collision */			cep->stats.tx_window_errors++;		if (bdp->cbd_sc & BD_ENET_TX_RL)	/* Retrans limit */			cep->stats.tx_aborted_errors++;		if (bdp->cbd_sc & BD_ENET_TX_UN)	/* Underrun */			cep->stats.tx_fifo_errors++;		if (bdp->cbd_sc & BD_ENET_TX_CSL)	/* Carrier lost */			cep->stats.tx_carrier_errors++;		/* No heartbeat or Lost carrier are not really bad errors.		 * The others require a restart transmit command.		 */		if (bdp->cbd_sc &		    (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) {			must_restart = 1;			cep->stats.tx_errors++;		}		cep->stats.tx_packets++;		/* Deferred means some collisions occurred during transmit,		 * but we eventually sent the packet OK.		 */		if (bdp->cbd_sc & BD_ENET_TX_DEF)			cep->stats.collisions++;		/* Free the sk buffer associated with this last transmit.		*/		dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]);		cep->skb_dirty = (cep->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 = cep->tx_bd_base;		else			bdp++;		/* I don't know if we can be held off from processing these		 * interrupts for more than one frame time.  I really hope		 * not.  In such a case, we would now want to check the		 * currently available BD (cur_tx) and determine if any		 * buffers between the dirty_tx and cur_tx have also been		 * sent.  We would want to process anything in between that		 * does not have BD_ENET_TX_READY set.		 */		/* Since we have freed up a buffer, the ring is no longer		 * full.		 */		if (cep->tx_full) {			cep->tx_full = 0;			if (netif_queue_stopped(dev)) {				netif_wake_queue(dev);			}		}		cep->dirty_tx = (cbd_t *)bdp;	    }	    if (must_restart) {		volatile cpm8260_t *cp;		/* Some transmit errors cause the transmitter to shut		 * down.  We now issue a restart transmit.  Since the		 * errors close the BD and update the pointers, the restart		 * _should_ pick up without having to reset any of our		 * pointers either.		 */		cp = cpmp;		cp->cp_cpcr =		    mk_cr_cmd(cep->fip->fc_cpmpage, cep->fip->fc_cpmblock,		    		0x0c, CPM_CR_RESTART_TX) | CPM_CR_FLG;		while (cp->cp_cpcr & CPM_CR_FLG);	    }	    spin_unlock(&cep->lock);	}	/* Check for receive busy, i.e. packets coming but no place to	 * put them.	 */	if (int_events & FCC_ENET_BSY) {		cep->stats.rx_dropped++;	}	return;}/* 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,

⌨️ 快捷键说明

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