drivers: net: apply serdes configuration for ENETC Ethernet interfaces
authorAlex Marginean <alexandru.marginean@nxp.com>
Wed, 3 Jul 2019 09:11:42 +0000 (12:11 +0300)
committerJoe Hershberger <joe.hershberger@ni.com>
Thu, 25 Jul 2019 18:13:30 +0000 (13:13 -0500)
Ethernet interfaces using serial protocols go through the serdes block
integrated in the SoC.  This is accessed over dedicated internal MDIOs
which are part of the Ethernet PCI functions.  Set up serdes at _start,
along with other protocol specific port/MAC configuration.
MDIO code is shared with enetc_mdio, read/write functions are exported
from fsl_enetc_mdio for this reason.

Signed-off-by: Alex Marginean <alexm.osslist@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
drivers/net/fsl_enetc.c
drivers/net/fsl_enetc.h
drivers/net/fsl_enetc_mdio.c

index f14b484b26e5c47d625514928ba5fb0aa4499d5c..da533a1e5dbbf0269282cfeaa4d68c11d2088df8 100644 (file)
@@ -39,6 +39,152 @@ static int enetc_bind(struct udevice *dev)
        return 0;
 }
 
+/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */
+static int enetc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+       struct enetc_mdio_priv priv;
+
+       priv.regs_base = bus->priv;
+       return enetc_mdio_read_priv(&priv, addr, devad, reg);
+}
+
+static int enetc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+                           u16 val)
+{
+       struct enetc_mdio_priv priv;
+
+       priv.regs_base = bus->priv;
+       return enetc_mdio_write_priv(&priv, addr, devad, reg, val);
+}
+
+/* only interfaces that can pin out through serdes have internal MDIO */
+static bool enetc_has_imdio(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
+
+       return !!(priv->imdio.priv);
+}
+
+/* set up serdes for SGMII */
+static int enetc_init_sgmii(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
+
+       if (!enetc_has_imdio(dev))
+               return 0;
+
+       /* Set to SGMII mode, use AN */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+                        ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN);
+
+       /* Dev ability - SGMII */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+                        ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII);
+
+       /* Adjust link timer for SGMII */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+                        ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+                        ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
+
+       /* restart PCS AN */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+                        ENETC_PCS_CR,
+                        ENETC_PCS_CR_RESET_AN | ENETC_PCS_CR_DEF_VAL);
+
+       return 0;
+}
+
+/* set up MAC for RGMII */
+static int enetc_init_rgmii(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
+       u32 if_mode;
+
+       /* enable RGMII AN */
+       if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
+       if_mode |= ENETC_PM_IF_MODE_AN_ENA;
+       enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+
+       return 0;
+}
+
+/* set up MAC and serdes for SXGMII */
+static int enetc_init_sxgmii(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
+       u32 if_mode;
+
+       /* set ifmode to (US)XGMII */
+       if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
+       if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
+       enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+
+       if (!enetc_has_imdio(dev))
+               return 0;
+
+       /* Dev ability - SXGMII */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+                        ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII);
+
+       /* Restart PCS AN */
+       enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+                        ENETC_PCS_CR,
+                        ENETC_PCS_CR_LANE_RESET | ENETC_PCS_CR_RESET_AN);
+
+       return 0;
+}
+
+/* Apply protocol specific configuration to MAC, serdes as needed */
+static void enetc_start_pcs(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
+       const char *if_str;
+
+       priv->if_type = PHY_INTERFACE_MODE_NONE;
+
+       /* check internal mdio capability, not all ports need it */
+       if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) {
+               /*
+                * set up internal MDIO, this is part of ETH PCI function and is
+                * used to access serdes / internal SoC PHYs.
+                * We don't currently register it as a MDIO bus as it goes away
+                * when the interface is removed, so it can't practically be
+                * used in the console.
+                */
+               priv->imdio.read = enetc_mdio_read;
+               priv->imdio.write = enetc_mdio_write;
+               priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
+               strncpy(priv->imdio.name, dev->name, MDIO_NAME_LEN);
+       }
+
+       if (!ofnode_valid(dev->node)) {
+               enetc_dbg(dev, "no enetc ofnode found, skipping PCS set-up\n");
+               return;
+       }
+
+       if_str = ofnode_read_string(dev->node, "phy-mode");
+       if (if_str)
+               priv->if_type = phy_get_interface_by_name(if_str);
+       else
+               enetc_dbg(dev,
+                         "phy-mode property not found, defaulting to SGMII\n");
+       if (priv->if_type < 0)
+               priv->if_type = PHY_INTERFACE_MODE_NONE;
+
+       switch (priv->if_type) {
+       case PHY_INTERFACE_MODE_SGMII:
+               enetc_init_sgmii(dev);
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+               enetc_init_rgmii(dev);
+               break;
+       case PHY_INTERFACE_MODE_XGMII:
+               enetc_init_sxgmii(dev);
+               break;
+       };
+}
+
 /* Configure the actual/external ethernet PHY, if one is found */
 static void enetc_start_phy(struct udevice *dev)
 {
@@ -303,7 +449,7 @@ static int enetc_start(struct udevice *dev)
        enetc_setup_tx_bdr(dev);
        enetc_setup_rx_bdr(dev);
 
-       priv->if_type = PHY_INTERFACE_MODE_NONE;
+       enetc_start_pcs(dev);
        enetc_start_phy(dev);
 
        return 0;
index fbb9dfa15e4a80892eec076fc410408b58fdf07d..7ac7c1fefeadc8cab43a8d0d1327274d56aa4ac1 100644 (file)
@@ -61,6 +61,8 @@ enum enetc_bdr_type {TX, RX};
 #define ENETC_PSIPMMR                  0x0018
 #define ENETC_PSIPMAR0                 0x0100
 #define ENETC_PSIPMAR1                 0x0104
+#define ENETC_PCAPR0                   0x0900
+#define  ENETC_PCAPRO_MDIO             BIT(11)
 #define ENETC_PSICFGR(n)               (0x0940 + (n) * 0x10)
 #define  ENETC_PSICFGR_SET_TXBDR(val)  ((val) & 0xff)
 #define  ENETC_PSICFGR_SET_RXBDR(val)  (((val) & 0xff) << 16)
@@ -70,6 +72,11 @@ enum enetc_bdr_type {TX, RX};
 #define  ENETC_PM_CC_RX_TX_EN          0x8813
 #define ENETC_PM_MAXFRM                        0x8014
 #define  ENETC_RX_MAXFRM_SIZE          PKTSIZE_ALIGN
+#define ENETC_PM_IMDIO_BASE            0x8030
+#define ENETC_PM_IF_MODE               0x8300
+#define  ENETC_PM_IF_MODE_RG           BIT(2)
+#define  ENETC_PM_IF_MODE_AN_ENA       BIT(15)
+#define  ENETC_PM_IF_IFMODE_MASK       GENMASK(1, 0)
 
 /* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */
 #define ENETC_BD_CNT           CONFIG_SYS_RX_ETH_BUFFER
@@ -146,6 +153,7 @@ struct enetc_priv {
        struct bd_ring rx_bdr;
 
        int if_type;
+       struct mii_dev imdio;
 };
 
 /* register accessors */
@@ -168,6 +176,27 @@ struct enetc_priv {
 #define enetc_bdr_write(priv, t, n, off, val) \
                        enetc_write(priv, ENETC_BDR(t, n, off), val)
 
+/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
+#define ENETC_PCS_PHY_ADDR     0
+
+/* PCS registers */
+#define ENETC_PCS_CR                   0x00
+#define  ENETC_PCS_CR_RESET_AN         0x1200
+#define  ENETC_PCS_CR_DEF_VAL          0x0140
+#define  ENETC_PCS_CR_LANE_RESET       0x8000
+#define ENETC_PCS_DEV_ABILITY          0x04
+#define  ENETC_PCS_DEV_ABILITY_SGMII   0x4001
+#define  ENETC_PCS_DEV_ABILITY_SXGMII  0x5001
+#define ENETC_PCS_LINK_TIMER1          0x12
+#define  ENETC_PCS_LINK_TIMER1_VAL     0x06a0
+#define ENETC_PCS_LINK_TIMER2          0x13
+#define  ENETC_PCS_LINK_TIMER2_VAL     0x0003
+#define ENETC_PCS_IF_MODE              0x14
+#define  ENETC_PCS_IF_MODE_SGMII_AN    0x0003
+
+/* PCS replicator block for USXGMII */
+#define ENETC_PCS_DEVAD_REPL           0x1f
+
 /* ENETC external MDIO registers */
 #define ENETC_MDIO_BASE                0x1c00
 #define ENETC_MDIO_CFG         0x00
@@ -186,4 +215,13 @@ struct enetc_mdio_priv {
        void *regs_base;
 };
 
+/*
+ * these functions are implemented by ENETC_MDIO and are re-used by ENETC driver
+ * to drive serdes / internal SoC PHYs
+ */
+int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+                        int reg);
+int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+                         int reg, u16 val);
+
 #endif /* _ENETC_H */
index 46afac06d7c881371516e93d1598b38013892fc8..60d21537b807f0105ed97c99102b7faa26a470ff 100644 (file)
@@ -21,8 +21,8 @@ static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
                cpu_relax();
 }
 
-static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr,
-                               int devad, int reg)
+int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+                        int reg)
 {
        if (devad == MDIO_DEVAD_NONE)
                enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
@@ -51,8 +51,8 @@ static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr,
        return enetc_read(priv, ENETC_MDIO_DATA);
 }
 
-static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr,
-                                int devad, int reg, u16 val)
+int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+                         int reg, u16 val)
 {
        if (devad == MDIO_DEVAD_NONE)
                enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);