[U-Boot] [PATCH 1/1] ppc4xx: Add support for GPCS, SGMII and M88E1112 PHY

Victor Gallardo vgallardo at amcc.com
Sat Aug 30 13:08:10 CEST 2008


This patch adds GPCS, SGMII and M88E1112 PHY support
for the AMCC PPC460GT/EX processors.

Signed-off-by: Victor Gallardo <vgallardo at amcc.com>
---
 cpu/ppc4xx/4xx_enet.c |  122 ++++++++++++++++++++++++++++++++++++++++++++++++-
 cpu/ppc4xx/miiphy.c   |   41 +++++++++++++++-
 include/ppc4xx_enet.h |    3 +
 3 files changed, 161 insertions(+), 5 deletions(-)

diff --git a/cpu/ppc4xx/4xx_enet.c b/cpu/ppc4xx/4xx_enet.c
index 8a38335..f6cbf2b 100644
--- a/cpu/ppc4xx/4xx_enet.c
+++ b/cpu/ppc4xx/4xx_enet.c
@@ -198,6 +198,7 @@
 #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) || \
@@ -216,6 +217,20 @@
 #define MAL_RX_CHAN_MUL	1
 #endif
 
+/*
+ * Some boards do not have a PHY for each ethernet port.
+ * For example on Arches board (2 CPU system) eth0 does not have
+ * a PHY, both CPU's are wired directly together (AC coupled)
+ * using SGMII0. In these cases set the appropriate
+ * CONFIG_PHY_ADDR equal to CONFIG_PHY_LESS to detect that
+ * the specified ethernet port does not have a PHY.
+ */
+#if !defined(CONFIG_PHY_LESS)
+#define CONFIG_PHY_LESS        0xFFFFFFFF /* PHY-less mode */
+#define CONFIG_PHY_LESS_SPEED  1000
+#define CONFIG_PHY_LESS_DUPLEX FULL
+#endif
+
 /*-----------------------------------------------------------------------------+
  * Global variables. TX and RX descriptors and buffers.
  *-----------------------------------------------------------------------------*/
@@ -611,8 +626,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 +659,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 +787,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 +985,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 +1056,9 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
 
 	bis->bi_phynum[devnum] = reg;
 
+	if (reg == CONFIG_PHY_LESS)
+		goto get_speed;
+
 #if defined(CONFIG_PHY_RESET)
 	/*
 	 * Reset the phy, only if its the first time through
@@ -986,6 +1071,33 @@ 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, 0x10, &reg_short);
+			reg_short &= ~0x0C00; /* Preferred Media MASK */
+			reg_short |=  0x0800; /* Preferred Media Copper */
+			reg_short &= ~0x0380; /* Mode Select MASK */
+			reg_short |=  0x0280; /* Mode Select Copper only */
+			miiphy_write(dev->name, reg, 0x10, reg_short);
+			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 +1192,14 @@ 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_PHY_LESS) {
+		speed = CONFIG_PHY_LESS_SPEED;
+		duplex = CONFIG_PHY_LESS_DUPLEX;
+	} else {
+		speed = miiphy_speed(dev->name, reg);
+		duplex = miiphy_duplex(dev->name, reg);
+	}
 
 	if (hw_p->print_speed) {
 		hw_p->print_speed = 0;
diff --git a/cpu/ppc4xx/miiphy.c b/cpu/ppc4xx/miiphy.c
index c882720..d303598 100644
--- a/cpu/ppc4xx/miiphy.c
+++ b/cpu/ppc4xx/miiphy.c
@@ -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;
diff --git a/include/ppc4xx_enet.h b/include/ppc4xx_enet.h
index b74c6fc..00669a7 100644
--- a/include/ppc4xx_enet.h
+++ b/include/ppc4xx_enet.h
@@ -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)
-- 
1.5.5



More information about the U-Boot mailing list