Skip to content
Snippets Groups Projects
tsec.c 44.2 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.
     *
    
    Andy Fleming's avatar
    Andy Fleming committed
     * 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"
    
    #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];
    
    /* The tsec_info structure contains 3 values which the
     * driver uses to determine how to operate a given ethernet
    
     * device. The information needed is:
    
     *  phyaddr - The address of the PHY which is attached to
    
     *	the given device.
    
     *  flags - This variable indicates whether the device
     *	supports gigabit speed ethernet, and whether it should be
     *	in reduced mode.
    
     *
     *  phyregidx - This variable specifies which ethernet device
    
     *	controls the MII Management registers which are connected
    
     *	to the PHY.  For now, only TSEC1 (index 0) has
    
     *	access to the PHYs, so all of the entries have "0".
    
     *
     * The values specified in the table are taken from the board's
     * config file in include/configs/.  When implementing a new
     * board with ethernet capability, it is necessary to define:
    
     *   TSECn_PHY_ADDR
     *   TSECn_PHYIDX
    
     * for n = 1,2,3, etc.  And for FEC:
    
     *   FEC_PHY_ADDR
     *   FEC_PHYIDX
     */
    static struct tsec_info_struct tsec_info[] = {
    
    #ifdef CONFIG_TSEC1
    	{TSEC1_PHY_ADDR, TSEC1_FLAGS, TSEC1_PHYIDX},
    
    #else
    
    #ifdef CONFIG_TSEC2
    	{TSEC2_PHY_ADDR, TSEC2_FLAGS, TSEC2_PHYIDX},
    
    #else
    
    #endif
    #ifdef CONFIG_MPC85XX_FEC
    
    	{FEC_PHY_ADDR, FEC_FLAGS, FEC_PHYIDX},
    
    #ifdef CONFIG_TSEC3
    	{TSEC3_PHY_ADDR, TSEC3_FLAGS, TSEC3_PHYIDX},
    
    #ifdef CONFIG_TSEC4
    	{TSEC4_PHY_ADDR, TSEC4_FLAGS, TSEC4_PHYIDX},
    
    #endif	/* CONFIG_TSEC4 */
    #endif	/* CONFIG_MPC85XX_FEC */
    
    #define MAXCONTROLLERS	(4)
    
    
    static int relocated = 0;
    
    static struct tsec_private *privlist[MAXCONTROLLERS];
    
    
    #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 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);
    
    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
    
    
    /* Initialize device structure. Returns success if PHY
     * initialization succeeded (i.e. if it recognizes the PHY)
     */
    
    int tsec_initialize(bd_t * bis, int index, char *devname)
    
    	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)
    
    		return 0;
    
    	privlist[index] = priv;
    
    	priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index * TSEC_SIZE);
    
    	priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR +
    
    					    tsec_info[index].phyregidx *
    					    TSEC_SIZE);
    
    
    	priv->phyaddr = tsec_info[index].phyaddr;
    
    	priv->flags = tsec_info[index].flags;
    
    	sprintf(dev->name, 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 */
    	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));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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);
    
    /* Write value to the device's PHY through the registers
     * specified in priv, modifying the register specified in regnum.
     * It will wait for the write to be done (or for a timeout to
     * expire) before exiting
     */
    
    void write_any_phy_reg(struct tsec_private *priv, uint phyid, uint regnum, uint value)
    
    {
    	volatile tsec_t *regbase = priv->phyregs;
    
    	int timeout = 1000000;
    
    
    	regbase->miimadd = (phyid << 8) | regnum;
    	regbase->miimcon = value;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    	timeout = 1000000;
    	while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ;
    
    /* #define to provide old write_phy_reg functionality without duplicating code */
    #define write_phy_reg(priv, regnum, value) write_any_phy_reg(priv,priv->phyaddr,regnum,value)
    
    
    /* Reads register regnum on the device's PHY through the
    
     * registers specified in priv.	 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 read_any_phy_reg(struct tsec_private *priv, uint phyid, uint regnum)
    
    	volatile tsec_t *regbase = priv->phyregs;
    
    	/* Put the address of the phy, and the register
    	 * number into MIIMADD */
    	regbase->miimadd = (phyid << 8) | regnum;
    
    
    	/* Clear the command register, and wait */
    	regbase->miimcom = 0;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    
    	/* Initiate a read command, and wait */
    	regbase->miimcom = MIIM_READ_COMMAND;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    
    	/* Wait for the the indication that the read is done */
    
    	while ((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;
    
    
    	/* Grab the value read from the PHY */
    	value = regbase->miimstat;
    
    	return value;
    }
    
    
    /* #define to provide old read_phy_reg functionality without duplicating code */
    #define read_phy_reg(priv,regnum) read_any_phy_reg(priv,priv->phyaddr,regnum)
    
    
    /* 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 *regs = (volatile tsec_t *)(TSEC_BASE_ADDR);
    
    
    	/* Assign a Physical address to the TBI */
    
    	regs->tbipa = CFG_TBIPA_VALUE;
    
    	regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE);
    
    	regs->tbipa = CFG_TBIPA_VALUE;
    
    
    	/* Reset MII (due to new addresses) */
    	priv->phyregs->miimcfg = MIIMCFG_RESET;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	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);
    
    	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
     */
    uint mii_cr_init(uint mii_reg, struct tsec_private * priv)
    
    	if (priv->flags & TSEC_GIGABIT)
    
    		return MIIM_CONTROL_INIT;
    
    		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)) {
    
    		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) {
    
    			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) */
    
    		if (mii_reg & MIIM_STATUS_LINK)
    			priv->link = 1;
    		else
    			priv->link = 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)
    
    	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
     */
    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
    
     * information
     */
    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
    
     * information
     */
    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
    
     * information
     */
    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
     */
    uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv)
    
    {
    	uint phyid;
    	volatile tsec_t *regbase = priv->phyregs;
    
    	int timeout = 1000000;
    
    	for (phyid = 0; phyid < 4; phyid++) {
    
    		regbase->miimadd = (phyid << 8) | mii_reg;
    		regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT;
    
    Eran Liberty's avatar
    Eran Liberty committed
    		asm("sync");
    
    		timeout = 1000000;
    		while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ;
    
    	return MIIM_CIS8204_SLEDCON_INIT;
    
    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;
    }
    
    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(volatile tsec_t * regs)
    
    {
    	/* Clear IEVENT */
    	regs->ievent = IEVENT_INIT_CLEAR;
    
    	regs->imask = IMASK_INIT_CLEAR;
    
    	regs->hash.iaddr0 = 0;
    	regs->hash.iaddr1 = 0;
    	regs->hash.iaddr2 = 0;
    	regs->hash.iaddr3 = 0;
    	regs->hash.iaddr4 = 0;
    	regs->hash.iaddr5 = 0;
    	regs->hash.iaddr6 = 0;
    	regs->hash.iaddr7 = 0;
    
    	regs->hash.gaddr0 = 0;
    	regs->hash.gaddr1 = 0;
    	regs->hash.gaddr2 = 0;
    	regs->hash.gaddr3 = 0;
    	regs->hash.gaddr4 = 0;
    	regs->hash.gaddr5 = 0;
    	regs->hash.gaddr6 = 0;
    	regs->hash.gaddr7 = 0;
    
    	regs->rctrl = 0x00000000;
    
    	/* Init RMON mib registers */
    	memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));
    
    	regs->rmon.cam1 = 0xffffffff;
    	regs->rmon.cam2 = 0xffffffff;
    
    	regs->mrblr = MRBLR_INIT_SETTINGS;
    
    	regs->minflr = MINFLR_INIT_SETTINGS;
    
    	regs->attr = ATTR_INIT_SETTINGS;
    	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;
    	volatile tsec_t *regs = priv->regs;
    
    
    	if (priv->link) {
    		if (priv->duplexity != 0)
    
    			regs->maccfg2 |= MACCFG2_FULL_DUPLEX;
    		else
    			regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX);
    
    
    		switch (priv->speed) {
    		case 1000:
    			regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF))
    					 | MACCFG2_GMII);
    			break;
    		case 100:
    		case 10:
    			regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF))
    					 | MACCFG2_MII);
    
    
    			/* Set R100 bit in all modes although
    			 * it is only used in RGMII mode
    
    			if (priv->speed == 100)
    
    				regs->ecntrl |= ECNTRL_R100;
    			else
    				regs->ecntrl &= ~(ECNTRL_R100);
    			break;
    		default:
    			printf("%s: Speed was bad\n", dev->name);
    			break;
    
    		}
    
    		printf("Speed: %d, %s duplex\n", priv->speed,
    
    		       (priv->duplexity) ? "full" : "half");
    
    
    	} else {
    		printf("%s: No link.\n", dev->name);
    	}
    }
    
    /* 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;
    	volatile tsec_t *regs = priv->regs;
    
    
    	/* Point to the buffer descriptors */
    	regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);
    	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);
    
    	/* Enable Transmit and Receive */
    	regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
    
    	/* Tell the DMA it is clear to go */
    	regs->dmactrl |= DMACTRL_INIT_SETTINGS;
    	regs->tstat = TSTAT_CLEAR_THALT;
    
    	regs->rstat = RSTAT_CLEAR_RHALT;
    
    	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
    }
    
    
    /* This returns the status bits of the device.	The return value
    
     * is never checked, and this is what the 8260 driver did, so we
    
     * do the same.	 Presumably, this would be zero if there were no
    
     * errors
     */
    static int tsec_send(struct eth_device *dev, volatile void *packet, int length)
    
    {
    	int i;
    	int result = 0;
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	/* Find an empty buffer descriptor */
    
    	for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
    
    		if (i >= TOUT_LOOP) {
    
    			debug("%s: tsec: tx buffers full\n", dev->name);
    
    	rtx.txbd[txIdx].bufPtr = (uint) packet;
    
    	rtx.txbd[txIdx].length = length;
    
    	rtx.txbd[txIdx].status |=
    	    (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);
    
    
    	/* Tell the DMA to go */
    	regs->tstat = TSTAT_CLEAR_THALT;
    
    	/* Wait for buffer to be transmitted */
    
    	for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
    
    		if (i >= TOUT_LOOP) {
    
    			debug("%s: tsec: tx error\n", dev->name);
    
    			return result;
    		}
    	}
    
    	txIdx = (txIdx + 1) % TX_BUF_CNT;
    	result = rtx.txbd[txIdx].status & TXBD_STATS;
    
    	return result;
    }
    
    
    static int tsec_recv(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    	while (!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) {
    
    
    		length = rtx.rxbd[rxIdx].length;
    
    		/* Send the packet up if there were no errors */
    		if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) {
    			NetReceive(NetRxPackets[rxIdx], length - 4);
    
    		} else {
    			printf("Got error %x\n",
    
    			       (rtx.rxbd[rxIdx].status & RXBD_STATS));
    
    		}
    
    		rtx.rxbd[rxIdx].length = 0;
    
    		/* Set the wrap bit if this is the last element in the list */
    
    		rtx.rxbd[rxIdx].status =
    		    RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0);
    
    
    		rxIdx = (rxIdx + 1) % PKTBUFSRX;
    	}
    
    
    	if (regs->ievent & IEVENT_BSY) {
    
    		regs->ievent = IEVENT_BSY;
    		regs->rstat = RSTAT_CLEAR_RHALT;
    	}
    
    	return -1;
    
    }
    
    
    /* Stop the interface */
    
    static void tsec_halt(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
    	regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);
    
    
    	while (!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))) ;
    
    
    	regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);
    
    
    	/* Shut down the PHY, as needed */
    
    	if(priv->phyinfo)
    		phy_run_commands(priv, priv->phyinfo->shutdown);
    
    struct phy_info phy_info_M88E1149S = {
    
    	0x1410ca,
    	"Marvell 88E1149S",
    	4,
    	(struct phy_cmd[]){     /* config */
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{0x1d, 0x1f, NULL},
    		{0x1e, 0x200c, NULL},
    		{0x1d, 0x5, NULL},
    		{0x1e, 0x0, NULL},
    		{0x1e, 0x100, NULL},
    		{MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
    		{MIIM_ANAR, MIIM_ANAR_INIT, NULL},
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
    		{miim_end,}
    	},
    	(struct phy_cmd[]){     /* startup */
    		/* Status is read once to clear old link state */
    		{MIIM_STATUS, miim_read, NULL},
    		/* Auto-negotiate */
    		{MIIM_STATUS, miim_read, &mii_parse_sr},
    		/* Read the status */
    		{MIIM_88E1011_PHY_STATUS, miim_read,
    		 &mii_parse_88E1011_psr},
    		{miim_end,}
    	},