tsec: Add support for using the BCM5482 PHY in fiber mode
authorPeter Tyser <ptyser@xes-inc.com>
Mon, 9 Nov 2009 19:09:47 +0000 (13:09 -0600)
committerBen Warren <biggerbadderben@gmail.com>
Mon, 1 Feb 2010 06:34:34 +0000 (22:34 -0800)
The BCM5482 PHY supports both copper and fiber as an ethernet medium.
By enabling its copper/fiber mode auto-detection feature it can
dynamically determine if it should be configured for copper or fiber.

Signed-off-by: Peter Tyser <ptyser@xes-inc.com>
Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
drivers/net/tsec.c
include/tsec.h

index 6a444186880160b68a0926cca4013d286e5040a1..7600e40de906d20d5885744878296d265f1333fc 100644 (file)
@@ -530,8 +530,105 @@ 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);
+       } 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
  */
@@ -1091,15 +1188,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 */
index 29e4c56d6cb7b19ad2ae9b07ce60cca9d49dc628..b2e37d4323beb14a3bbd4e9f5e6ee1b721bee817 100644 (file)
 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK   0x0700
 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT  8
 
+#define MIIM_BCM54XX_SHD       0x1c    /* 0x1c shadow registers */
+#define MIIM_BCM54XX_SHD_WRITE 0x8000
+#define MIIM_BCM54XX_SHD_VAL(x)        ((x & 0x1f) << 10)
+#define MIIM_BCM54XX_SHD_DATA(x)       ((x & 0x3ff) << 0)
+#define MIIM_BCM54XX_SHD_WR_ENCODE(val, data)  \
+       (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
+        MIIM_BCM54XX_SHD_DATA(data))
+
+#define MIIM_BCM54XX_EXP_DATA  0x15    /* Expansion register data */
+#define MIIM_BCM54XX_EXP_SEL   0x17    /* Expansion register select */
+#define MIIM_BCM54XX_EXP_SEL_SSD       0x0e00  /* Secondary SerDes select */
+#define MIIM_BCM54XX_EXP_SEL_ER        0x0f00  /* Expansion register select */
+
 /* Cicada Auxiliary Control/Status Register */
 #define MIIM_CIS8201_AUX_CONSTAT       0x1c
 #define MIIM_CIS8201_AUXCONSTAT_INIT   0x0004