ppc4xx: Add support for GPCS, SGMII and M88E1112 PHY
authorVictor Gallardo <vgallardo@amcc.com>
Fri, 5 Sep 2008 06:49:36 +0000 (23:49 -0700)
committerStefan Roese <sr@denx.de>
Fri, 5 Sep 2008 10:21:16 +0000 (12:21 +0200)
This patch adds GPCS, SGMII and M88E1112 PHY support
for the AMCC PPC460GT/EX processors.

Signed-off-by: Victor Gallardo <vgallardo@amcc.com>
Signed-off-by: Stefan Roese <sr@denx.de>
cpu/ppc4xx/4xx_enet.c
cpu/ppc4xx/miiphy.c
include/ppc4xx_enet.h

index 8a3833513e2b21512814a8f872190da66840cb1c..071ac0a96771a0f5379edef3a90760765c12e90c 100644 (file)
 #define BI_PHYMODE_RMII  8
 #endif
 #endif
+#define BI_PHYMODE_SGMII 9
 
 #if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
     defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
 #define MAL_RX_CHAN_MUL        1
 #endif
 
+/*--------------------------------------------------------------------+
+ * Fixed PHY (PHY-less) support for Ethernet Ports.
+ *--------------------------------------------------------------------*/
+
+/*
+ * Some boards do not have a PHY for each ethernet port. These ports
+ * are known as Fixed PHY (or PHY-less) ports. For such ports, set
+ * the appropriate CONFIG_PHY_ADDR equal to CONFIG_FIXED_PHY and
+ * then define CFG_FIXED_PHY_PORTS to define what the speed and
+ * duplex should be for these ports in the board configuration
+ * file.
+ *
+ * For Example:
+ *     #define CONFIG_FIXED_PHY   0xFFFFFFFF
+ *
+ *     #define CONFIG_PHY_ADDR    CONFIG_FIXED_PHY
+ *     #define CONFIG_PHY1_ADDR   1
+ *     #define CONFIG_PHY2_ADDR   CONFIG_FIXED_PHY
+ *     #define CONFIG_PHY3_ADDR   3
+ *
+ *     #define CFG_FIXED_PHY_PORT(devnum,speed,duplex) \
+ *                     {devnum, speed, duplex},
+ *
+ *     #define CFG_FIXED_PHY_PORTS \
+ *                     CFG_FIXED_PHY_PORT(0,1000,FULL) \
+ *                     CFG_FIXED_PHY_PORT(2,100,HALF)
+ */
+
+#ifndef CONFIG_FIXED_PHY
+#define CONFIG_FIXED_PHY       0xFFFFFFFF /* Fixed PHY (PHY-less) */
+#endif
+
+#ifndef CFG_FIXED_PHY_PORTS
+#define CFG_FIXED_PHY_PORTS    /* default is an empty array */
+#endif
+
+struct fixed_phy_port {
+       unsigned int devnum;    /* ethernet port */
+       unsigned int speed;     /* specified speed 10,100 or 1000 */
+       unsigned int duplex;    /* specified duplex FULL or HALF */
+};
+
+static const struct fixed_phy_port fixed_phy_port[] = {
+       CFG_FIXED_PHY_PORTS     /* defined in board configuration file */
+};
+
 /*-----------------------------------------------------------------------------+
  * Global variables. TX and RX descriptors and buffers.
  *-----------------------------------------------------------------------------*/
@@ -611,8 +658,17 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
 
 #if defined(CONFIG_460EX)
        mode = 9;
+       mfsdr(SDR0_ETH_CFG, eth_cfg);
+       if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) &&
+           ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0))
+               mode = 11; /* config SGMII */
 #else
        mode = 10;
+       mfsdr(SDR0_ETH_CFG, eth_cfg);
+       if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) &&
+           ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0) &&
+           ((eth_cfg & SDR0_ETH_CFG_SGMII2_ENABLE) > 0))
+               mode = 12; /* config SGMII */
 #endif
 
        /* TODO:
@@ -635,6 +691,8 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
        /*
         * Right now only 2*RGMII is supported. Please extend when needed.
         * sr - 2008-02-19
+        * Add SGMII support.
+        * vg - 2008-07-28
         */
        switch (mode) {
        case 1:
@@ -761,6 +819,20 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
                bis->bi_phymode[2] = BI_PHYMODE_RGMII;
                bis->bi_phymode[3] = BI_PHYMODE_RGMII;
                break;
+       case 11:
+               /* 2 SGMII - 460EX */
+               bis->bi_phymode[0] = BI_PHYMODE_SGMII;
+               bis->bi_phymode[1] = BI_PHYMODE_SGMII;
+               bis->bi_phymode[2] = BI_PHYMODE_NONE;
+               bis->bi_phymode[3] = BI_PHYMODE_NONE;
+               break;
+       case 12:
+               /* 3 SGMII - 460GT */
+               bis->bi_phymode[0] = BI_PHYMODE_SGMII;
+               bis->bi_phymode[1] = BI_PHYMODE_SGMII;
+               bis->bi_phymode[2] = BI_PHYMODE_SGMII;
+               bis->bi_phymode[3] = BI_PHYMODE_NONE;
+               break;
        default:
                break;
        }
@@ -945,6 +1017,48 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
        out_be32((void *)EMAC_M1 + hw_p->hw_addr, mode_reg);
 #endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */
 
+#if defined(CONFIG_GPCS_PHY_ADDR) || defined(CONFIG_GPCS_PHY1_ADDR) || \
+    defined(CONFIG_GPCS_PHY2_ADDR) || defined(CONFIG_GPCS_PHY3_ADDR)
+       if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) {
+               /*
+                * In SGMII mode, GPCS access is needed for
+                * communication with the internal SGMII SerDes.
+                */
+               switch (devnum) {
+#if defined(CONFIG_GPCS_PHY_ADDR)
+               case 0:
+                       reg = CONFIG_GPCS_PHY_ADDR;
+                       break;
+#endif
+#if defined(CONFIG_GPCS_PHY1_ADDR)
+               case 1:
+                       reg = CONFIG_GPCS_PHY1_ADDR;
+                       break;
+#endif
+#if defined(CONFIG_GPCS_PHY2_ADDR)
+               case 2:
+                       reg = CONFIG_GPCS_PHY2_ADDR;
+                       break;
+#endif
+#if defined(CONFIG_GPCS_PHY3_ADDR)
+               case 3:
+                       reg = CONFIG_GPCS_PHY3_ADDR;
+                       break;
+#endif
+               }
+
+               mode_reg = in_be32((void *)EMAC_M1 + hw_p->hw_addr);
+               mode_reg |= EMAC_M1_MF_1000GPCS | EMAC_M1_IPPA_SET(reg);
+               out_be32((void *)EMAC_M1 + hw_p->hw_addr, mode_reg);
+
+               /* Configure GPCS interface to recommended setting for SGMII */
+               miiphy_reset(dev->name, reg);
+               miiphy_write(dev->name, reg, 0x04, 0x8120); /* AsymPause, FDX */
+               miiphy_write(dev->name, reg, 0x07, 0x2801); /* msg_pg, toggle */
+               miiphy_write(dev->name, reg, 0x00, 0x0140); /* 1Gbps, FDX     */
+       }
+#endif /* defined(CONFIG_GPCS_PHY_ADDR) */
+
        /* wait for PHY to complete auto negotiation */
        reg_short = 0;
 #ifndef CONFIG_CS8952_PHY
@@ -974,6 +1088,9 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
 
        bis->bi_phynum[devnum] = reg;
 
+       if (reg == CONFIG_FIXED_PHY)
+               goto get_speed;
+
 #if defined(CONFIG_PHY_RESET)
        /*
         * Reset the phy, only if its the first time through
@@ -986,6 +1103,27 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
                miiphy_write (dev->name, reg, 0x09, 0x0e00);
                miiphy_write (dev->name, reg, 0x04, 0x01e1);
 #endif
+#if defined(CONFIG_M88E1112_PHY)
+               if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) {
+                       /*
+                        * Marvell 88E1112 PHY needs to have the SGMII MAC
+                        * interace (page 2) properly configured to
+                        * communicate with the 460EX/GT GPCS interface.
+                        */
+
+                       /* Set access to Page 2 */
+                       miiphy_write(dev->name, reg, 0x16, 0x0002);
+
+                       miiphy_write(dev->name, reg, 0x00, 0x0040); /* 1Gbps */
+                       miiphy_read(dev->name, reg, 0x1a, &reg_short);
+                       reg_short |= 0x8000; /* bypass Auto-Negotiation */
+                       miiphy_write(dev->name, reg, 0x1a, reg_short);
+                       miiphy_reset(dev->name, reg); /* reset MAC interface */
+
+                       /* Reset access to Page 0 */
+                       miiphy_write(dev->name, reg, 0x16, 0x0000);
+               }
+#endif /* defined(CONFIG_M88E1112_PHY) */
                miiphy_reset (dev->name, reg);
 
 #if defined(CONFIG_440GX) || \
@@ -1080,8 +1218,25 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
        }
 #endif /* #ifndef CONFIG_CS8952_PHY */
 
-       speed = miiphy_speed (dev->name, reg);
-       duplex = miiphy_duplex (dev->name, reg);
+get_speed:
+       if (reg == CONFIG_FIXED_PHY) {
+               for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) {
+                       if (devnum == fixed_phy_port[i].devnum) {
+                               speed = fixed_phy_port[i].speed;
+                               duplex = fixed_phy_port[i].duplex;
+                               break;
+                       }
+               }
+
+               if (i == ARRAY_SIZE(fixed_phy_port)) {
+                       printf("ERROR: PHY (%s) not configured correctly!\n",
+                               dev->name);
+                       return -1;
+               }
+       } else {
+               speed = miiphy_speed(dev->name, reg);
+               duplex = miiphy_duplex(dev->name, reg);
+       }
 
        if (hw_p->print_speed) {
                hw_p->print_speed = 0;
index c8827201e9861e3261de443bb26d59cacdfa7bca..d303598118db2dada66e4997f7d2bbb8c229e035 100644 (file)
@@ -180,8 +180,10 @@ int phy_setup_aneg (char *devname, unsigned char addr)
  *
  * sr: Currently on 460EX only EMAC0 works with MDIO, so we always
  * return EMAC0 offset here
+ * vg: For 460EX/460GT if internal GPCS PHY address is specified
+ * return appropriate EMAC offset
  */
-unsigned int miiphy_getemac_offset (void)
+unsigned int miiphy_getemac_offset(u8 addr)
 {
 #if (defined(CONFIG_440) && \
     !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \
@@ -233,6 +235,39 @@ unsigned int miiphy_getemac_offset (void)
                return 0x100;
 #endif
 
+#if defined(CONFIG_460EX) || defined(CONFIG_460GT)
+       u32 mode_reg;
+       u32 eoffset = 0;
+
+       switch (addr) {
+#if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR)
+       case CONFIG_GPCS_PHY1_ADDR:
+               mode_reg = in_be32((void *)EMAC_M1 + 0x100);
+               if (addr == EMAC_M1_IPPA_GET(mode_reg))
+                       eoffset = 0x100;
+               break;
+#endif
+#if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR)
+       case CONFIG_GPCS_PHY2_ADDR:
+               mode_reg = in_be32((void *)EMAC_M1 + 0x300);
+               if (addr == EMAC_M1_IPPA_GET(mode_reg))
+                       eoffset = 0x300;
+               break;
+#endif
+#if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR)
+       case CONFIG_GPCS_PHY3_ADDR:
+               mode_reg = in_be32((void *)EMAC_M1 + 0x400);
+               if (addr == EMAC_M1_IPPA_GET(mode_reg))
+                       eoffset = 0x400;
+               break;
+#endif
+       default:
+               eoffset = 0;
+               break;
+       }
+       return eoffset;
+#endif
+
        return 0;
 #endif
 }
@@ -262,7 +297,7 @@ static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value)
        u32 emac_reg;
        u32 sta_reg;
 
-       emac_reg = miiphy_getemac_offset();
+       emac_reg = miiphy_getemac_offset(addr);
 
        /* wait for completion */
        if (emac_miiphy_wait(emac_reg) != 0)
@@ -311,7 +346,7 @@ int emac4xx_miiphy_read (char *devname, unsigned char addr, unsigned char reg,
        unsigned long sta_reg;
        unsigned long emac_reg;
 
-       emac_reg = miiphy_getemac_offset ();
+       emac_reg = miiphy_getemac_offset(addr);
 
        if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0)
                return -1;
index b74c6fcafde7255fac2f8cfa9d1169bd0637283a..00669a717aaddf742eb1d970fac38f227d9e8b28 100644 (file)
@@ -376,6 +376,7 @@ typedef struct emac_4xx_hw_st {
 #define EMAC_M1_APP            (0x08000000)
 #define EMAC_M1_RSVD           (0x06000000)
 #define EMAC_M1_IST            (0x01000000)
+#define EMAC_M1_MF_1000GPCS    (0x00C00000)
 #define EMAC_M1_MF_1000MBPS    (0x00800000)    /* 0's for 10MBPS */
 #define EMAC_M1_MF_100MBPS     (0x00400000)
 #define EMAC_M1_RFS_MASK       (0x00380000)
@@ -394,6 +395,8 @@ typedef struct emac_4xx_hw_st {
 #define EMAC_M1_MWSW           (0x00007000)
 #define EMAC_M1_JUMBO_ENABLE   (0x00000800)
 #define EMAC_M1_IPPA           (0x000007c0)
+#define EMAC_M1_IPPA_SET(id)   (((id) & 0x1f) << 6)
+#define EMAC_M1_IPPA_GET(id)   (((id) >> 6) & 0x1f)
 #define EMAC_M1_OBCI_GT100     (0x00000020)
 #define EMAC_M1_OBCI_100       (0x00000018)
 #define EMAC_M1_OBCI_83                (0x00000010)