Skip to content
Snippets Groups Projects
tsec.c 49.5 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
    
    
    /* 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
    };
    
    
    /* 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);
    
    /*
     * 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;
    }
    
    
    static 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,}
    	},
    	(struct phy_cmd[]) {     /* shutdown */
    		{miim_end,}
    	},
    };
    
    /* The 5411 id is 0x206070, the 5421 is 0x2060e0 */
    static struct phy_info phy_info_BCM5461S = {
    	0x02060c1,	/* 5461 ID */
    	"Broadcom BCM5461S",
    	0, /* not clear to me what minor revisions we can shift away */
    	(struct phy_cmd[]) { /* config */
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, 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_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr},
    		{miim_end,}
    	},
    	(struct phy_cmd[]) { /* shutdown */
    		{miim_end,}
    	},
    };
    
    static struct phy_info phy_info_BCM5464S = {
    	0x02060b1,	/* 5464 ID */
    	"Broadcom BCM5464S",
    	0, /* not clear to me what minor revisions we can shift away */
    	(struct phy_cmd[]) { /* config */
    
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, 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_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr},
    
    	(struct phy_cmd[]) { /* shutdown */
    
    static struct phy_info phy_info_BCM5482S =  {
    	0x0143bcb,
    	"Broadcom BCM5482S",
    	4,
    
    	(struct phy_cmd[]) { /* config */
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    
    		/* Setup read from auxilary control shadow register 7 */
    		{MIIM_BCM54xx_AUXCNTL, MIIM_BCM54xx_AUXCNTL_ENCODE(7), NULL},
    		/* Read Misc Control register and or in Ethernet@Wirespeed */
    		{MIIM_BCM54xx_AUXCNTL, 0, &mii_BCM54xx_wirespeed},
    
    		{MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
    
    		/* Initial config/enable of secondary SerDes interface */
    		{MIIM_BCM54XX_SHD, MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf), NULL},
    		/* Write intial value to secondary SerDes Contol */
    		{MIIM_BCM54XX_EXP_SEL, MIIM_BCM54XX_EXP_SEL_SSD | 0, NULL},
    		{MIIM_BCM54XX_EXP_DATA, MIIM_CONTROL_RESTART, NULL},
    		/* Enable copper/fiber auto-detect */
    		{MIIM_BCM54XX_SHD, MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201)},
    
    		{miim_end,}
    	},
    	(struct phy_cmd[]) { /* startup */
    		/* Status is read once to clear old link state */
    		{MIIM_STATUS, miim_read, NULL},
    
    		/* Determine copper/fiber, auto-negotiate, and read the result */
    		{MIIM_STATUS, miim_read, &mii_parse_BCM5482_sr},
    
    		{miim_end,}
    	},
    	(struct phy_cmd[]) { /* shutdown */
    		{miim_end,}
    	},
    };
    
    
    static struct phy_info phy_info_M88E1011S = {
    
    	0x01410c6,
    	"Marvell 88E1011S",
    	4,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(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,}
    	},
    	(struct phy_cmd[]) {	/* shutdown */
    		{miim_end,}
    	},
    
    static struct phy_info phy_info_M88E1111S = {
    
    	0x01410cc,
    	"Marvell 88E1111S",
    	4,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(struct phy_cmd[]) {	/* config */
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{0x1b, 0x848f, &mii_m88e1111s_setmode},
    		{0x14, 0x0cd2, NULL}, /* Delay RGMII TX and RX */
    		{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,}
    	},
    	(struct phy_cmd[]) {	/* shutdown */
    		{miim_end,}
    	},
    
    static struct phy_info phy_info_M88E1118 = {
    
    	0x01410e1,
    	"Marvell 88E1118",
    	4,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(struct phy_cmd[]) {	/* config */
    
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{0x16, 0x0002, NULL}, /* Change Page Number */
    		{0x15, 0x1070, NULL}, /* Delay RGMII TX and RX */
    
    		{0x16, 0x0003, NULL}, /* Change Page Number */
    		{0x10, 0x021e, NULL}, /* Adjust LED control */
    		{0x16, 0x0000, NULL}, /* Change Page Number */
    
    		{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,}
    
    Peter Tyser's avatar
    Peter Tyser committed
    	},
    	(struct phy_cmd[]) {	/* startup */
    
    		{0x16, 0x0000, NULL}, /* Change Page Number */
    		/* 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,}
    
    Peter Tyser's avatar
    Peter Tyser committed
    	},
    	(struct phy_cmd[]) {	/* shutdown */
    
    		{miim_end,}
    
    Peter Tyser's avatar
    Peter Tyser committed
    	},
    
    /*
     *  Since to access LED register we need do switch the page, we
     * do LED configuring in the miim_read-like function as follows
     */
    
    static uint mii_88E1121_set_led (uint mii_reg, struct tsec_private *priv)
    
    {
    	uint pg;
    
    	/* Switch the page to access the led register */
    	pg = read_phy_reg(priv, MIIM_88E1121_PHY_PAGE);
    	write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, MIIM_88E1121_PHY_LED_PAGE);
    
    	/* Configure leds */
    	write_phy_reg(priv, MIIM_88E1121_PHY_LED_CTRL,
    		      MIIM_88E1121_PHY_LED_DEF);
    
    	/* Restore the page pointer */
    	write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, pg);
    	return 0;
    }
    
    
    static struct phy_info phy_info_M88E1121R = {
    
    	0x01410cb,
    	"Marvell 88E1121R",
    	4,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(struct phy_cmd[]) {	/* config */
    		/* Reset and configure the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
    		{MIIM_ANAR, MIIM_ANAR_INIT, NULL},
    		/* Configure leds */
    		{MIIM_88E1121_PHY_LED_CTRL, miim_read, &mii_88E1121_set_led},
    		{MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
    		/* Disable IRQs and de-assert interrupt */
    		{MIIM_88E1121_PHY_IRQ_EN, 0, NULL},
    		{MIIM_88E1121_PHY_IRQ_STATUS, miim_read, NULL},
    		{miim_end,}
    	},
    	(struct phy_cmd[]) {	/* startup */
    		/* Status is read once to clear old link state */
    		{MIIM_STATUS, miim_read, NULL},
    		{MIIM_STATUS, miim_read, &mii_parse_sr},
    		{MIIM_STATUS, miim_read, &mii_parse_link},
    		{miim_end,}
    	},
    	(struct phy_cmd[]) {	/* shutdown */
    		{miim_end,}
    	},
    
    static unsigned int m88e1145_setmode(uint mii_reg, struct tsec_private *priv)
    {
    	uint mii_data = read_phy_reg(priv, mii_reg);
    
    	/* Setting MIIM_88E1145_PHY_EXT_CR */
    	if (priv->flags & TSEC_REDUCED)
    		return mii_data |
    
    		    MIIM_M88E1145_RGMII_RX_DELAY | MIIM_M88E1145_RGMII_TX_DELAY;
    
    	else
    		return mii_data;
    }
    
    static struct phy_info phy_info_M88E1145 = {
    	0x01410cd,
    	"Marvell 88E1145",
    	4,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(struct phy_cmd[]) {	/* config */
    		/* Reset the PHY */
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    
    		/* Errata E0, E1 */
    		{29, 0x001b, NULL},
    		{30, 0x418f, NULL},
    		{29, 0x0016, NULL},
    		{30, 0xa2da, NULL},
    
    		/* Configure the PHY */
    		{MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
    		{MIIM_ANAR, MIIM_ANAR_INIT, NULL},
    		{MIIM_88E1011_PHY_SCR, MIIM_88E1011_PHY_MDI_X_AUTO, NULL},
    		{MIIM_88E1145_PHY_EXT_CR, 0, &m88e1145_setmode},
    		{MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
    		{MIIM_CONTROL, MIIM_CONTROL_INIT, NULL},
    		{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},
    		{MIIM_88E1111_PHY_LED_CONTROL, MIIM_88E1111_PHY_LED_DIRECT, NULL},
    		/* Read the Status */
    		{MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr},
    		{miim_end,}
    	},
    	(struct phy_cmd[]) {	/* shutdown */
    		{miim_end,}
    	},
    
    static struct phy_info phy_info_cis8204 = {
    
    	0x3f11,
    	"Cicada Cis8204",
    	6,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	(struct phy_cmd[]) {	/* config */
    		/* Override PHY config settings */
    		{MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
    		/* Configure some basic stuff */
    		{MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
    		{MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT,
    		 &mii_cis8204_fixled},
    		{MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT,
    		 &mii_cis8204_setmode},
    		{miim_end,}
    	},
    	(struct phy_cmd[]) {	/* startup */
    		/* Read the Status (2x to make sure link is right) */