Marvell SGMII patch: MHSLPONLGCY-151 Add SGMII support on GMAC1 (u-boot)

Marvell gave us an update to uboot for thunderbolt:
SDK_2.7.25_thunderbolt_v1.

Adding all their uboot changes would be unnecessary and high risk,
low reward. For this reason, we are only applying the minimal set
of changes to get SGMII working.

This patch was taken from SDK_2.7.25_thunderbolt_v1 as part of
that minimal set, along with go/fibercl/82349

Marvell's patch notes:

From: David Ferruz Garcia
Sent: Wednesday, November 23, 2016 8:07 PM
To: 'Jian Ho'
Subject: RE: SPI flash image for Thunderbolt 88LX5153

This patch enables the support of both Ethernet MACs
(SGMII and Internal PHY) in the 88F6601.

-        mv_phy.c: Here is the LP SERDES configuration for SGMII.
We have defined a function called mvBoardGMACModeSet which configures
the LP SERDES and the GMAC connected to the SERDES. These changes are
applied on DB-88F6601 which is our development board, but you shall call
the function in the specific part for your board.

    ****Aaron: This is already called by our board****

-        The rest of the files are macros definitions.

-        In addition, in your board definition
(in file board/mv_feroceon/mv_kw2/kw2_family/boardEnv/mvBoardEnvSpec.c )
you shall configure the ethSataComplexOpt parameter to
ESC_OPT_GEPHY_MAC0|ESC_OPT_SGMII

   ****Aaron: This is done in go/fibercl/82572****

-        Review your mvNetConfig u-boot environment variable.
You should have something like this:

Marvell>> printenv mvNetConfig
mvNetConfig=mv_net_config=0 mem=128M

Change-Id: Ided37f098b675b11db985ddd05cb34cdaf35fc79
diff --git a/board/mv_feroceon/mv_kw2/kw2_family/boardEnv/mvBoardEnvLib.c b/board/mv_feroceon/mv_kw2/kw2_family/boardEnv/mvBoardEnvLib.c
index 6aab4bd..38b5617 100644
--- a/board/mv_feroceon/mv_kw2/kw2_family/boardEnv/mvBoardEnvLib.c
+++ b/board/mv_feroceon/mv_kw2/kw2_family/boardEnv/mvBoardEnvLib.c
@@ -2310,6 +2310,10 @@
 			mvOsOutput("       LP SERDES connected to external PHY.\n");
 		else
 			mvOsOutput("       LP SERDES connected to SFP.\n");
+		/* SERDES speed */
+		mvOsOutput("       LP SERDES configured at %sGbps\n",
+		    (ethConfig & ESC_OPT_SGMII_2_5)?"2.5":"1");
+
 		return;
 	}
 	else if (ethConfig & (ESC_OPT_GEPHY_MAC0 | ESC_OPT_GEPHY_MAC1 | ESC_OPT_GEPHY_SW_P0 | ESC_OPT_GEPHY_SW_P5 )) {
diff --git a/board/mv_feroceon/mv_kw2/kw2_family/ctrlEnv/mvCtrlEnvRegs.h b/board/mv_feroceon/mv_kw2/kw2_family/ctrlEnv/mvCtrlEnvRegs.h
index c36c68e..7a96e01 100644
--- a/board/mv_feroceon/mv_kw2/kw2_family/ctrlEnv/mvCtrlEnvRegs.h
+++ b/board/mv_feroceon/mv_kw2/kw2_family/ctrlEnv/mvCtrlEnvRegs.h
@@ -518,6 +518,27 @@
 #define THERMAL_TEMPERATURE_OFFSET 10
 #define THERMAL_TEMPERATURE_MASK (0x1FF << THERMAL_TEMPERATURE_OFFSET)
 
+// LP SERDES PHY AND NETWORKING REGISTERS
+#define ETHERNET_COMPLEX_CTRL_REG_0		(0x18810)
+#define SOFTWARE_RESET_CTRL_REG			(0x18220)
+#define GE_MAC_CTRL_REG(port)				(0x72C00 + (port * 0x4000))
+#define ETH_UNIT_CTRL_REG(port)				(0x720B0 + (port * 0x4000))
+#define PORT_MAC_CTRL_REG2(port)			(0x72C08 + (port * 0x4000))
+#define PORT_AUTO_NEG_CTRL_REG(port)		(0x72C0C + (port * 0x4000))
+#define SYNC_PATTERN_REG					(0x72E90)
+#define SERDES_CONFIG_REG(port)				(0x724A0 + (port * 0x4000))
+#define SERDES_STATUS_REG(port)				(0x724A4 + (port * 0x4000))
+#define ONEMS_CLK_DIVIDER_CTRL_REG(port)	(0x724F4 + (port * 0x4000))
+#define POWER_PLL_CTRL_REG				(0x72E04)
+#define KVCO_CALIBRATION_CTRL_REG			(0x72E08)
+#define IMPEDANCE_CALIBRATION_CTRL_REG	(0x72E0C)
+#define GENERATION_1_SETTING_0_REG		(0x72E34)
+#define GENERATION_1_SETTING_1_REG		(0x72E38)
+#define DIGITAL_LOOPBACK_ENABLE_REG		(0x72E8C)
+#define PHY_ISOLATION_MODE_CTRL_REG		(0x72E98)
+#define PORT_BUCKET_REFILL_REG(port)			(0x73E10 + (port * 0x4000))
+#define QUEUE_BUCKET_REFILL_REG(port, txq)	(0x73E20 + (port * 0x4000) + (txq * 4))
+
 #endif /* MV_ASMLANGUAGE */
 
 #ifdef __cplusplus
diff --git a/board/mv_feroceon/mv_kw2/mv_phy.c b/board/mv_feroceon/mv_kw2/mv_phy.c
index d36d68b..d5f8ce9 100755
--- a/board/mv_feroceon/mv_kw2/mv_phy.c
+++ b/board/mv_feroceon/mv_kw2/mv_phy.c
@@ -296,5 +296,279 @@
 			if (eeeEnable == MV_TRUE)
 				mvNetaGmacLpiSet(port, 1);
 		}
+                #define A_MC_DEBUG
+#ifdef A_MC_DEBUG
+    printf("MTL: Configuring GMAC Mode. ethComplex: 0x%x\n", ethComplex);
+#endif
+    /* Disable SMI polling on the port using SGMII if it is also using GEPHY on MAC0*/
+		if (((ethComplex & ESC_OPT_SGMII) || (ethComplex & ESC_OPT_SGMII_2_5)) && (ethComplex & ESC_OPT_GEPHY_MAC0))
+		{
+		  MV_U32 port = (ethComplex & ESC_OPT_GEPHY_MAC0) ? 1 : 0;
+		  MV_GMAC_MODE mode = (ethComplex & ESC_OPT_SGMII) ? PHY_SGMII_1G: PHY_SGMII_2_5G;
+
+#ifdef A_MC_DEBUG
+	    printf("MTL: Configuring GMAC Mode. mode:%d port:%d\n", mode, port);
+#endif
+      mvBoardGMACModeSet(mode, port, MV_FALSE);
+
+                }
+
 	}
 }
+
+/***********************************************************
+ * GMAC mode configuration			   *
+ ***********************************************************/
+void mvBoardGMACModeSet(MV_GMAC_MODE mode, int port, MV_BOOL polarityInv)
+{
+  MV_U32 reg;
+  MV_U32 i;
+
+  if (mode == PHY_1000BASE_X_1G)
+  {
+    /* for 1000base-X link */
+    printf("MTL: Init 1000base-X@1G on MAC %d..\n", port);
+
+    // disable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg &= ~(1 << 0));
+
+    // disable SMI polling
+    reg = MV_REG_READ(ETH_UNIT_CTRL_REG(port));
+    MV_REG_WRITE(ETH_UNIT_CTRL_REG(port), reg &= ~(1 << 1));
+
+    // enable SGMII AutoNeg clock
+    reg = MV_REG_READ(ONEMS_CLK_DIVIDER_CTRL_REG(port));
+    MV_REG_WRITE(ONEMS_CLK_DIVIDER_CTRL_REG(port), reg |= (1 << 31));
+
+    // PCS enable
+    reg = MV_REG_READ(PORT_MAC_CTRL_REG2(port));
+    MV_REG_WRITE(PORT_MAC_CTRL_REG2(port), reg |= (1 << 3));
+
+    // AN reg: set InBandAnEn, clear InBandAnByPassEn
+    MV_REG_WRITE(PORT_AUTO_NEG_CTRL_REG(port), 0x9044);
+
+    if (polarityInv == MV_TRUE)
+    {
+      // Invert TXD_INV
+      reg = MV_REG_READ(SYNC_PATTERN_REG);
+      MV_REG_WRITE(SYNC_PATTERN_REG, reg |= (1 << 10));
+    }
+
+    // Enable 1000base-X AN
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg |= (1 << 1));
+
+    // Recalibrate SERDES
+    reg = MV_REG_READ(KVCO_CALIBRATION_CTRL_REG);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg |= (1 << 15));
+    mvOsDelay(2);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg &= (1 << 15));
+
+    // enable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg |= (1 << 0));
+
+  }
+  else if (mode == PHY_1000BASE_X_2_5G)
+  {
+    /* for 1000base-X link */
+    printf("MTL: Init 1000base-X@2,5G on MAC %d..\n", port);
+
+    // disable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg &= ~(1 << 0));
+
+    // PCS enable
+    reg = MV_REG_READ(PORT_MAC_CTRL_REG2(port));
+    MV_REG_WRITE(PORT_MAC_CTRL_REG2(port), reg |= (1 << 3));
+
+    // enable SGMII AutoNeg clock
+    reg = MV_REG_READ(ONEMS_CLK_DIVIDER_CTRL_REG(port));
+    MV_REG_WRITE(ONEMS_CLK_DIVIDER_CTRL_REG(port), reg |= (1 << 31));
+
+    // Reset LP serdes
+    reg = MV_REG_READ(SOFTWARE_RESET_CTRL_REG);
+    MV_REG_WRITE(SOFTWARE_RESET_CTRL_REG, reg |= (1 << 24));
+    MV_REG_WRITE(SOFTWARE_RESET_CTRL_REG, reg &= ~(1 << 24));
+
+    // Clear SERDES config data
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0);
+
+    // Reserved bits
+    reg = MV_REG_READ(ETHERNET_COMPLEX_CTRL_REG_0);
+    MV_REG_WRITE(ETHERNET_COMPLEX_CTRL_REG_0, reg &= ~(1 << 3));
+
+    reg = MV_REG_READ(0x18804);
+    MV_REG_WRITE(0x18804, reg |= (1 << 25));
+
+    // Config Power and PLL
+    MV_REG_WRITE(POWER_PLL_CTRL_REG, 0xF880);
+
+    // SERDES config data
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCC0);
+
+    // Generation 1 register 0
+    MV_REG_WRITE(GENERATION_1_SETTING_0_REG, 0x8F9);
+    // Generation 1 register 1
+    MV_REG_WRITE(GENERATION_1_SETTING_1_REG, 0x9055);
+
+    // digital loopback register
+    MV_REG_WRITE(DIGITAL_LOOPBACK_ENABLE_REG, 0x430);
+
+    // PHY isolation mode
+    MV_REG_WRITE(PHY_ISOLATION_MODE_CTRL_REG, 0x0566);
+    // PHY isolation mode
+    MV_REG_WRITE(PHY_ISOLATION_MODE_CTRL_REG, 0x0166);
+
+    // digital loopback register
+    MV_REG_WRITE(DIGITAL_LOOPBACK_ENABLE_REG, 0x0072);
+
+    //Polling on PLL ready - register 0xF10724A4 bit 2 should be 1
+    MV_REG_READ(SERDES_STATUS_REG(port));
+    // sleep 1
+    mvOsDelay(1);
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCD0);
+
+    //Polling on RX Done - register 0xF10724A4 bit 0 should be 1
+    MV_REG_READ(SERDES_STATUS_REG(port));
+    // sleep 1
+    mvOsDelay(1);
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCC0);
+
+    // Recalibrate SERDES
+    reg = MV_REG_READ(KVCO_CALIBRATION_CTRL_REG);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg |= (1 << 15));
+    mvOsDelay(2);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg &= (1 << 15));
+
+    // Enable port, set port type, Set frame size limit to 767** Check this!
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), 0x8BFF);
+
+    if (polarityInv == MV_TRUE)
+    {
+      // Invert tx polarity
+      reg = MV_REG_READ(SYNC_PATTERN_REG);
+      MV_REG_WRITE(SYNC_PATTERN_REG, reg |= (1 << 10));
+    }
+
+    // Set Impedance calibration values
+    MV_REG_WRITE(IMPEDANCE_CALIBRATION_CTRL_REG, 0x9044);
+
+    // Set Eth limit to 3G
+    MV_REG_WRITE(PORT_BUCKET_REFILL_REG(port), 0x100B9B);
+    for (i = 0; i < 8; i++)
+    {
+      MV_REG_WRITE(QUEUE_BUCKET_REFILL_REG(port, i), 0x100B9B);
+    }
+  }
+  else if (mode == PHY_SGMII_1G)
+  {
+    printf("MTL: Init SGMII@1G on MAC %d..\n", port);
+
+    //Set 1G and 100M modes adn full duplex
+    MV_REG_WRITE(PORT_AUTO_NEG_CTRL_REG(port), 0x9062);
+
+    //disable port, set packet size
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), 0x8B9C);
+
+    // Recalibrate SERDES
+    reg = MV_REG_READ(KVCO_CALIBRATION_CTRL_REG);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg |= (1 << 15));
+    mvOsDelay(2);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg &= (1 << 15));
+
+    //enable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg |= 1);
+
+    if (polarityInv == MV_TRUE)
+    {
+      // Invert tx polarity
+      reg = MV_REG_READ(SYNC_PATTERN_REG);
+      MV_REG_WRITE(SYNC_PATTERN_REG, reg |= (1 << 10));
+    }
+
+  }
+  else if (mode == PHY_SGMII_2_5G)
+  {
+    printf("MTL: Init SGMII@2,5G on MAC %d..\n", port);
+    // disable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg &= ~(0x1));
+    // working with PCS
+    reg = MV_REG_READ(PORT_MAC_CTRL_REG2(port));
+    MV_REG_WRITE(PORT_MAC_CTRL_REG2(port), reg |= (1 << 3));
+    //Enable 1ms clock generation
+    reg = MV_REG_READ(ONEMS_CLK_DIVIDER_CTRL_REG(port));
+    MV_REG_WRITE(ONEMS_CLK_DIVIDER_CTRL_REG(port), reg |= (1 << 31));
+    //LP Serdes reset
+    reg = MV_REG_READ(SOFTWARE_RESET_CTRL_REG);
+    MV_REG_WRITE(SOFTWARE_RESET_CTRL_REG, reg |= (1 << 24));
+    MV_REG_WRITE(SOFTWARE_RESET_CTRL_REG, reg &= ~(1 << 24));
+    //Serdes config data
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0);
+    // Reserved bits
+    reg = MV_REG_READ(ETHERNET_COMPLEX_CTRL_REG_0);
+    MV_REG_WRITE(ETHERNET_COMPLEX_CTRL_REG_0, reg &= ~(1 << 3));
+    // ?? Unknown register
+    reg = MV_REG_READ(0x18804);
+    MV_REG_WRITE(0x18804, reg |= (1 << 31));
+    // PHY mode and power up
+    MV_REG_WRITE(POWER_PLL_CTRL_REG, 0xF880);
+    // SERDES configuration
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCC0);
+    // SERDES configuration
+    MV_REG_WRITE(GENERATION_1_SETTING_0_REG, 0x8F9);
+    // SERDES configuration
+    MV_REG_WRITE(GENERATION_1_SETTING_1_REG, 0x9055);
+    // Digital loopback enable
+    MV_REG_WRITE(DIGITAL_LOOPBACK_ENABLE_REG, 0x430);
+    // PHY isolation mode
+    MV_REG_WRITE(PHY_ISOLATION_MODE_CTRL_REG, 0x0566);
+    // PHY RX initialization
+    MV_REG_WRITE(PHY_ISOLATION_MODE_CTRL_REG, 0x0166);
+    // Digital loopback enable
+    MV_REG_WRITE(DIGITAL_LOOPBACK_ENABLE_REG, 0x0072);
+
+    // Polling on PLL ready - register 0xF10724A4 bit 2 should be 1
+    // SERDES status
+    MV_REG_READ(SERDES_STATUS_REG(port));
+    mvOsDelay(1);
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCD0);
+
+    // Polling on RX Done - register 0xF10724A4 bit 0 should be 1
+    MV_REG_READ(SERDES_STATUS_REG(port));
+    mvOsDelay(1);
+    MV_REG_WRITE(SERDES_CONFIG_REG(port), 0xCC0);
+
+    // Disable autoneg. set speed and full duplex
+    MV_REG_WRITE(PORT_AUTO_NEG_CTRL_REG(port), 0x9062);
+    // Disable port, set packet size
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), 0x8B9C);
+
+    // Recalibrate SERDES
+    reg = MV_REG_READ(KVCO_CALIBRATION_CTRL_REG);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg |= (1 << 15));
+    mvOsDelay(2);
+    MV_REG_WRITE(KVCO_CALIBRATION_CTRL_REG, reg &= (1 << 15));
+
+    // Enable port
+    reg = MV_REG_READ(GE_MAC_CTRL_REG(port));
+    MV_REG_WRITE(GE_MAC_CTRL_REG(port), reg |= (1));
+
+    if (polarityInv == MV_TRUE)
+    {
+      // Invert Tx polarity
+      reg = MV_REG_READ(SYNC_PATTERN_REG);
+      MV_REG_WRITE(SYNC_PATTERN_REG, reg |= (1 << 10));
+    }
+
+    // Set Eth limit to 3G
+    MV_REG_WRITE(PORT_BUCKET_REFILL_REG(port), 0x100B9B);
+    for (i = 0; i < 8; i++)
+    {
+      MV_REG_WRITE(QUEUE_BUCKET_REFILL_REG(port, i), 0x100B9B);
+    }
+  }
+}
diff --git a/board/mv_feroceon/mv_kw2/mv_phy.h b/board/mv_feroceon/mv_kw2/mv_phy.h
index 81b6a2d..ad862a5 100644
--- a/board/mv_feroceon/mv_kw2/mv_phy.h
+++ b/board/mv_feroceon/mv_kw2/mv_phy.h
@@ -65,7 +65,16 @@
 #include "mvOs.h"
 #include "eth-phy/mvEthPhy.h"
 
+typedef enum
+{
+  PHY_1000BASE_X_1G = 0,
+  PHY_1000BASE_X_2_5G, // 1
+  PHY_SGMII_1G,  // 2
+  PHY_SGMII_2_5G, // 3
+} MV_GMAC_MODE;
+
 void mvBoardEgigaPhyInit(MV_VOID); 
+void mvBoardGMACModeSet(MV_GMAC_MODE mode, int port, MV_BOOL polarityInv);
 
 // void mvEthSwitchRegWrite(MV_U32 ethPortNum, MV_U32 phyAddr,
 //                                  MV_U32 regOffs, MV_U16 data);