fcc_enet.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 1,911 行 · 第 1/4 页

C
1,911
字号
/* * BK Id: %F% %I% %G% %U% %#% *//* * 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.  This version has some additional * configuration options, which should probably be moved out of * here.  This driver currently works for the EST SBC8260, * SBS Diablo/BCM, Embedded Planet RPX6, TQM8260, and others. * * 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.  Since this is a cache coherent processor and CPM, * I could also preallocate SKB's and use them directly on the interface. * */#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)#ifdef	CONFIG_USE_MDIO/* 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;/* 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 */#endif	/* CONFIG_USE_MDIO *//* 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_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 fcc_restart(struct net_device *dev, int duplex);static int fcc_enet_set_mac_address(struct net_device *dev, void *addr);/* 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.*/#ifdef	CONFIG_TQM8260/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */#define PC_MDIO		((uint)0x00000002)#define PC_MDCK		((uint)0x00000001)#else#define PC_MDIO		((uint)0x00000004)#define PC_MDCK		((uint)0x00000020)#endif/* 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,# if defined(CONFIG_TQM8260)		PC_MDIO, PC_MDCK },# else		0x00000004, 0x00000100 },# endif#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,# if defined(CONFIG_TQM8260)		PC_MDIO, PC_MDCK },# elif defined(CONFIG_EST8260) || defined(CONFIG_ADS8260)		0x00400000, 0x00200000 },# else		0x00000002, 0x00000080 },# endif#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,# if defined(CONFIG_TQM8260)		PC_MDIO, PC_MDCK },# else		0x00000001, 0x00000040 },# endif#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;#ifdef	CONFIG_USE_MDIO	uint	phy_id;	uint	phy_id_done;	uint	phy_status;	phy_info_t	*phy;	struct tq_struct phy_task;	uint	sequence_done;	uint	phy_addr;#endif	/* CONFIG_USE_MDIO */	int	link;	int	old_link;	int	full_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);#ifdef	CONFIG_USE_MDIOstatic int	mii_queue(struct net_device *dev, int request, void (*func)(uint, struct net_device *));static uint	mii_send_receive(fcc_info_t *fip, uint cmd);static void	fcc_stop(struct net_device *dev);/* Make MII read/write commands for the FCC.*/#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#endif	/* CONFIG_USE_MDIO */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;	if (!cep->link) {		/* Link is down or autonegotiation is in progress. */		return 1;	}	/* 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);

⌨️ 快捷键说明

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