]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - drivers/net/tsec.c
tsec: Clean up Broadcom PHY status parsing
[karo-tx-uboot.git] / drivers / net / tsec.c
index 108cebd8797321b272e350a0ba76e95fe79ade66..76902bcd83505d2262d7f45737375d26156f2b1f 100644 (file)
@@ -5,7 +5,7 @@
  * terms of the GNU Public License, Version 2, incorporated
  * herein by reference.
  *
- * Copyright 2004, 2007 Freescale Semiconductor, Inc.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc.
  * (C) Copyright 2003, Motorola, Inc.
  * author Andy Fleming
  *
@@ -16,9 +16,9 @@
 #include <malloc.h>
 #include <net.h>
 #include <command.h>
+#include <tsec.h>
+#include <asm/errno.h>
 
-#if defined(CONFIG_TSEC_ENET)
-#include "tsec.h"
 #include "miiphy.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -33,69 +33,10 @@ typedef volatile struct rtxbd {
        rxbd8_t rxbd[PKTBUFSRX];
 } RTXBD;
 
-struct tsec_info_struct {
-       unsigned int phyaddr;
-       u32 flags;
-       unsigned int phyregidx;
-};
-
-/* 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
-       {0, 0, 0},
-#endif
-#ifdef CONFIG_TSEC2
-       {TSEC2_PHY_ADDR, TSEC2_FLAGS, TSEC2_PHYIDX},
-#else
-       {0, 0, 0},
-#endif
-#ifdef CONFIG_MPC85XX_FEC
-       {FEC_PHY_ADDR, FEC_FLAGS, FEC_PHYIDX},
-#else
-#ifdef CONFIG_TSEC3
-       {TSEC3_PHY_ADDR, TSEC3_FLAGS, TSEC3_PHYIDX},
-#else
-       {0, 0, 0},
-#endif
-#ifdef CONFIG_TSEC4
-       {TSEC4_PHY_ADDR, TSEC4_FLAGS, TSEC4_PHYIDX},
-#else
-       {0, 0, 0},
-#endif /* CONFIG_TSEC4 */
-#endif /* CONFIG_MPC85XX_FEC */
-};
-
-#define MAXCONTROLLERS (4)
-
-static int relocated = 0;
+#define MAXCONTROLLERS (8)
 
 static struct tsec_private *privlist[MAXCONTROLLERS];
+static int num_tsecs = 0;
 
 #ifdef __GNUC__
 static RTXBD rtx __attribute__ ((aligned(8)));
@@ -116,7 +57,6 @@ 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,
@@ -128,10 +68,51 @@ static int tsec_miiphy_read(char *devname, unsigned char addr,
 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
+};
+
+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, int index, char *devname)
+int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info)
 {
        struct eth_device *dev;
        int i;
@@ -149,16 +130,15 @@ int tsec_initialize(bd_t * bis, int index, char *devname)
        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);
+       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[index].phyaddr;
-       priv->flags = tsec_info[index].flags;
+       priv->phyaddr = tsec_info->phyaddr;
+       priv->flags = tsec_info->flags;
 
-       sprintf(dev->name, devname);
+       sprintf(dev->name, tsec_info->devname);
        dev->iobase = 0;
        dev->priv = priv;
        dev->init = tsec_init;
@@ -177,6 +157,7 @@ int tsec_initialize(bd_t * bis, int index, char *devname)
 
        /* Reset the MAC */
        priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;
+       udelay(2);  /* Soft Reset must be asserted for 3 TX clocks */
        priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);
 
 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \
@@ -215,7 +196,10 @@ int tsec_init(struct eth_device *dev, bd_t * bd)
        for (i = 0; i < MAC_ADDR_LEN; i++) {
                tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];
        }
-       regs->macstnaddr1 = *((uint *) (tmpbuf));
+       tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) |
+                 tmpbuf[3];
+
+       regs->macstnaddr1 = tempval;
 
        tempval = *((uint *) (tmpbuf + 4));
 
@@ -232,62 +216,87 @@ int tsec_init(struct eth_device *dev, bd_t * bd)
        startup_tsec(dev);
 
        /* If there's no link, fail */
-       return priv->link;
-
+       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_phy_reg(struct tsec_private *priv, uint regnum, uint value)
+/* Writes the given phy's reg with value, using the specified MDIO regs */
+static void tsec_local_mdio_write(volatile tsec_mdio_t *phyregs, uint addr,
+               uint reg, uint value)
 {
-       volatile tsec_t *regbase = priv->phyregs;
-       uint phyid = priv->phyaddr;
        int timeout = 1000000;
 
-       regbase->miimadd = (phyid << 8) | regnum;
-       regbase->miimcon = value;
+       phyregs->miimadd = (addr << 8) | reg;
+       phyregs->miimcon = value;
        asm("sync");
 
        timeout = 1000000;
-       while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ;
+       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
- * registers specified in priv.         It lowers and raises the read
+ * 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 read_phy_reg(struct tsec_private *priv, uint regnum)
+uint tsec_local_mdio_read(volatile tsec_mdio_t *phyregs, uint phyid, uint regnum)
 {
        uint value;
-       volatile tsec_t *regbase = priv->phyregs;
-       uint phyid = priv->phyaddr;
 
        /* Put the address of the phy, and the register
         * number into MIIMADD */
-       regbase->miimadd = (phyid << 8) | regnum;
+       phyregs->miimadd = (phyid << 8) | regnum;
 
        /* Clear the command register, and wait */
-       regbase->miimcom = 0;
+       phyregs->miimcom = 0;
        asm("sync");
 
        /* Initiate a read command, and wait */
-       regbase->miimcom = MIIM_READ_COMMAND;
+       phyregs->miimcom = MIIM_READ_COMMAND;
        asm("sync");
 
        /* Wait for the the indication that the read is done */
-       while ((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;
+       while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;
 
        /* Grab the value read from the PHY */
-       value = regbase->miimstat;
+       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 \
+               )
+
+/* Force the TBI PHY into 1000Mbps full duplex when in SGMII mode */
+#define TBICR_SETTINGS ( \
+               TBICR_PHY_RESET \
+               | 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->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,
+                       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
@@ -296,12 +305,10 @@ 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);
+       volatile tsec_t *regs = priv->regs;
 
        /* 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;
+       regs->tbipa = CONFIG_SYS_TBIPA_VALUE;
        asm("sync");
 
        /* Reset MII (due to new addresses) */
@@ -311,9 +318,6 @@ static int init_phy(struct eth_device *dev)
        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);
@@ -325,6 +329,9 @@ static int init_phy(struct eth_device *dev)
                return 0;
        }
 
+       if (regs->ecntrl & ECNTRL_SGMII_MODE)
+               tsec_configure_serdes(priv);
+
        priv->phyinfo = curphy;
 
        phy_run_commands(priv, priv->phyinfo->config);
@@ -344,8 +351,8 @@ uint mii_cr_init(uint mii_reg, struct tsec_private * priv)
                return MIIM_CR_INIT;
 }
 
-/* Parse the status register for link, and then do
- * auto-negotiation
+/*
+ * Wait for auto-negotiation to complete, then determine link
  */
 uint mii_parse_sr(uint mii_reg, struct tsec_private * priv)
 {
@@ -354,8 +361,7 @@ uint mii_parse_sr(uint mii_reg, struct tsec_private * priv)
         * (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)) {
+       if ((mii_reg & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) {
                int i = 0;
 
                puts("Waiting for PHY auto negotiation to complete");
@@ -369,6 +375,12 @@ uint mii_parse_sr(uint mii_reg, struct tsec_private * priv)
                                return 0;
                        }
 
+                       if (ctrlc()) {
+                               puts("user interrupt!\n");
+                               priv->link = 0;
+                               return -EINTR;
+                       }
+
                        if ((i++ % 1000) == 0) {
                                putc('.');
                        }
@@ -376,15 +388,15 @@ uint mii_parse_sr(uint mii_reg, struct tsec_private * priv)
                        mii_reg = read_phy_reg(priv, MIIM_STATUS);
                }
                puts(" done\n");
-               priv->link = 1;
+
+               /* 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) */
-       } else {
-               if (mii_reg & MIIM_STATUS_LINK)
-                       priv->link = 1;
-               else
-                       priv->link = 0;
        }
 
+       priv->link = mii_reg & MIIM_STATUS_LINK ? 1 : 0;
+
        return 0;
 }
 
@@ -458,56 +470,59 @@ uint mii_parse_link(uint mii_reg, struct tsec_private *priv)
        return 0;
 }
 
+/*
+ * "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.
+ */
+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.
  */
 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:
-                       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;
+       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;
        }
 
        return 0;
@@ -571,6 +586,64 @@ uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv)
        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;
+                               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
  */
@@ -651,7 +724,7 @@ uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private * priv)
 uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv)
 {
        uint phyid;
-       volatile tsec_t *regbase = priv->phyregs;
+       volatile tsec_mdio_t *regbase = priv->phyregs;
        int timeout = 1000000;
 
        for (phyid = 0; phyid < 4; phyid++) {
@@ -1003,6 +1076,34 @@ struct phy_info phy_info_BCM5464S = {
        },
 };
 
+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},
+               {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,}
+       },
+};
+
 struct phy_info phy_info_M88E1011S = {
        0x01410c6,
        "Marvell 88E1011S",
@@ -1066,6 +1167,91 @@ struct phy_info phy_info_M88E1111S = {
                           },
 };
 
+struct phy_info phy_info_M88E1118 = {
+       0x01410e1,
+       "Marvell 88E1118",
+       4,
+       (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,}
+               },
+       (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,}
+               },
+       (struct phy_cmd[]){     /* shutdown */
+               {miim_end,}
+               },
+};
+
+/*
+ *  Since to access LED register we need do switch the page, we
+ * do LED configuring in the miim_read-like function as follows
+ */
+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;
+}
+
+struct phy_info phy_info_M88E1121R = {
+       0x01410cb,
+       "Marvell 88E1121R",
+       4,
+       (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);
@@ -1180,6 +1366,35 @@ struct phy_info phy_info_cis8201 = {
                           {miim_end,}
                           },
 };
+struct phy_info phy_info_VSC8211 = {
+       0xfc4b,
+       "Vitesse VSC8211",
+       4,
+       (struct phy_cmd[]) { /* config */
+                          /* Override PHY config settings */
+                          {MIIM_CIS8201_AUX_CONSTAT,
+                           MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
+                          /* Set up the interface mode */
+                          {MIIM_CIS8201_EXT_CON1,
+                           MIIM_CIS8201_EXTCON1_INIT, NULL},
+                          /* Configure some basic stuff */
+                          {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]) { /* startup */
+                          /* Read the Status (2x to make sure link is right) */
+                          {MIIM_STATUS, miim_read, NULL},
+                          /* Auto-negotiate */
+                          {MIIM_STATUS, miim_read, &mii_parse_sr},
+                          /* Read the status */
+                          {MIIM_CIS8201_AUX_CONSTAT, miim_read,
+                           &mii_parse_cis8201},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]) { /* shutdown */
+                          {miim_end,}
+       },
+};
 struct phy_info phy_info_VSC8244 = {
        0x3f1b,
        "Vitesse VSC8244",
@@ -1205,6 +1420,91 @@ struct phy_info phy_info_VSC8244 = {
                           },
 };
 
+struct phy_info phy_info_VSC8641 = {
+       0x7043,
+       "Vitesse VSC8641",
+       4,
+       (struct phy_cmd[]){     /* config */
+                          /* Configure some basic stuff */
+                          {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]){     /* startup */
+                          /* Read the Status (2x to make sure link is right) */
+                          {MIIM_STATUS, miim_read, NULL},
+                          /* Auto-negotiate */
+                          {MIIM_STATUS, miim_read, &mii_parse_sr},
+                          /* Read the status */
+                          {MIIM_VSC8244_AUX_CONSTAT, miim_read,
+                           &mii_parse_vsc8244},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]){     /* shutdown */
+                          {miim_end,}
+                          },
+};
+
+struct phy_info phy_info_VSC8221 = {
+       0xfc55,
+       "Vitesse VSC8221",
+       4,
+       (struct phy_cmd[]){     /* config */
+                          /* Configure some basic stuff */
+                          {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]){     /* startup */
+                          /* Read the Status (2x to make sure link is right) */
+                          {MIIM_STATUS, miim_read, NULL},
+                          /* Auto-negotiate */
+                          {MIIM_STATUS, miim_read, &mii_parse_sr},
+                          /* Read the status */
+                          {MIIM_VSC8244_AUX_CONSTAT, miim_read,
+                           &mii_parse_vsc8244},
+                          {miim_end,}
+                          },
+       (struct phy_cmd[]){     /* shutdown */
+                          {miim_end,}
+                          },
+};
+
+struct phy_info phy_info_VSC8601 = {
+               0x00007042,
+               "Vitesse VSC8601",
+               4,
+               (struct phy_cmd[]){     /* config */
+                               /* Override PHY config settings */
+                               /* Configure some basic stuff */
+                               {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
+#ifdef CONFIG_SYS_VSC8601_SKEWFIX
+                               {MIIM_VSC8601_EPHY_CON,MIIM_VSC8601_EPHY_CON_INIT_SKEW,NULL},
+#if defined(CONFIG_SYS_VSC8601_SKEW_TX) && defined(CONFIG_SYS_VSC8601_SKEW_RX)
+                               {MIIM_EXT_PAGE_ACCESS,1,NULL},
+#define VSC8101_SKEW   (CONFIG_SYS_VSC8601_SKEW_TX<<14)|(CONFIG_SYS_VSC8601_SKEW_RX<<12)
+                               {MIIM_VSC8601_SKEW_CTRL,VSC8101_SKEW,NULL},
+                               {MIIM_EXT_PAGE_ACCESS,0,NULL},
+#endif
+#endif
+                               {MIIM_ANAR, MIIM_ANAR_INIT, NULL},
+                               {MIIM_CONTROL, MIIM_CONTROL_RESTART, &mii_cr_init},
+                               {miim_end,}
+                                },
+               (struct phy_cmd[]){     /* startup */
+                               /* Read the Status (2x to make sure link is right) */
+                               {MIIM_STATUS, miim_read, NULL},
+                               /* Auto-negotiate */
+                               {MIIM_STATUS, miim_read, &mii_parse_sr},
+                               /* Read the status */
+                               {MIIM_VSC8244_AUX_CONSTAT, miim_read,
+                                               &mii_parse_vsc8244},
+                               {miim_end,}
+                               },
+               (struct phy_cmd[]){     /* shutdown */
+                               {miim_end,}
+                               },
+};
+
+
 struct phy_info phy_info_dm9161 = {
        0x0181b88,
        "Davicom DM9161E",
@@ -1361,20 +1661,55 @@ struct phy_info phy_info_dp83865 = {
                           },
 };
 
+struct phy_info phy_info_rtl8211b = {
+       0x001cc91,
+       "RealTek RTL8211B",
+       4,
+       (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_RTL8211B_PHY_STATUS, miim_read, &mii_parse_RTL8211B_sr},
+               {miim_end,}
+       },
+       (struct phy_cmd[]){     /* shutdown */
+               {miim_end,}
+       },
+};
+
 struct phy_info *phy_info[] = {
        &phy_info_cis8204,
        &phy_info_cis8201,
        &phy_info_BCM5461S,
        &phy_info_BCM5464S,
+       &phy_info_BCM5482S,
        &phy_info_M88E1011S,
        &phy_info_M88E1111S,
+       &phy_info_M88E1118,
+       &phy_info_M88E1121R,
        &phy_info_M88E1145,
        &phy_info_M88E1149S,
        &phy_info_dm9161,
        &phy_info_lxt971,
+       &phy_info_VSC8211,
        &phy_info_VSC8244,
+       &phy_info_VSC8601,
+       &phy_info_VSC8641,
+       &phy_info_VSC8221,
        &phy_info_dp83865,
-       &phy_info_generic,
+       &phy_info_rtl8211b,
+       &phy_info_generic,      /* must be last; has ID 0 and 32 bit mask */
        NULL
 };
 
@@ -1406,9 +1741,8 @@ struct phy_info *get_phy_info(struct eth_device *dev)
                }
        }
 
-       if (theInfo == NULL) {
-               printf("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
-               return NULL;
+       if (theInfo == &phy_info_generic) {
+               printf("%s: No support for PHY id %x; assuming generic\n", dev->name, phy_ID);
        } else {
                debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID);
        }
@@ -1423,7 +1757,7 @@ void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd)
 {
        int i;
        uint result;
-       volatile tsec_t *phyregs = priv->phyregs;
+       volatile tsec_mdio_t *phyregs = priv->phyregs;
 
        phyregs->miimcfg = MIIMCFG_RESET;
 
@@ -1451,64 +1785,9 @@ void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd)
        }
 }
 
-/* Relocate the function pointers in the phy cmd lists */
-static void relocate_cmds(void)
-{
-       struct phy_cmd **cmdlistptr;
-       struct phy_cmd *cmd;
-       int i, j, k;
-
-       for (i = 0; phy_info[i]; i++) {
-               /* First thing's first: relocate the pointers to the
-                * PHY command structures (the structs were done) */
-               phy_info[i] = (struct phy_info *)((uint) phy_info[i]
-                                                 + gd->reloc_off);
-               phy_info[i]->name += gd->reloc_off;
-               phy_info[i]->config =
-                   (struct phy_cmd *)((uint) phy_info[i]->config
-                                      + gd->reloc_off);
-               phy_info[i]->startup =
-                   (struct phy_cmd *)((uint) phy_info[i]->startup
-                                      + gd->reloc_off);
-               phy_info[i]->shutdown =
-                   (struct phy_cmd *)((uint) phy_info[i]->shutdown
-                                      + gd->reloc_off);
-
-               cmdlistptr = &phy_info[i]->config;
-               j = 0;
-               for (; cmdlistptr <= &phy_info[i]->shutdown; cmdlistptr++) {
-                       k = 0;
-                       for (cmd = *cmdlistptr;
-                            cmd->mii_reg != miim_end;
-                            cmd++) {
-                               /* Only relocate non-NULL pointers */
-                               if (cmd->funct)
-                                       cmd->funct += gd->reloc_off;
-
-                               k++;
-                       }
-                       j++;
-               }
-       }
-
-       relocated = 1;
-}
-
 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \
        && !defined(BITBANGMII)
 
-struct tsec_private *get_priv_for_phy(unsigned char phyaddr)
-{
-       int i;
-
-       for (i = 0; i < MAXCONTROLLERS; i++) {
-               if (privlist[i]->phyaddr == phyaddr)
-                       return privlist[i];
-       }
-
-       return NULL;
-}
-
 /*
  * Read a MII PHY register.
  *
@@ -1519,14 +1798,14 @@ static int tsec_miiphy_read(char *devname, unsigned char addr,
                            unsigned char reg, unsigned short *value)
 {
        unsigned short ret;
-       struct tsec_private *priv = get_priv_for_phy(addr);
+       struct tsec_private *priv = privlist[0];
 
        if (NULL == priv) {
                printf("Can't read PHY at address %d\n", addr);
                return -1;
        }
 
-       ret = (unsigned short)read_phy_reg(priv, reg);
+       ret = (unsigned short)tsec_local_mdio_read(priv->phyregs, addr, reg);
        *value = ret;
 
        return 0;
@@ -1541,14 +1820,14 @@ static int tsec_miiphy_read(char *devname, unsigned char addr,
 static int tsec_miiphy_write(char *devname, unsigned char addr,
                             unsigned char reg, unsigned short value)
 {
-       struct tsec_private *priv = get_priv_for_phy(addr);
+       struct tsec_private *priv = privlist[0];
 
        if (NULL == priv) {
                printf("Can't write PHY at address %d\n", addr);
                return -1;
        }
 
-       write_phy_reg(priv, reg, value);
+       tsec_local_mdio_write(priv->phyregs, addr, reg, value);
 
        return 0;
 }
@@ -1596,5 +1875,3 @@ tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set)
        return 0;
 }
 #endif /* Multicast TFTP ? */
-
-#endif /* CONFIG_TSEC_ENET */