4xx_enet.c

来自「uboot详细解读可用启动引导LINUX2.6内核」· C语言 代码 · 共 2,100 行 · 第 1/4 页

C
2,100
字号
/*-----------------------------------------------------------------------------+ * *	 This source code has been made available to you by IBM on an AS-IS *	 basis.	 Anyone receiving this source is licensed under IBM *	 copyrights to use it in any way he or she deems fit, including *	 copying it, modifying it, compiling it, and redistributing it either *	 with or without modifications.	 No license under IBM patents or *	 patent applications is to be implied by the copyright license. * *	 Any user of this software should understand that IBM cannot provide *	 technical support for this software and will not be responsible for *	 any consequences resulting from the use of this software. * *	 Any person who transfers this source code or any derivative work *	 must include the IBM copyright notice, this paragraph, and the *	 preceding two paragraphs in the transferred software. * *	 COPYRIGHT   I B M   CORPORATION 1995 *	 LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M *-----------------------------------------------------------------------------*//*-----------------------------------------------------------------------------+ * *  File Name:	enetemac.c * *  Function:	Device driver for the ethernet EMAC3 macro on the 405GP. * *  Author:	Mark Wisner * *  Change Activity- * *  Date	Description of Change					    BY *  ---------	---------------------					    --- *  05-May-99	Created							    MKW *  27-Jun-99	Clean up						    JWB *  16-Jul-99	Added MAL error recovery and better IP packet handling	    MKW *  29-Jul-99	Added Full duplex support				    MKW *  06-Aug-99	Changed names for Mal CR reg				    MKW *  23-Aug-99	Turned off SYE when running at 10Mbs			    MKW *  24-Aug-99	Marked descriptor empty after call_xlc			    MKW *  07-Sep-99	Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16	    MCG *		to avoid chaining maximum sized packets. Push starting *		RX descriptor address up to the next cache line boundary. *  16-Jan-00	Added support for booting with IP of 0x0		    MKW *  15-Mar-00	Updated enetInit() to enable broadcast addresses in the *		EMAC_RXM register.					    JWB *  12-Mar-01	anne-sophie.harnois@nextream.fr *		 - Variables are compatible with those already defined in *		  include/net.h *		- Receive buffer descriptor ring is used to send buffers *		  to the user *		- Info print about send/received/handled packet number if *		  INFO_405_ENET is set *  17-Apr-01	stefan.roese@esd-electronics.com *		- MAL reset in "eth_halt" included *		- Enet speed and duplex output now in one line *  08-May-01	stefan.roese@esd-electronics.com *		- MAL error handling added (eth_init called again) *  13-Nov-01	stefan.roese@esd-electronics.com *		- Set IST bit in EMAC_M1 reg upon 100MBit or full duplex *  04-Jan-02	stefan.roese@esd-electronics.com *		- Wait for PHY auto negotiation to complete added *  06-Feb-02	stefan.roese@esd-electronics.com *		- Bug fixed in waiting for auto negotiation to complete *  26-Feb-02	stefan.roese@esd-electronics.com *		- rx and tx buffer descriptors now allocated (no fixed address *		  used anymore) *  17-Jun-02	stefan.roese@esd-electronics.com *		- MAL error debug printf 'M' removed (rx de interrupt may *		  occur upon many incoming packets with only 4 rx buffers). *-----------------------------------------------------------------------------* *  17-Nov-03	travis.sawyer@sandburst.com *		- ported from 405gp_enet.c to utilized upto 4 EMAC ports *		  in the 440GX.	 This port should work with the 440GP *		  (2 EMACs) also *  15-Aug-05	sr@denx.de *		- merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c		  now handling all 4xx cpu's. *-----------------------------------------------------------------------------*/#include <config.h>#include <common.h>#include <net.h>#include <asm/processor.h>#include <asm/io.h>#include <asm/cache.h>#include <asm/mmu.h>#include <commproc.h>#include <ppc4xx.h>#include <ppc4xx_enet.h>#include <405_mal.h>#include <miiphy.h>#include <malloc.h>#include <asm/ppc4xx-intvec.h>/* * Only compile for platform with AMCC EMAC ethernet controller and * network support enabled. * Remark: CONFIG_405 describes Xilinx PPC405 FPGA without EMAC controller! */#if defined(CONFIG_CMD_NET) && !defined(CONFIG_405) && !defined(CONFIG_IOP480)#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII))#error "CONFIG_MII has to be defined!"#endif#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_NET_MULTI)#error "CONFIG_NET_MULTI has to be defined for NetConsole"#endif#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */#define PHY_AUTONEGOTIATE_TIMEOUT 5000	/* 5000 ms autonegotiate timeout *//* Ethernet Transmit and Receive Buffers *//* AS.HARNOIS * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from * PKTSIZE and PKTSIZE_ALIGN (include/net.h) */#define ENET_MAX_MTU	       PKTSIZE#define ENET_MAX_MTU_ALIGNED   PKTSIZE_ALIGN/*-----------------------------------------------------------------------------+ * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal * Interrupt Controller). *-----------------------------------------------------------------------------*/#define MAL_UIC_ERR ( UIC_MAL_SERR | UIC_MAL_TXDE  | UIC_MAL_RXDE)#define MAL_UIC_DEF  (UIC_MAL_RXEOB | MAL_UIC_ERR)#define EMAC_UIC_DEF UIC_ENET#define EMAC_UIC_DEF1 UIC_ENET1#define SEL_UIC_DEF(p) (p ? UIC_ENET1 : UIC_ENET )#undef INFO_4XX_ENET#define BI_PHYMODE_NONE	 0#define BI_PHYMODE_ZMII	 1#define BI_PHYMODE_RGMII 2#define BI_PHYMODE_GMII  3#define BI_PHYMODE_RTBI  4#define BI_PHYMODE_TBI   5#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \    defined(CONFIG_460EX) || defined(CONFIG_460GT) || \    defined(CONFIG_405EX)#define BI_PHYMODE_SMII  6#define BI_PHYMODE_MII   7#if defined(CONFIG_460EX) || defined(CONFIG_460GT)#define BI_PHYMODE_RMII  8#endif#endif#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \    defined(CONFIG_460EX) || defined(CONFIG_460GT) || \    defined(CONFIG_405EX)#define SDR0_MFR_ETH_CLK_SEL_V(n)	((0x01<<27) / (n+1))#endif#if defined(CONFIG_460EX) || defined(CONFIG_460GT)#define SDR0_ETH_CFG_CLK_SEL_V(n)	(0x01 << (8 + n))#endif#if defined(CONFIG_460EX) || defined(CONFIG_460GT)#define MAL_RX_CHAN_MUL	8	/* 460EX/GT uses MAL channel 8 for EMAC1 */#else#define MAL_RX_CHAN_MUL	1#endif/*-----------------------------------------------------------------------------+ * Global variables. TX and RX descriptors and buffers. *-----------------------------------------------------------------------------*//* IER globals */static uint32_t mal_ier;#if !defined(CONFIG_NET_MULTI)struct eth_device *emac0_dev = NULL;#endif/* * Get count of EMAC devices (doesn't have to be the max. possible number * supported by the cpu) * * CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the * EMAC count is possible. As it is needed for the Kilauea/Haleakala * 405EX/405EXr eval board, using the same binary. */#if defined(CONFIG_BOARD_EMAC_COUNT)#define LAST_EMAC_NUM	board_emac_count()#else /* CONFIG_BOARD_EMAC_COUNT */#if defined(CONFIG_HAS_ETH3)#define LAST_EMAC_NUM	4#elif defined(CONFIG_HAS_ETH2)#define LAST_EMAC_NUM	3#elif defined(CONFIG_HAS_ETH1)#define LAST_EMAC_NUM	2#else#define LAST_EMAC_NUM	1#endif#endif /* CONFIG_BOARD_EMAC_COUNT *//* normal boards start with EMAC0 */#if !defined(CONFIG_EMAC_NR_START)#define CONFIG_EMAC_NR_START	0#endif#if defined(CONFIG_405EX) || defined(CONFIG_440EPX)#define ETH_IRQ_NUM(dev)	(VECNUM_ETH0 + ((dev)))#else#define ETH_IRQ_NUM(dev)	(VECNUM_ETH0 + ((dev) * 2))#endif#define MAL_RX_DESC_SIZE	2048#define MAL_TX_DESC_SIZE	2048#define MAL_ALLOC_SIZE		(MAL_TX_DESC_SIZE + MAL_RX_DESC_SIZE)/*-----------------------------------------------------------------------------+ * Prototypes and externals. *-----------------------------------------------------------------------------*/static void enet_rcv (struct eth_device *dev, unsigned long malisr);int enetInt (struct eth_device *dev);static void mal_err (struct eth_device *dev, unsigned long isr,		     unsigned long uic, unsigned long maldef,		     unsigned long mal_errr);static void emac_err (struct eth_device *dev, unsigned long isr);extern int phy_setup_aneg (char *devname, unsigned char addr);extern int emac4xx_miiphy_read (char *devname, unsigned char addr,		unsigned char reg, unsigned short *value);extern int emac4xx_miiphy_write (char *devname, unsigned char addr,		unsigned char reg, unsigned short value);int board_emac_count(void);static void emac_loopback_enable(EMAC_4XX_HW_PST hw_p){#if defined(CONFIG_440SPE) || \    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \    defined(CONFIG_405EX)	u32 val;	mfsdr(sdr_mfr, val);	val |= SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum);	mtsdr(sdr_mfr, val);#elif defined(CONFIG_460EX) || defined(CONFIG_460GT)	u32 val;	mfsdr(SDR0_ETH_CFG, val);	val |= SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum);	mtsdr(SDR0_ETH_CFG, val);#endif}static void emac_loopback_disable(EMAC_4XX_HW_PST hw_p){#if defined(CONFIG_440SPE) || \    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \    defined(CONFIG_405EX)	u32 val;	mfsdr(sdr_mfr, val);	val &= ~SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum);	mtsdr(sdr_mfr, val);#elif defined(CONFIG_460EX) || defined(CONFIG_460GT)	u32 val;	mfsdr(SDR0_ETH_CFG, val);	val &= ~SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum);	mtsdr(SDR0_ETH_CFG, val);#endif}/*-----------------------------------------------------------------------------+| ppc_4xx_eth_halt| Disable MAL channel, and EMACn+-----------------------------------------------------------------------------*/static void ppc_4xx_eth_halt (struct eth_device *dev){	EMAC_4XX_HW_PST hw_p = dev->priv;	u32 val = 10000;	out_be32((void *)EMAC_IER + hw_p->hw_addr, 0x00000000);	/* disable emac interrupts */	/* 1st reset MAL channel */	/* Note: writing a 0 to a channel has no effect */#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR)	mtdcr (maltxcarr, (MAL_CR_MMSR >> (hw_p->devnum * 2)));#else	mtdcr (maltxcarr, (MAL_CR_MMSR >> hw_p->devnum));#endif	mtdcr (malrxcarr, (MAL_CR_MMSR >> hw_p->devnum));	/* wait for reset */	while (mfdcr (malrxcasr) & (MAL_CR_MMSR >> hw_p->devnum)) {		udelay (1000);	/* Delay 1 MS so as not to hammer the register */		val--;		if (val == 0)			break;	}	/* provide clocks for EMAC internal loopback  */	emac_loopback_enable(hw_p);	/* EMAC RESET */	out_be32((void *)EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST);	/* remove clocks for EMAC internal loopback  */	emac_loopback_disable(hw_p);#ifndef CONFIG_NETCONSOLE	hw_p->print_speed = 1;	/* print speed message again next time */#endif#if defined(CONFIG_460EX) || defined(CONFIG_460GT)	/* don't bypass the TAHOE0/TAHOE1 cores for Linux */	mfsdr(SDR0_ETH_CFG, val);	val &= ~(SDR0_ETH_CFG_TAHOE0_BYPASS | SDR0_ETH_CFG_TAHOE1_BYPASS);	mtsdr(SDR0_ETH_CFG, val);#endif	return;}#if defined (CONFIG_440GX)int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis){	unsigned long pfc1;	unsigned long zmiifer;	unsigned long rmiifer;	mfsdr(sdr_pfc1, pfc1);	pfc1 = SDR0_PFC1_EPS_DECODE(pfc1);	zmiifer = 0;	rmiifer = 0;	switch (pfc1) {	case 1:		zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);		zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1);		zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2);		zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_ZMII;		bis->bi_phymode[3] = BI_PHYMODE_ZMII;		break;	case 2:		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_ZMII;		bis->bi_phymode[3] = BI_PHYMODE_ZMII;		break;	case 3:		zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);		rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_NONE;		bis->bi_phymode[2] = BI_PHYMODE_RGMII;		bis->bi_phymode[3] = BI_PHYMODE_NONE;		break;	case 4:		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1);		rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2);		rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_RGMII;		bis->bi_phymode[3] = BI_PHYMODE_RGMII;		break;	case 5:		zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2);		rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_ZMII;		bis->bi_phymode[3] = BI_PHYMODE_RGMII;		break;	case 6:		zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);		zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);		rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_RGMII;		break;	case 0:	default:		zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum);		rmiifer = 0x0;		bis->bi_phymode[0] = BI_PHYMODE_ZMII;		bis->bi_phymode[1] = BI_PHYMODE_ZMII;		bis->bi_phymode[2] = BI_PHYMODE_ZMII;		bis->bi_phymode[3] = BI_PHYMODE_ZMII;		break;	}	/* Ensure we setup mdio for this devnum and ONLY this devnum */	zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum);	out_be32((void *)ZMII_FER, zmiifer);	out_be32((void *)RGMII_FER, rmiifer);	return ((int)pfc1);}#endif	/* CONFIG_440_GX */#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX)int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis){	unsigned long zmiifer=0x0;	unsigned long pfc1;	mfsdr(sdr_pfc1, pfc1);	pfc1 &= SDR0_PFC1_SELECT_MASK;	switch (pfc1) {	case SDR0_PFC1_SELECT_CONFIG_2:		/* 1 x GMII port */		out_be32((void *)ZMII_FER, 0x00);		out_be32((void *)RGMII_FER, 0x00000037);		bis->bi_phymode[0] = BI_PHYMODE_GMII;		bis->bi_phymode[1] = BI_PHYMODE_NONE;		break;	case SDR0_PFC1_SELECT_CONFIG_4:		/* 2 x RGMII ports */		out_be32((void *)ZMII_FER, 0x00);		out_be32((void *)RGMII_FER, 0x00000055);		bis->bi_phymode[0] = BI_PHYMODE_RGMII;		bis->bi_phymode[1] = BI_PHYMODE_RGMII;		break;	case SDR0_PFC1_SELECT_CONFIG_6:		/* 2 x SMII ports */		out_be32((void *)ZMII_FER,			 ((ZMII_FER_SMII) << ZMII_FER_V(0)) |			 ((ZMII_FER_SMII) << ZMII_FER_V(1)));		out_be32((void *)RGMII_FER, 0x00000000);		bis->bi_phymode[0] = BI_PHYMODE_SMII;		bis->bi_phymode[1] = BI_PHYMODE_SMII;		break;	case SDR0_PFC1_SELECT_CONFIG_1_2:		/* only 1 x MII supported */		out_be32((void *)ZMII_FER, (ZMII_FER_MII) << ZMII_FER_V(0));		out_be32((void *)RGMII_FER, 0x00000000);		bis->bi_phymode[0] = BI_PHYMODE_MII;		bis->bi_phymode[1] = BI_PHYMODE_NONE;		break;	default:		break;	}	/* Ensure we setup mdio for this devnum and ONLY this devnum */	zmiifer = in_be32((void *)ZMII_FER);	zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum);	out_be32((void *)ZMII_FER, zmiifer);	return ((int)0x0);}#endif	/* CONFIG_440EPX */#if defined(CONFIG_405EX)int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis){	u32 gmiifer = 0;	/*	 * Right now only 2*RGMII is supported. Please extend when needed.	 * sr - 2007-09-19	 */	switch (1) {	case 1:		/* 2 x RGMII ports */		out_be32((void *)RGMII_FER, 0x00000055);		bis->bi_phymode[0] = BI_PHYMODE_RGMII;		bis->bi_phymode[1] = BI_PHYMODE_RGMII;		break;	case 2:		/* 2 x SMII ports */		break;	default:		break;	}	/* Ensure we setup mdio for this devnum and ONLY this devnum */	gmiifer = in_be32((void *)RGMII_FER);	gmiifer |= (1 << (19-devnum));	out_be32((void *)RGMII_FER, gmiifer);	return ((int)0x0);}#endif  /* CONFIG_405EX */#if defined(CONFIG_460EX) || defined(CONFIG_460GT)int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis){	u32 eth_cfg;	u32 zmiifer;		/* ZMII0_FER reg. */	u32 rmiifer;		/* RGMII0_FER reg. Bridge 0 */	u32 rmiifer1;		/* RGMII0_FER reg. Bridge 1 */	int mode;	zmiifer  = 0;	rmiifer  = 0;	rmiifer1 = 0;#if defined(CONFIG_460EX)	mode = 9;#else	mode = 10;#endif	/* TODO:	 * NOTE: 460GT has 2 RGMII bridge cores:	 *		emac0 ------ RGMII0_BASE	 *		           |	 *		emac1 -----+	 *	 *		emac2 ------ RGMII1_BASE	 *		           |	 *		emac3 -----+	 *

⌨️ 快捷键说明

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