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

📄 tsec.c

📁 最新版的u-boot,2008-10-18发布
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Freescale Three Speed Ethernet Controller driver * * This software may be used and distributed according to the * terms of the GNU Public License, Version 2, incorporated * herein by reference. * * Copyright 2004, 2007 Freescale Semiconductor, Inc. * (C) Copyright 2003, Motorola, Inc. * author Andy Fleming * */#include <config.h>#include <common.h>#include <malloc.h>#include <net.h>#include <command.h>#include <tsec.h>#include "miiphy.h"DECLARE_GLOBAL_DATA_PTR;#define TX_BUF_CNT		2static uint rxIdx;		/* index of the current RX buffer */static uint txIdx;		/* index of the current TX buffer */typedef volatile struct rtxbd {	txbd8_t txbd[TX_BUF_CNT];	rxbd8_t rxbd[PKTBUFSRX];} RTXBD;#define MAXCONTROLLERS	(8)static int relocated = 0;static struct tsec_private *privlist[MAXCONTROLLERS];static int num_tsecs = 0;#ifdef __GNUC__static RTXBD rtx __attribute__ ((aligned(8)));#else#error "rtx must be 64-bit aligned"#endifstatic int tsec_send(struct eth_device *dev,		     volatile void *packet, int length);static int tsec_recv(struct eth_device *dev);static int tsec_init(struct eth_device *dev, bd_t * bd);static void tsec_halt(struct eth_device *dev);static void init_registers(volatile tsec_t * regs);static void startup_tsec(struct eth_device *dev);static int init_phy(struct eth_device *dev);void write_phy_reg(struct tsec_private *priv, uint regnum, uint value);uint read_phy_reg(struct tsec_private *priv, uint regnum);struct phy_info *get_phy_info(struct eth_device *dev);void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd);static void adjust_link(struct eth_device *dev);static void relocate_cmds(void);#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \	&& !defined(BITBANGMII)static int tsec_miiphy_write(char *devname, unsigned char addr,			     unsigned char reg, unsigned short value);static int tsec_miiphy_read(char *devname, unsigned char addr,			    unsigned char reg, unsigned short *value);#endif#ifdef CONFIG_MCAST_TFTPstatic int tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set);#endif/* Default initializations for TSEC controllers. */static struct tsec_info_struct tsec_info[] = {#ifdef CONFIG_TSEC1	STD_TSEC_INFO(1),	/* TSEC1 */#endif#ifdef CONFIG_TSEC2	STD_TSEC_INFO(2),	/* TSEC2 */#endif#ifdef CONFIG_MPC85XX_FEC	{		.regs = (tsec_t *)(TSEC_BASE_ADDR + 0x2000),		.miiregs = (tsec_t *)(TSEC_BASE_ADDR),		.devname = CONFIG_MPC85XX_FEC_NAME,		.phyaddr = FEC_PHY_ADDR,		.flags = FEC_FLAGS	},			/* FEC */#endif#ifdef CONFIG_TSEC3	STD_TSEC_INFO(3),	/* TSEC3 */#endif#ifdef CONFIG_TSEC4	STD_TSEC_INFO(4),	/* TSEC4 */#endif};int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num){	int i;	for (i = 0; i < num; i++)		tsec_initialize(bis, &tsecs[i]);	return 0;}int tsec_standard_init(bd_t *bis){	return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info));}/* Initialize device structure. Returns success if PHY * initialization succeeded (i.e. if it recognizes the PHY) */int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info){	struct eth_device *dev;	int i;	struct tsec_private *priv;	dev = (struct eth_device *)malloc(sizeof *dev);	if (NULL == dev)		return 0;	memset(dev, 0, sizeof *dev);	priv = (struct tsec_private *)malloc(sizeof(*priv));	if (NULL == priv)		return 0;	privlist[num_tsecs++] = priv;	priv->regs = tsec_info->regs;	priv->phyregs = tsec_info->miiregs;	priv->phyaddr = tsec_info->phyaddr;	priv->flags = tsec_info->flags;	sprintf(dev->name, tsec_info->devname);	dev->iobase = 0;	dev->priv = priv;	dev->init = tsec_init;	dev->halt = tsec_halt;	dev->send = tsec_send;	dev->recv = tsec_recv;#ifdef CONFIG_MCAST_TFTP	dev->mcast = tsec_mcast_addr;#endif	/* Tell u-boot to get the addr from the env */	for (i = 0; i < 6; i++)		dev->enetaddr[i] = 0;	eth_register(dev);	/* Reset the MAC */	priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;	priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \	&& !defined(BITBANGMII)	miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write);#endif	/* Try to initialize PHY here, and return */	return init_phy(dev);}/* Initializes data structures and registers for the controller, * and brings the interface up.	 Returns the link status, meaning * that it returns success if the link is up, failure otherwise. * This allows u-boot to find the first active controller. */int tsec_init(struct eth_device *dev, bd_t * bd){	uint tempval;	char tmpbuf[MAC_ADDR_LEN];	int i;	struct tsec_private *priv = (struct tsec_private *)dev->priv;	volatile tsec_t *regs = priv->regs;	/* Make sure the controller is stopped */	tsec_halt(dev);	/* Init MACCFG2.  Defaults to GMII */	regs->maccfg2 = MACCFG2_INIT_SETTINGS;	/* Init ECNTRL */	regs->ecntrl = ECNTRL_INIT_SETTINGS;	/* Copy the station address into the address registers.	 * Backwards, because little endian MACS are dumb */	for (i = 0; i < MAC_ADDR_LEN; i++) {		tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];	}	regs->macstnaddr1 = *((uint *) (tmpbuf));	tempval = *((uint *) (tmpbuf + 4));	regs->macstnaddr2 = tempval;	/* reset the indices to zero */	rxIdx = 0;	txIdx = 0;	/* Clear out (for the most part) the other registers */	init_registers(regs);	/* Ready the device for tx/rx */	startup_tsec(dev);	/* If there's no link, fail */	return (priv->link ? 0 : -1);}/* Writes the given phy's reg with value, using the specified MDIO regs */static void tsec_local_mdio_write(volatile tsec_t *phyregs, uint addr,		uint reg, uint value){	int timeout = 1000000;	phyregs->miimadd = (addr << 8) | reg;	phyregs->miimcon = value;	asm("sync");	timeout = 1000000;	while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ;}/* Provide the default behavior of writing the PHY of this ethernet device */#define write_phy_reg(priv, regnum, value) tsec_local_mdio_write(priv->phyregs,priv->phyaddr,regnum,value)/* Reads register regnum on the device's PHY through the * specified registers.	 It lowers and raises the read * command, and waits for the data to become valid (miimind * notvalid bit cleared), and the bus to cease activity (miimind * busy bit cleared), and then returns the value */uint tsec_local_mdio_read(volatile tsec_t *phyregs, uint phyid, uint regnum){	uint value;	/* Put the address of the phy, and the register	 * number into MIIMADD */	phyregs->miimadd = (phyid << 8) | regnum;	/* Clear the command register, and wait */	phyregs->miimcom = 0;	asm("sync");	/* Initiate a read command, and wait */	phyregs->miimcom = MIIM_READ_COMMAND;	asm("sync");	/* Wait for the the indication that the read is done */	while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;	/* Grab the value read from the PHY */	value = phyregs->miimstat;	return value;}/* #define to provide old read_phy_reg functionality without duplicating code */#define read_phy_reg(priv,regnum) tsec_local_mdio_read(priv->phyregs,priv->phyaddr,regnum)#define TBIANA_SETTINGS ( \		TBIANA_ASYMMETRIC_PAUSE \		| TBIANA_SYMMETRIC_PAUSE \		| TBIANA_FULL_DUPLEX \		)#define TBICR_SETTINGS ( \		TBICR_PHY_RESET \		| TBICR_ANEG_ENABLE \		| TBICR_FULL_DUPLEX \		| TBICR_SPEED1_SET \		)/* Configure the TBI for SGMII operation */static void tsec_configure_serdes(struct tsec_private *priv){	/* Access TBI PHY registers at given TSEC register offset as opposed to the	 * register offset used for external PHY accesses */	tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_ANA,			TBIANA_SETTINGS);	tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_TBICON,			TBICON_CLK_SELECT);	tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_CR,			TBICR_SETTINGS);}/* Discover which PHY is attached to the device, and configure it * properly.  If the PHY is not recognized, then return 0 * (failure).  Otherwise, return 1 */static int init_phy(struct eth_device *dev){	struct tsec_private *priv = (struct tsec_private *)dev->priv;	struct phy_info *curphy;	volatile tsec_t *phyregs = priv->phyregs;	volatile tsec_t *regs = priv->regs;	/* Assign a Physical address to the TBI */	regs->tbipa = CFG_TBIPA_VALUE;	phyregs->tbipa = CFG_TBIPA_VALUE;	asm("sync");	/* Reset MII (due to new addresses) */	priv->phyregs->miimcfg = MIIMCFG_RESET;	asm("sync");	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;	asm("sync");	while (priv->phyregs->miimind & MIIMIND_BUSY) ;	if (0 == relocated)		relocate_cmds();	/* Get the cmd structure corresponding to the attached	 * PHY */	curphy = get_phy_info(dev);	if (curphy == NULL) {		priv->phyinfo = NULL;		printf("%s: No PHY found\n", dev->name);		return 0;	}	if (regs->ecntrl & ECNTRL_SGMII_MODE)		tsec_configure_serdes(priv);	priv->phyinfo = curphy;	phy_run_commands(priv, priv->phyinfo->config);	return 1;}/* * Returns which value to write to the control register. * For 10/100, the value is slightly different */uint mii_cr_init(uint mii_reg, struct tsec_private * priv){	if (priv->flags & TSEC_GIGABIT)		return MIIM_CONTROL_INIT;	else		return MIIM_CR_INIT;}/* Parse the status register for link, and then do * auto-negotiation */uint mii_parse_sr(uint mii_reg, struct tsec_private * priv){	/*	 * Wait if the link is up, and autonegotiation is in progress	 * (ie - we're capable and it's not done)	 */	mii_reg = read_phy_reg(priv, MIIM_STATUS);	if ((mii_reg & MIIM_STATUS_LINK) && (mii_reg & PHY_BMSR_AUTN_ABLE)	    && !(mii_reg & PHY_BMSR_AUTN_COMP)) {		int i = 0;		puts("Waiting for PHY auto negotiation to complete");		while (!(mii_reg & PHY_BMSR_AUTN_COMP)) {			/*			 * Timeout reached ?			 */			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {				puts(" TIMEOUT !\n");				priv->link = 0;				return 0;			}			if ((i++ % 1000) == 0) {				putc('.');			}			udelay(1000);	/* 1 ms */			mii_reg = read_phy_reg(priv, MIIM_STATUS);		}		puts(" done\n");		priv->link = 1;		udelay(500000);	/* another 500 ms (results in faster booting) */	} else {		if (mii_reg & MIIM_STATUS_LINK)			priv->link = 1;		else			priv->link = 0;	}	return 0;}/* Generic function which updates the speed and duplex.  If * autonegotiation is enabled, it uses the AND of the link * partner's advertised capabilities and our advertised * capabilities.  If autonegotiation is disabled, we use the * appropriate bits in the control register. * * Stolen from Linux's mii.c and phy_device.c */uint mii_parse_link(uint mii_reg, struct tsec_private *priv){	/* We're using autonegotiation */	if (mii_reg & PHY_BMSR_AUTN_ABLE) {		uint lpa = 0;		uint gblpa = 0;		/* Check for gigabit capability */		if (mii_reg & PHY_BMSR_EXT) {			/* We want a list of states supported by			 * both PHYs in the link			 */			gblpa = read_phy_reg(priv, PHY_1000BTSR);			gblpa &= read_phy_reg(priv, PHY_1000BTCR) << 2;		}		/* Set the baseline so we only have to set them		 * if they're different		 */		priv->speed = 10;		priv->duplexity = 0;		/* Check the gigabit fields */		if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {			priv->speed = 1000;			if (gblpa & PHY_1000BTSR_1000FD)				priv->duplexity = 1;			/* We're done! */			return 0;		}		lpa = read_phy_reg(priv, PHY_ANAR);		lpa &= read_phy_reg(priv, PHY_ANLPAR);		if (lpa & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) {			priv->speed = 100;			if (lpa & PHY_ANLPAR_TXFD)				priv->duplexity = 1;		} else if (lpa & PHY_ANLPAR_10FD)			priv->duplexity = 1;	} else {		uint bmcr = read_phy_reg(priv, PHY_BMCR);		priv->speed = 10;		priv->duplexity = 0;		if (bmcr & PHY_BMCR_DPLX)			priv->duplexity = 1;		if (bmcr & PHY_BMCR_1000_MBPS)			priv->speed = 1000;		else if (bmcr & PHY_BMCR_100_MBPS)			priv->speed = 100;	}	return 0;}/* * Parse the BCM54xx status register for speed and duplex information. * The linux sungem_phy has this information, but in a table format. */uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv){	switch((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT){		case 1:			printf("Enet starting in 10BT/HD\n");			priv->duplexity = 0;			priv->speed = 10;			break;		case 2:			printf("Enet starting in 10BT/FD\n");			priv->duplexity = 1;			priv->speed = 10;			break;		case 3:			printf("Enet starting in 100BT/HD\n");			priv->duplexity = 0;			priv->speed = 100;			break;		case 5:			printf("Enet starting in 100BT/FD\n");			priv->duplexity = 1;			priv->speed = 100;			break;		case 6:			printf("Enet starting in 1000BT/HD\n");			priv->duplexity = 0;			priv->speed = 1000;			break;		case 7:			printf("Enet starting in 1000BT/FD\n");			priv->duplexity = 1;			priv->speed = 1000;			break;		default:			printf("Auto-neg error, defaulting to 10BT/HD\n");			priv->duplexity = 0;			priv->speed = 10;			break;	}	return 0;}/* Parse the 88E1011's status register for speed and duplex * information */uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv){	uint speed;	mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);	if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) &&		!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {		int i = 0;		puts("Waiting for PHY realtime link");		while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {			/* Timeout reached ? */			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {				puts(" TIMEOUT !\n");				priv->link = 0;				break;			}			if ((i++ % 1000) == 0) {				putc('.');			}			udelay(1000);	/* 1 ms */			mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);		}		puts(" done\n");		udelay(500000);	/* another 500 ms (results in faster booting) */	} else {		if (mii_reg & MIIM_88E1011_PHYSTAT_LINK)			priv->link = 1;		else			priv->link = 0;	}	if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)		priv->duplexity = 1;	else		priv->duplexity = 0;	speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);	switch (speed) {	case MIIM_88E1011_PHYSTAT_GBIT:		priv->speed = 1000;		break;	case MIIM_88E1011_PHYSTAT_100:		priv->speed = 100;		break;	default:		priv->speed = 10;	}	return 0;}/* Parse the RTL8211B's status register for speed and duplex * information */uint mii_parse_RTL8211B_sr(uint mii_reg, struct tsec_private * priv){	uint speed;	mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS);	if (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {		int i = 0;		/* in case of timeout ->link is cleared */		priv->link = 1;		puts("Waiting for PHY realtime link");		while (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {			/* Timeout reached ? */			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {				puts(" TIMEOUT !\n");				priv->link = 0;

⌨️ 快捷键说明

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