Skip to content
Snippets Groups Projects
tsec.c 50.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * 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-2011 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"
    
    #define TX_BUF_CNT		2
    
    static 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];
    
    #define MAXCONTROLLERS	(8)
    
    
    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"
    #endif
    
    
    static 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 int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info);
    
    static void tsec_halt(struct eth_device *dev);
    
    static void init_registers(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);
    
    static struct phy_info *get_phy_info(struct eth_device *dev);
    static void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd);
    
    static void adjust_link(struct eth_device *dev);
    
    #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \
    	&& !defined(BITBANGMII)
    
    static int tsec_miiphy_write(const char *devname, unsigned char addr,
    
    			     unsigned char reg, unsigned short value);
    
    static int tsec_miiphy_read(const char *devname, unsigned char addr,
    
    			    unsigned char reg, unsigned short *value);
    
    David Updegraff's avatar
    David Updegraff committed
    #ifdef CONFIG_MCAST_TFTP
    static 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_mdio_t *)(MDIO_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
    };
    
    
    /*
     * Initialize all the TSEC devices
     *
     * Returns the number of TSEC devices that were initialized
     */
    
    int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num)
    {
    	int i;
    
    	int ret, count = 0;
    
    	for (i = 0; i < num; i++) {
    		ret = tsec_initialize(bis, &tsecs[i]);
    		if (ret > 0)
    			count += ret;
    	}
    
    }
    
    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)
     */
    
    static int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info)
    
    	struct eth_device *dev;
    
    	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)
    
    	privlist[num_tsecs++] = priv;
    	priv->regs = tsec_info->regs;
    	priv->phyregs = tsec_info->miiregs;
    
    	priv->phyregs_sgmii = tsec_info->miiregs_sgmii;
    
    	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;
    
    David Updegraff's avatar
    David Updegraff committed
    #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 */
    
    	setbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
    
    	udelay(2);  /* Soft Reset must be asserted for 3 TX clocks */
    
    	clrbits_be32(&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.
     */
    
    static 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;
    
    	tsec_t *regs = priv->regs;
    
    
    	/* Make sure the controller is stopped */
    	tsec_halt(dev);
    
    
    	/* Init MACCFG2.  Defaults to GMII */
    
    	out_be32(&regs->maccfg2, MACCFG2_INIT_SETTINGS);
    
    
    	/* Init ECNTRL */
    
    	out_be32(&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];
    
    	tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) |
    		  tmpbuf[3];
    
    
    	out_be32(&regs->macstnaddr1, tempval);
    
    	tempval = *((uint *) (tmpbuf + 4));
    
    	out_be32(&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(tsec_mdio_t *phyregs, uint addr,
    
    		uint reg, uint value)
    
    	int timeout = 1000000;
    
    	out_be32(&phyregs->miimadd, (addr << 8) | reg);
    	out_be32(&phyregs->miimcon, value);
    
    	timeout = 1000000;
    
    	while ((in_be32(&phyregs->miimind) & MIIMIND_BUSY) && timeout--)
    		;
    
    
    /* Provide the default behavior of writing the PHY of this ethernet device */
    
    Peter Tyser's avatar
    Peter Tyser committed
    #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
     */
    
    static uint tsec_local_mdio_read(tsec_mdio_t *phyregs, uint phyid, uint regnum)
    
    	/* Put the address of the phy, and the register
    	 * number into MIIMADD */
    
    	out_be32(&phyregs->miimadd, (phyid << 8) | regnum);
    
    
    	/* Clear the command register, and wait */
    
    	out_be32(&phyregs->miimcom, 0);
    
    
    	/* Initiate a read command, and wait */
    
    	out_be32(&phyregs->miimcom, MIIM_READ_COMMAND);
    
    
    	/* Wait for the the indication that the read is done */
    
    	while ((in_be32(&phyregs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY)))
    		;
    
    
    	/* Grab the value read from the PHY */
    
    	value = in_be32(&phyregs->miimstat);
    
    /* #define to provide old read_phy_reg functionality without duplicating code */
    
    Peter Tyser's avatar
    Peter Tyser committed
    #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 \
    		)
    
    
    /* By default force the TBI PHY into 1000Mbps full duplex when in SGMII mode */
    #ifndef CONFIG_TSEC_TBICR_SETTINGS
    
    #define CONFIG_TSEC_TBICR_SETTINGS ( \
    
    		TBICR_PHY_RESET \
    
    		| TBICR_FULL_DUPLEX \
    		| TBICR_SPEED1_SET \
    		)
    
    #endif /* CONFIG_TSEC_TBICR_SETTINGS */
    
    /* Configure the TBI for SGMII operation */
    static void tsec_configure_serdes(struct tsec_private *priv)
    {
    
    Peter Tyser's avatar
    Peter Tyser committed
    	/* 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->phyregs_sgmii, priv->regs->tbipa, TBI_ANA,
    
    			TBIANA_SETTINGS);
    
    	tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_TBICON,
    
    			TBICON_CLK_SELECT);
    
    	tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_CR,
    
    			CONFIG_TSEC_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;
    
    	tsec_t *regs = priv->regs;
    
    
    	/* Assign a Physical address to the TBI */
    
    	out_be32(&regs->tbipa, CONFIG_SYS_TBIPA_VALUE);
    
    
    	/* Reset MII (due to new addresses) */
    
    	out_be32(&priv->phyregs->miimcfg, MIIMCFG_RESET);
    	out_be32(&priv->phyregs->miimcfg, MIIMCFG_INIT_VALUE);
    	while (in_be32(&priv->phyregs->miimind) & MIIMIND_BUSY)
    		;
    
    	/* 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);
    
    	if (in_be32(&regs->ecntrl) & ECNTRL_SGMII_MODE)
    
    		tsec_configure_serdes(priv);
    
    
    	priv->phyinfo = curphy;
    
    	phy_run_commands(priv, priv->phyinfo->config);
    
    /*
     * Returns which value to write to the control register.
     * For 10/100, the value is slightly different
     */
    
    static uint mii_cr_init(uint mii_reg, struct tsec_private * priv)
    
    	if (priv->flags & TSEC_GIGABIT)
    
    		return MIIM_CONTROL_INIT;
    
    		return MIIM_CR_INIT;
    }
    
    /*
     * Wait for auto-negotiation to complete, then determine link
    
    static 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 & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
    
    		puts("Waiting for PHY auto negotiation to complete");
    
    		while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
    
    			/*
    			 * Timeout reached ?
    			 */
    			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
    
    				puts(" TIMEOUT !\n");
    
    				priv->link = 0;
    
    				return 0;
    
    			if (ctrlc()) {
    				puts("user interrupt!\n");
    				priv->link = 0;
    				return -EINTR;
    			}
    
    
    			if ((i++ % 1000) == 0) {
    
    			udelay(1000);	/* 1 ms */
    
    			mii_reg = read_phy_reg(priv, MIIM_STATUS);
    
    		puts(" done\n");
    
    
    		/* Link status bit is latched low, read it again */
    		mii_reg = read_phy_reg(priv, MIIM_STATUS);
    
    
    		udelay(500000);	/* another 500 ms (results in faster booting) */
    
    	priv->link = mii_reg & MIIM_STATUS_LINK ? 1 : 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
     */
    
    static uint mii_parse_link(uint mii_reg, struct tsec_private *priv)
    
    {
    	/* We're using autonegotiation */
    
    	if (mii_reg & BMSR_ANEGCAPABLE) {
    
    		uint lpa = 0;
    		uint gblpa = 0;
    
    		/* Check for gigabit capability */
    
    		if (mii_reg & BMSR_ERCAP) {
    
    			/* We want a list of states supported by
    			 * both PHYs in the link
    			 */
    
    			gblpa = read_phy_reg(priv, MII_STAT1000);
    			gblpa &= read_phy_reg(priv, MII_CTRL1000) << 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, MII_ADVERTISE);
    		lpa &= read_phy_reg(priv, MII_LPA);
    
    		if (lpa & (LPA_100FULL | LPA_100HALF)) {
    
    			if (lpa & LPA_100FULL)
    
    		} else if (lpa & LPA_10FULL)
    
    			priv->duplexity = 1;
    	} else {
    
    		uint bmcr = read_phy_reg(priv, MII_BMCR);
    
    
    		priv->speed = 10;
    		priv->duplexity = 0;
    
    
    		if (bmcr & BMCR_FULLDPLX)
    
    		if (bmcr & BMCR_SPEED1000)
    
    		else if (bmcr & BMCR_SPEED100)
    
    /*
     * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
     * circumstances.  eg a gigabit TSEC connected to a gigabit switch with
     * a 4-wire ethernet cable.  Both ends advertise gigabit, but can't
     * link.  "Ethernet@Wirespeed" reduces advertised speed until link
     * can be achieved.
     */
    
    static uint mii_BCM54xx_wirespeed(uint mii_reg, struct tsec_private *priv)
    
    {
    	return (read_phy_reg(priv, mii_reg) & 0x8FFF) | 0x8010;
    }
    
    
    /*
     * Parse the BCM54xx status register for speed and duplex information.
     * The linux sungem_phy has this information, but in a table format.
     */
    
    static uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv)
    
    	/* If there is no link, speed and duplex don't matter */
    	if (!priv->link)
    		return 0;
    
    	switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
    		MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
    	case 1:
    		priv->duplexity = 0;
    		priv->speed = 10;
    		break;
    	case 2:
    		priv->duplexity = 1;
    		priv->speed = 10;
    		break;
    	case 3:
    		priv->duplexity = 0;
    		priv->speed = 100;
    		break;
    	case 5:
    		priv->duplexity = 1;
    		priv->speed = 100;
    		break;
    	case 6:
    		priv->duplexity = 0;
    		priv->speed = 1000;
    		break;
    	case 7:
    		priv->duplexity = 1;
    		priv->speed = 1000;
    		break;
    	default:
    		printf("Auto-neg error, defaulting to 10BT/HD\n");
    		priv->duplexity = 0;
    		priv->speed = 10;
    		break;
    
    /*
     * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
     * 0x42 - "Operating Mode Status Register"
     */
    static int BCM8482_is_serdes(struct tsec_private *priv)
    {
    	u16 val;
    	int serdes = 0;
    
    	write_phy_reg(priv, MIIM_BCM54XX_EXP_SEL, MIIM_BCM54XX_EXP_SEL_ER | 0x42);
    	val = read_phy_reg(priv, MIIM_BCM54XX_EXP_DATA);
    
    	switch (val & 0x1f) {
    	case 0x0d:	/* RGMII-to-100Base-FX */
    	case 0x0e:	/* RGMII-to-SGMII */
    	case 0x0f:	/* RGMII-to-SerDes */
    	case 0x12:	/* SGMII-to-SerDes */
    	case 0x13:	/* SGMII-to-100Base-FX */
    	case 0x16:	/* SerDes-to-Serdes */
    		serdes = 1;
    		break;
    	case 0x6:	/* RGMII-to-Copper */
    	case 0x14:	/* SGMII-to-Copper */
    	case 0x17:	/* SerDes-to-Copper */
    		break;
    	default:
    		printf("ERROR, invalid PHY mode (0x%x\n)", val);
    		break;
    	}
    
    	return serdes;
    
    
    /*
     * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
     * Mode Status Register"
     */
    uint mii_parse_BCM5482_serdes_sr(struct tsec_private *priv)
    {
    	u16 val;
    	int i = 0;
    
    	/* Wait 1s for link - Clause 37 autonegotiation happens very fast */
    	while (1) {
    		write_phy_reg(priv, MIIM_BCM54XX_EXP_SEL,
    				MIIM_BCM54XX_EXP_SEL_ER | 0x42);
    		val = read_phy_reg(priv, MIIM_BCM54XX_EXP_DATA);
    
    		if (val & 0x8000)
    			break;
    
    		if (i++ > 1000) {
    			priv->link = 0;
    			return 1;
    		}
    
    		udelay(1000);	/* 1 ms */
    	}
    
    	priv->link = 1;
    	switch ((val >> 13) & 0x3) {
    	case (0x00):
    		priv->speed = 10;
    		break;
    	case (0x01):
    		priv->speed = 100;
    		break;
    	case (0x02):
    		priv->speed = 1000;
    		break;
    	}
    
    	priv->duplexity = (val & 0x1000) == 0x1000;
    
    	return 0;
    }
    
    /*
     * Figure out if BCM5482 is in serdes or copper mode and determine link
     * configuration accordingly
     */
    static uint mii_parse_BCM5482_sr(uint mii_reg, struct tsec_private *priv)
    {
    	if (BCM8482_is_serdes(priv)) {
    		mii_parse_BCM5482_serdes_sr(priv);
    
    Peter Tyser's avatar
    Peter Tyser committed
    		priv->flags |= TSEC_FIBER;
    
    	} else {
    		/* Wait for auto-negotiation to complete or fail */
    		mii_parse_sr(mii_reg, priv);
    
    		/* Parse BCM54xx copper aux status register */
    		mii_reg = read_phy_reg(priv, MIIM_BCM54xx_AUXSTATUS);
    		mii_parse_BCM54xx_sr(mii_reg, priv);
    	}
    
    	return 0;
    }
    
    
    /* Parse the 88E1011's status register for speed and duplex
    
    static uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv)
    
    	mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);
    
    
    	if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) &&
    		!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {
    
    		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) {
    
    			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;
    
    /* Parse the RTL8211B's status register for speed and duplex
     * information
     */
    
    static 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)) {
    
    		/* 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;
    				break;
    			}
    
    			if ((i++ % 1000) == 0) {
    				putc('.');
    			}
    			udelay(1000);	/* 1 ms */
    			mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS);
    		}
    		puts(" done\n");
    		udelay(500000);	/* another 500 ms (results in faster booting) */
    	} else {
    		if (mii_reg & MIIM_RTL8211B_PHYSTAT_LINK)
    			priv->link = 1;
    		else
    			priv->link = 0;
    	}
    
    	if (mii_reg & MIIM_RTL8211B_PHYSTAT_DUPLEX)
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = (mii_reg & MIIM_RTL8211B_PHYSTAT_SPEED);
    
    	switch (speed) {
    	case MIIM_RTL8211B_PHYSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_RTL8211B_PHYSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    	}
    
    	return 0;
    }
    
    
    /* Parse the cis8201's status register for speed and duplex
    
    static uint mii_parse_cis8201(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
    
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
    
    	switch (speed) {
    	case MIIM_CIS8201_AUXCONSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_CIS8201_AUXCONSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    		break;
    
    /* Parse the vsc8244's status register for speed and duplex
    
    static uint mii_parse_vsc8244(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & MIIM_VSC8244_AUXCONSTAT_DUPLEX)
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = mii_reg & MIIM_VSC8244_AUXCONSTAT_SPEED;
    	switch (speed) {
    	case MIIM_VSC8244_AUXCONSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_VSC8244_AUXCONSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    		break;
    	}
    
    	return 0;
    }
    
    
    /* Parse the DM9161's status register for speed and duplex
    
    static uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
    
    		priv->speed = 100;
    	else
    		priv->speed = 10;
    
    
    	if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
    
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	return 0;
    }
    
    
    /*
     * Hack to write all 4 PHYs with the LED values
     */
    
    static uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv)
    
    	tsec_mdio_t *regbase = priv->phyregs;
    
    	int timeout = 1000000;
    
    	for (phyid = 0; phyid < 4; phyid++) {
    
    		out_be32(&regbase->miimadd, (phyid << 8) | mii_reg);
    		out_be32(&regbase->miimcon, MIIM_CIS8204_SLEDCON_INIT);
    
    		timeout = 1000000;
    
    		while ((in_be32(&regbase->miimind) & MIIMIND_BUSY) && timeout--)
    			;
    
    	return MIIM_CIS8204_SLEDCON_INIT;
    
    static uint mii_cis8204_setmode(uint mii_reg, struct tsec_private * priv)
    
    {
    	if (priv->flags & TSEC_REDUCED)
    		return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII;
    	else
    		return MIIM_CIS8204_EPHYCON_INIT;
    }
    
    static uint mii_m88e1111s_setmode(uint mii_reg, struct tsec_private *priv)
    
    {
    	uint mii_data = read_phy_reg(priv, mii_reg);
    
    	if (priv->flags & TSEC_REDUCED)
    		mii_data = (mii_data & 0xfff0) | 0x000b;
    	return mii_data;
    }
    
    
    /* Initialized required registers to appropriate values, zeroing
     * those we don't care about (unless zero is bad, in which case,
    
     * choose a more appropriate value)
     */
    
    static void init_registers(tsec_t *regs)
    
    {
    	/* Clear IEVENT */
    
    	out_be32(&regs->ievent, IEVENT_INIT_CLEAR);
    
    	out_be32(&regs->imask, IMASK_INIT_CLEAR);
    
    	out_be32(&regs->hash.iaddr0, 0);
    	out_be32(&regs->hash.iaddr1, 0);
    	out_be32(&regs->hash.iaddr2, 0);
    	out_be32(&regs->hash.iaddr3, 0);
    	out_be32(&regs->hash.iaddr4, 0);
    	out_be32(&regs->hash.iaddr5, 0);
    	out_be32(&regs->hash.iaddr6, 0);
    	out_be32(&regs->hash.iaddr7, 0);
    
    	out_be32(&regs->hash.gaddr0, 0);
    	out_be32(&regs->hash.gaddr1, 0);
    	out_be32(&regs->hash.gaddr2, 0);
    	out_be32(&regs->hash.gaddr3, 0);
    	out_be32(&regs->hash.gaddr4, 0);
    	out_be32(&regs->hash.gaddr5, 0);
    	out_be32(&regs->hash.gaddr6, 0);
    	out_be32(&regs->hash.gaddr7, 0);
    
    	out_be32(&regs->rctrl, 0x00000000);
    
    
    	/* Init RMON mib registers */
    	memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));
    
    
    	out_be32(&regs->rmon.cam1, 0xffffffff);
    	out_be32(&regs->rmon.cam2, 0xffffffff);
    
    	out_be32(&regs->mrblr, MRBLR_INIT_SETTINGS);
    
    	out_be32(&regs->minflr, MINFLR_INIT_SETTINGS);
    
    	out_be32(&regs->attr, ATTR_INIT_SETTINGS);
    	out_be32(&regs->attreli, ATTRELI_INIT_SETTINGS);
    
    /* Configure maccfg2 based on negotiated speed and duplex
    
     * reported by PHY handling code
     */
    
    static void adjust_link(struct eth_device *dev)
    {
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    
    	tsec_t *regs = priv->regs;
    	u32 ecntrl, maccfg2;
    
    	if (!priv->link) {
    		printf("%s: No link.\n", dev->name);
    		return;
    	}
    
    	/* clear all bits relative with interface mode */
    	ecntrl = in_be32(&regs->ecntrl);
    	ecntrl &= ~ECNTRL_R100;
    
    	maccfg2 = in_be32(&regs->maccfg2);
    	maccfg2 &= ~(MACCFG2_IF | MACCFG2_FULL_DUPLEX);
    
    	if (priv->duplexity)
    		maccfg2 |= MACCFG2_FULL_DUPLEX;
    
    	switch (priv->speed) {
    	case 1000:
    		maccfg2 |= MACCFG2_GMII;
    		break;
    	case 100:
    	case 10:
    		maccfg2 |= MACCFG2_MII;
    
    		/* Set R100 bit in all modes although
    		 * it is only used in RGMII mode
    		 */
    		if (priv->speed == 100)
    			ecntrl |= ECNTRL_R100;
    		break;
    	default:
    		printf("%s: Speed was bad\n", dev->name);
    		break;
    
    
    	out_be32(&regs->ecntrl, ecntrl);
    	out_be32(&regs->maccfg2, maccfg2);
    
    	printf("Speed: %d, %s duplex%s\n", priv->speed,
    			(priv->duplexity) ? "full" : "half",
    			(priv->flags & TSEC_FIBER) ? ", fiber mode" : "");
    
    }
    
    /* Set up the buffers and their descriptors, and bring up the
    
    static void startup_tsec(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    
    	tsec_t *regs = priv->regs;
    
    
    	/* Point to the buffer descriptors */
    
    	out_be32(&regs->tbase, (unsigned int)(&rtx.txbd[txIdx]));
    	out_be32(&regs->rbase, (unsigned int)(&rtx.rxbd[rxIdx]));
    
    
    	/* Initialize the Rx Buffer descriptors */
    	for (i = 0; i < PKTBUFSRX; i++) {
    		rtx.rxbd[i].status = RXBD_EMPTY;
    		rtx.rxbd[i].length = 0;
    
    		rtx.rxbd[i].bufPtr = (uint) NetRxPackets[i];
    
    	rtx.rxbd[PKTBUFSRX - 1].status |= RXBD_WRAP;
    
    
    	/* Initialize the TX Buffer Descriptors */
    
    	for (i = 0; i < TX_BUF_CNT; i++) {
    
    		rtx.txbd[i].status = 0;
    		rtx.txbd[i].length = 0;
    		rtx.txbd[i].bufPtr = 0;
    	}
    
    	rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP;
    
    	/* Start up the PHY */
    
    	if(priv->phyinfo)
    		phy_run_commands(priv, priv->phyinfo->startup);