From: Igal Liberman <ig...@marvell.com>

E2110 support 10M/100M/1G/2.5G/5G speed and use C45 register definition.
Need to use C45 or C22 r13/r14 indirect method to access

Signed-off-by: Kevin Shi <k...@marvell.com>
Signed-off-by: Igal Liberman <ig...@marvell.com>
Signed-off-by: Stefan Roese <s...@denx.de>
---

 drivers/net/phy/marvell.c | 205 ++++++++++++++++++++++++++++++++++++++
 include/linux/ethtool.h   |   3 +-
 2 files changed, 207 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index a62c695c5c84..c3f86d98f9e3 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -6,6 +6,7 @@
  * author Andy Fleming
  */
 #include <common.h>
+#include <console.h>
 #include <errno.h>
 #include <phy.h>
 #include <linux/bitops.h>
@@ -104,6 +105,19 @@
 #define MIIM_88E151x_MODE_SGMII                1
 #define MIIM_88E151x_RESET_OFFS                15
 
+/* 88E2110 PHY defines */
+#define MIIM_88E2110_PHY_STATUS                0x8008
+#define MIIM_88E2110_PHYSTAT_SPEED     0xc000
+#define MIIM_88E2110_PHYSTAT_10GBIT    0xc000
+#define MIIM_88E2110_PHYSTAT_GBIT      0x8000
+#define MIIM_88E2110_PHYSTAT_100       0x4000
+#define MIIM_88E2110_PHYSTAT_DUPLEX    0x2000
+#define MIIM_88E2110_PHYSTAT_SPDDONE   0x0800
+#define MIIM_88E2110_PHYSTAT_LINK      0x0400
+#define MIIM_88E2110_PHYSTAT_SPEED_5G  0x000c
+#define MIIM_88E2110_PHYSTAT_5GBIT     0x0008
+#define MIIM_88E2110_PHYSTAT_2_5GBIT   0x0004
+
 static int m88e1xxx_phy_extread(struct phy_device *phydev, int addr,
                                int devaddr, int regnum)
 {
@@ -590,6 +604,185 @@ static int m88e1680_config(struct phy_device *phydev)
        return 0;
 }
 
+/* Marvell 88E2110 */
+static int m88e2110_probe(struct phy_device *phydev)
+{
+       /*
+        * skip reset since phy has its own initial value.
+        * resettting leads to weird behavior
+        */
+       phydev->flags |= PHY_FLAG_BROKEN_RESET;
+
+       return 0;
+}
+
+static int m88e2110_config(struct phy_device *phydev)
+{
+       u16 reg;
+
+       /* Perform lane swap */
+       reg = phy_read(phydev, 1, 0xc000);
+       reg |= 0x1;
+       phy_write(phydev, 1, 0xc000, reg);
+
+       /* Configure auto-negotiation advertisement */
+       if (phydev->interface == PHY_INTERFACE_MODE_SFI) {
+               /* Disabled 10G advertisement */
+               phy_write(phydev, 7, 0x20, 0x1e1);
+       } else {
+               if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) {
+                       /* Disabled 10G/5G advertisements */
+                       phy_write(phydev, 7, 0x20, 0xa1);
+               } else {
+                       /* Disable 10G/5G/2.5G auto-negotiation advertisement */
+                       phy_write(phydev, 7, 0x20, 0x1);
+               }
+       }
+
+       /* Restart auto-negotiation */
+       phy_write(phydev, 7, 0, 0x3200);
+
+       return 0;
+}
+
+/* Parse the 88E2110's status register for speed and duplex
+ * information
+ */
+static uint m88e2110_parse_status(struct phy_device *phydev)
+{
+       unsigned int speed;
+       unsigned int mii_reg;
+
+       mii_reg = phy_read(phydev, 3, MIIM_88E2110_PHY_STATUS);
+
+       if ((mii_reg & MIIM_88E2110_PHYSTAT_LINK) &&
+           !(mii_reg & MIIM_88E2110_PHYSTAT_SPDDONE)) {
+               int i = 0;
+
+               puts("Waiting for PHY realtime link");
+               while (!(mii_reg & MIIM_88E2110_PHYSTAT_SPDDONE)) {
+                       /* Timeout reached ? */
+                       if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+                               puts(" TIMEOUT !\n");
+                               phydev->link = 0;
+                               break;
+                       }
+
+                       if ((i++ % 1000) == 0)
+                               putc('.');
+                       udelay(1000);
+                       mii_reg = phy_read(phydev, 3, MIIM_88E2110_PHY_STATUS);
+               }
+               puts(" done\n");
+               mdelay(500);    /* another 500 ms (results in faster booting) */
+       } else {
+               if (mii_reg & MIIM_88E2110_PHYSTAT_LINK)
+                       phydev->link = 1;
+               else
+                       phydev->link = 0;
+       }
+
+       if (mii_reg & MIIM_88E2110_PHYSTAT_DUPLEX)
+               phydev->duplex = DUPLEX_FULL;
+       else
+               phydev->duplex = DUPLEX_HALF;
+
+       speed = mii_reg & MIIM_88E2110_PHYSTAT_SPEED;
+
+       switch (speed) {
+       case MIIM_88E2110_PHYSTAT_10GBIT:
+               switch (mii_reg & MIIM_88E2110_PHYSTAT_SPEED_5G) {
+               case MIIM_88E2110_PHYSTAT_5GBIT:
+                       phydev->speed = SPEED_5000;
+                       break;
+               case MIIM_88E2110_PHYSTAT_2_5GBIT:
+                       phydev->speed = SPEED_2500;
+                       break;
+               default:
+                       puts(" Unknown speed detected\n");
+                       break;
+               }
+       case MIIM_88E2110_PHYSTAT_GBIT:
+               phydev->speed = SPEED_1000;
+               break;
+       case MIIM_88E2110_PHYSTAT_100:
+               phydev->speed = SPEED_100;
+               break;
+       default:
+               phydev->speed = SPEED_10;
+               break;
+       }
+
+       return 0;
+}
+
+static int m88e2110_update_link(struct phy_device *phydev)
+{
+       unsigned int mii_reg;
+
+       /*
+        * Wait if the link is up, and autonegotiation is in progress
+        * (ie - we're capable and it's not done)
+        */
+       mii_reg = phy_read(phydev, 7, MII_BMSR);
+
+       /*
+        * If we already saw the link up, and it hasn't gone down, then
+        * we don't need to wait for autoneg again
+        */
+       if (phydev->link && mii_reg & BMSR_LSTATUS)
+               return 0;
+
+       if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
+               int i = 0;
+
+               debug("%s Waiting for PHY auto negotiation to complete",
+                     phydev->drv->name);
+               while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
+                       /*
+                        * Timeout reached ?
+                        */
+                       if (i > PHY_ANEG_TIMEOUT) {
+                               debug(" TIMEOUT !\n");
+                               phydev->link = 0;
+                               return 0;
+                       }
+
+                       if (ctrlc()) {
+                               puts("user interrupt!\n");
+                               phydev->link = 0;
+                               return -EINTR;
+                       }
+
+                       if ((i++ % 500) == 0)
+                               debug(".");
+
+                       udelay(1000);   /* 1 ms */
+                       mii_reg = phy_read(phydev, 7, MII_BMSR);
+               }
+               debug(" done\n");
+               phydev->link = 1;
+       } else {
+               /* Read the link a second time to clear the latched state */
+               mii_reg = phy_read(phydev, 7, MII_BMSR);
+
+               if (mii_reg & BMSR_LSTATUS)
+                       phydev->link = 1;
+               else
+                       phydev->link = 0;
+       }
+
+       return 0;
+}
+
+static int m88e2110_startup(struct phy_device *phydev)
+{
+       m88e2110_update_link(phydev);
+       m88e2110_parse_status(phydev);
+
+       return 0;
+}
+
 static struct phy_driver M88E1011S_driver = {
        .name = "Marvell 88E1011S",
        .uid = 0x1410c60,
@@ -692,6 +885,17 @@ static struct phy_driver M88E1680_driver = {
        .shutdown = &genphy_shutdown,
 };
 
+static struct phy_driver M88E2110_driver = {
+       .name = "Marvell 88E2110",
+       .uid = 0x2b09b8,
+       .mask = 0xffffff0,
+       .features = PHY_GBIT_FEATURES,
+       .probe = &m88e2110_probe,
+       .config = &m88e2110_config,
+       .startup = &m88e2110_startup,
+       .shutdown = &genphy_shutdown,
+};
+
 int phy_marvell_init(void)
 {
        phy_register(&M88E1310_driver);
@@ -704,6 +908,7 @@ int phy_marvell_init(void)
        phy_register(&M88E1011S_driver);
        phy_register(&M88E151x_driver);
        phy_register(&M88E1680_driver);
+       phy_register(&M88E2110_driver);
 
        return 0;
 }
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index f6dbdb096d34..a97bde48df99 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -614,11 +614,12 @@ enum ethtool_sfeatures_retval_bits {
  * it was foced up into this mode or autonegotiated.
  */
 
-/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */
+/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb,  5Gb, 10GbE. */
 #define SPEED_10               10
 #define SPEED_100              100
 #define SPEED_1000             1000
 #define SPEED_2500             2500
+#define SPEED_5000             5000
 #define SPEED_10000            10000
 
 /* Duplex, half or full. */
-- 
2.31.0

Reply via email to