Merge branch 'master' of git://git.denx.de/u-boot-usb
[oweals/u-boot.git] / drivers / net / tsec.c
index 6a444186880160b68a0926cca4013d286e5040a1..9c8fe6244191db15ccb282a110e09450237b4c98 100644 (file)
@@ -5,7 +5,7 @@
  * terms of the GNU Public License, Version 2, incorporated
  * herein by reference.
  *
- * Copyright 2004-2009 Freescale Semiconductor, Inc.
+ * Copyright 2004-2010 Freescale Semiconductor, Inc.
  * (C) Copyright 2003, Motorola, Inc.
  * author Andy Fleming
  *
@@ -60,9 +60,9 @@ static void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd);
 static void adjust_link(struct eth_device *dev);
 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \
        && !defined(BITBANGMII)
-static int tsec_miiphy_write(char *devname, unsigned char addr,
+static int tsec_miiphy_write(const char *devname, unsigned char addr,
                             unsigned char reg, unsigned short value);
-static int tsec_miiphy_read(char *devname, unsigned char addr,
+static int tsec_miiphy_read(const char *devname, unsigned char addr,
                            unsigned char reg, unsigned short *value);
 #endif
 #ifdef CONFIG_MCAST_TFTP
@@ -95,14 +95,23 @@ static struct tsec_info_struct tsec_info[] = {
 #endif
 };
 
+/*
+ * Initialize all the TSEC devices
+ *
+ * Returns the number of TSEC devices that were initialized
+ */
 int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num)
 {
        int i;
+       int ret, count = 0;
 
-       for (i = 0; i < num; i++)
-               tsec_initialize(bis, &tsecs[i]);
+       for (i = 0; i < num; i++) {
+               ret = tsec_initialize(bis, &tsecs[i]);
+               if (ret > 0)
+                       count += ret;
+       }
 
-       return 0;
+       return count;
 }
 
 int tsec_standard_init(bd_t *bis)
@@ -281,12 +290,15 @@ static uint tsec_local_mdio_read(volatile tsec_mdio_t *phyregs,
                | TBIANA_FULL_DUPLEX \
                )
 
-/* Force the TBI PHY into 1000Mbps full duplex when in SGMII mode */
-#define TBICR_SETTINGS ( \
+/* 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_ANEG_ENABLE \
                | 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)
@@ -298,7 +310,7 @@ static void tsec_configure_serdes(struct tsec_private *priv)
        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);
+                       CONFIG_TSEC_TBICR_SETTINGS);
 }
 
 /* Discover which PHY is attached to the device, and configure it
@@ -365,11 +377,11 @@ static 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 & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) {
+       if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
                int i = 0;
 
                puts("Waiting for PHY auto negotiation to complete");
-               while (!(mii_reg & PHY_BMSR_AUTN_COMP)) {
+               while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
                        /*
                         * Timeout reached ?
                         */
@@ -415,17 +427,17 @@ static uint mii_parse_sr(uint mii_reg, struct tsec_private * priv)
 static uint mii_parse_link(uint mii_reg, struct tsec_private *priv)
 {
        /* We're using autonegotiation */
-       if (mii_reg & PHY_BMSR_AUTN_ABLE) {
+       if (mii_reg & BMSR_ANEGCAPABLE) {
                uint lpa = 0;
                uint gblpa = 0;
 
                /* Check for gigabit capability */
-               if (mii_reg & PHY_BMSR_EXT) {
+               if (mii_reg & BMSR_ERCAP) {
                        /* 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;
+                       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
@@ -445,29 +457,29 @@ static uint mii_parse_link(uint mii_reg, struct tsec_private *priv)
                        return 0;
                }
 
-               lpa = read_phy_reg(priv, PHY_ANAR);
-               lpa &= read_phy_reg(priv, PHY_ANLPAR);
+               lpa = read_phy_reg(priv, MII_ADVERTISE);
+               lpa &= read_phy_reg(priv, MII_LPA);
 
-               if (lpa & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) {
+               if (lpa & (LPA_100FULL | LPA_100HALF)) {
                        priv->speed = 100;
 
-                       if (lpa & PHY_ANLPAR_TXFD)
+                       if (lpa & LPA_100FULL)
                                priv->duplexity = 1;
 
-               } else if (lpa & PHY_ANLPAR_10FD)
+               } else if (lpa & LPA_10FULL)
                        priv->duplexity = 1;
        } else {
-               uint bmcr = read_phy_reg(priv, PHY_BMCR);
+               uint bmcr = read_phy_reg(priv, MII_BMCR);
 
                priv->speed = 10;
                priv->duplexity = 0;
 
-               if (bmcr & PHY_BMCR_DPLX)
+               if (bmcr & BMCR_FULLDPLX)
                        priv->duplexity = 1;
 
-               if (bmcr & PHY_BMCR_1000_MBPS)
+               if (bmcr & BMCR_SPEED1000)
                        priv->speed = 1000;
-               else if (bmcr & PHY_BMCR_100_MBPS)
+               else if (bmcr & BMCR_SPEED100)
                        priv->speed = 100;
        }
 
@@ -530,8 +542,106 @@ static uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv)
        }
 
        return 0;
+}
+
+/*
+ * 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);
+               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
  * information
  */
@@ -843,8 +953,9 @@ static void adjust_link(struct eth_device *dev)
                        break;
                }
 
-               printf("Speed: %d, %s duplex\n", priv->speed,
-                      (priv->duplexity) ? "full" : "half");
+               printf("Speed: %d, %s duplex%s\n", priv->speed,
+                      (priv->duplexity) ? "full" : "half",
+                      (priv->flags & TSEC_FIBER) ? ", fiber mode" : "");
 
        } else {
                printf("%s: No link.\n", dev->name);
@@ -983,7 +1094,8 @@ static void tsec_halt(struct eth_device *dev)
        regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
        regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);
 
-       while (!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))) ;
+       while ((regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))
+               != (IEVENT_GRSC | IEVENT_GTSC)) ;
 
        regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);
 
@@ -1091,15 +1203,20 @@ static struct phy_info phy_info_BCM5482S =  {
                /* 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},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, &mii_parse_sr},
-               /* Read the status */
-               {MIIM_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr},
+               /* Determine copper/fiber, auto-negotiate, and read the result */
+               {MIIM_STATUS, miim_read, &mii_parse_BCM5482_sr},
                {miim_end,}
        },
        (struct phy_cmd[]) { /* shutdown */
@@ -1522,20 +1639,41 @@ static struct phy_info phy_info_dm9161 = {
        },
 };
 
+/* micrel KSZ804  */
+static struct phy_info phy_info_ksz804 =  {
+       0x0022151,
+       "Micrel KSZ804 PHY",
+       4,
+       (struct phy_cmd[]) { /* config */
+               {MII_BMCR, BMCR_RESET, NULL},
+               {MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART, NULL},
+               {miim_end,}
+       },
+       (struct phy_cmd[]) { /* startup */
+               {MII_BMSR, miim_read, NULL},
+               {MII_BMSR, miim_read, &mii_parse_sr},
+               {MII_BMSR, miim_read, &mii_parse_link},
+               {miim_end,}
+       },
+       (struct phy_cmd[]) { /* shutdown */
+               {miim_end,}
+       }
+};
+
 /* a generic flavor.  */
 static struct phy_info phy_info_generic =  {
        0,
        "Unknown/Generic PHY",
        32,
        (struct phy_cmd[]) { /* config */
-               {PHY_BMCR, PHY_BMCR_RESET, NULL},
-               {PHY_BMCR, PHY_BMCR_AUTON|PHY_BMCR_RST_NEG, NULL},
+               {MII_BMCR, BMCR_RESET, NULL},
+               {MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART, NULL},
                {miim_end,}
        },
        (struct phy_cmd[]) { /* startup */
-               {PHY_BMSR, miim_read, NULL},
-               {PHY_BMSR, miim_read, &mii_parse_sr},
-               {PHY_BMSR, miim_read, &mii_parse_link},
+               {MII_BMSR, miim_read, NULL},
+               {MII_BMSR, miim_read, &mii_parse_sr},
+               {MII_BMSR, miim_read, &mii_parse_link},
                {miim_end,}
        },
        (struct phy_cmd[]) { /* shutdown */
@@ -1685,6 +1823,7 @@ static struct phy_info *phy_info[] = {
        &phy_info_M88E1145,
        &phy_info_M88E1149S,
        &phy_info_dm9161,
+       &phy_info_ksz804,
        &phy_info_lxt971,
        &phy_info_VSC8211,
        &phy_info_VSC8244,
@@ -1779,7 +1918,7 @@ static void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd)
  * Returns:
  *  0 on success
  */
-static int tsec_miiphy_read(char *devname, unsigned char addr,
+static int tsec_miiphy_read(const char *devname, unsigned char addr,
                            unsigned char reg, unsigned short *value)
 {
        unsigned short ret;
@@ -1802,7 +1941,7 @@ static int tsec_miiphy_read(char *devname, unsigned char addr,
  * Returns:
  *  0 on success
  */
-static int tsec_miiphy_write(char *devname, unsigned char addr,
+static int tsec_miiphy_write(const char *devname, unsigned char addr,
                             unsigned char reg, unsigned short value)
 {
        struct tsec_private *priv = privlist[0];