From: Dirk Eibach <dirk.eib...@gdsys.cc>

Implement support for the Marvell Alaska X 88X2242P Integrated Dual-port
and Quad-port Multi-speed Ethernet Transceivers.

Signed-off-by: Dirk Eibach <dirk.eib...@gdsys.cc>
Signed-off-by: Mario Six <mario....@gdsys.cc>
---

 drivers/net/phy/Kconfig   |  67 ++++
 drivers/net/phy/Makefile  |   1 +
 drivers/net/phy/marvell.c |   1 -
 drivers/net/phy/mv88x2.c  | 846 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/mv88x2.h  |  12 +
 drivers/net/phy/phy.c     |   3 +
 include/phy.h             |   1 +
 7 files changed, 930 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/phy/mv88x2.c
 create mode 100644 drivers/net/phy/mv88x2.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 0230852244..bb21be197f 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -55,6 +55,73 @@ config PHY_LXT
 config PHY_MARVELL
        bool "Marvell Ethernet PHYs support"
 
+
+if PHY_MARVELL
+
+config BOARD_M88E1510_CONFIG
+       bool "M88E1510 board-specific callback"
+
+endif
+
+config PHY_MV88X2
+       depends on PHYLIB_10G
+       bool "Marvell 88X2 PHYs support"
+
+if PHY_MV88X2
+
+config BOARD_MV88X2_CONFIG
+       bool "MV88X2 board-specific callback"
+
+config MV88X2_DEBUG_REGS
+       bool "Debug register printing"
+
+config MV88X2_LINE_10GBASE_R
+       bool "Enable Line 10GBASE-R support"
+
+config MV88X2_HOST_10GBASE_X2
+       bool "Enable Host 10GBASE-X2 support"
+
+config MV88X2_X2_DISPARITY
+       bool "Enable 10GBASE-X2 disparity"
+
+config MV88X2_LED0_BLINK
+       int "LED0 blink behavior"
+       range 0 11
+       default 0
+
+config MV88X2_LED0_SOLID
+       int "LED0 solid behavior"
+       range 0 7
+       default 0
+
+config MV88X2_LED1_BLINK
+       int "LED1 blink behavior"
+       range 0 11
+       default 0
+
+config MV88X2_LED1_SOLID
+       int "LED1 solid behavior"
+       range 0 7
+       default 0
+
+config MV88X2_HOST_LANE_MUX_2_PORT
+       hex "Host side lane muxing (2 ports)"
+       default 0x0
+
+config MV88X2_HOST_LANE_MUX_4_PORT
+       hex "Host side lane muxing (4 ports)"
+       default 0x0
+
+config MV88X2_SFI_POL
+       hex "SFI polarity"
+       default 0x0
+
+config MV88X2_XFI_POL
+       hex "XFI polarity"
+       default 0x0
+
+endif
+
 config PHY_MICREL
        bool "Micrel Ethernet PHYs support"
        help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 88c00a5cd3..631797a1f0 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_PHY_ET1011C) += et1011c.o
 obj-$(CONFIG_PHY_LXT) += lxt.o
 obj-$(CONFIG_PHY_MARVELL) += marvell.o
 obj-$(CONFIG_PHY_MICREL) += micrel.o
+obj-$(CONFIG_PHY_MV88X2) += mv88x2.o
 obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
 obj-$(CONFIG_PHY_REALTEK) += realtek.o
 obj-$(CONFIG_PHY_SMSC) += smsc.o
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 8eddf70c68..66107a8af3 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -720,6 +720,5 @@ int phy_marvell_init(void)
        phy_register(&M88E1510_driver);
        phy_register(&M88E1518_driver);
        phy_register(&M88E1680_driver);
-
        return 0;
 }
diff --git a/drivers/net/phy/mv88x2.c b/drivers/net/phy/mv88x2.c
new file mode 100644
index 0000000000..9d04197e47
--- /dev/null
+++ b/drivers/net/phy/mv88x2.c
@@ -0,0 +1,846 @@
+/*
+ * Driver for Marvell 88X2 PHYs
+ * Support for Revision A devices is not included.
+ *
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, eib...@gdsys.de
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <console.h>
+
+#include "mv88x2.h"
+
+enum {
+       MGD_2140M_A1    = 0x1b,         /* 88X2140M_A1 */
+
+       MGD_22x2M       = 0x31,         /* 88X22x2M */
+       MGD_2242_B0     = 0x311,        /* 88X2242_B0 */
+       MGD_2242M_A0    = 0x312,        /* 88X2242M_A0 (MGD_22x2M + rev=2) */
+       MGD_2242M_B0    = 0x313,        /* 88X2242M_B0 (MGD_22x2M + rev=3) */
+       MGD_2222_B0     = 0x315,        /* 88X2222_B0 (MGD_22x2M + rev=5) */
+       MGD_2222M_B0    = 0x317,        /* 88X2222M_B0 (MGD_22x2M + rev=7) */
+
+       MGD_22x2P       = 0x19,         /* 88X22x2P */
+       MGD_2242P_A0    = 0x191,        /* 88X2242P_A0 (MGD_22x2P + rev=1) */
+       MGD_2222P_A0    = 0x199,        /* 88X2222P_A0 (MGD_22x2P + rev=9) */
+};
+
+enum {
+       MGD_PCS_SPEED_40GBASE_R4        = 0x70,
+       MGD_PCS_SPEED_10GBASE_R         = 0x71,
+       MGD_PCS_SPEED_10GBASE_X2        = 0x72,
+       MGD_PCS_SPEED_10GBASE_X4        = 0x73,
+       MGD_PCS_SPEED_10GBASE_W         = 0x74, /* line side only */
+       MGD_PCS_SPEED_20GBASE_R2        = 0x75,
+       MGD_PCS_SPEED_20GBASE_X4        = 0x76,
+       MGD_PCS_SPEED_2500BASE_X        = 0X76,
+       MGD_PCS_SPEED_20GBASE_R8        = 0x77, /* host side only */
+       MGD_PCS_SPEED_40GBASE_R8        = 0x77, /* host side only */
+       MGD_PCS_SPEED_1000BASE_AN_OFF   = 0x7A,
+       MGD_PCS_SPEED_1000BASE_AN_ON    = 0x7B,
+       MGD_PCS_SPEED_SGMII_MAC_AN_OFF  = 0x7C,
+       MGD_PCS_SPEED_SGMII_MAC_AN_ON   = 0x7D,
+       MGD_PCS_SPEED_SGMII_LINE_AN_OFF = 0x7E,
+       MGD_PCS_SPEED_SGMII_LINE_AN_ON  = 0x7F,
+       MGD_PCS_SPEED_UNKNOWN           = 0x78, /* temp set reserved */
+};
+
+struct debug_register {
+       int port;
+       int devad;
+       int regnum;
+} debug_registers[] = {
+       /* PCS Registers */
+       { 0, 31, 0xf002 },
+       /* line side */
+       { 0, 3, 0xf074 }, /* pwr mgmt TX state control*/
+       { 0, 3, 0x0001 }, /* PCS status*/
+       { 0, 3, 0xf002 }, /* PCS port configuration*/
+       { 0, 3, 0xf010 }, /* Packet generator/CRC checker*/
+       { 0, 3, 0x3021 }, /* PCS status2 (block lock/High BER)*/
+       /* host side */
+       { 0, 4, 0xf074 }, /* Power management control*/
+       { 0, 4, 0x1018 }, /* XAUI/RXAUI alignment and sync*/
+       { 0, 4, 0x3032 }, /* XAUI/RXAUI block lock per lane*/
+       { 0, 4, 0x3034 }, /* XAUI/RXAUI alignment*/
+       { 0, 4, 0x0001 }, /* PCS status*/
+       { 0, 4, 0xf002 }, /* PCS port configuration*/
+       { 0, 4, 0xf010 }, /* Packet generator/CRC checker*/
+       { 0, 4, 0x3021 }, /* PCS status2 (block lock/High BER)*/
+       /* DSP Registers */
+       /* line side */
+       { 0, 30, 0xb841 },
+       /* port 0 */
+       { 0, 30, 0xb111 },
+       { 0, 30, 0xb116 }, /* sfi tx settings*/
+       { 0, 30, 0xb117 }, /* sfi tx settings*/
+       { 0, 30, 0xb042 }, /* dsp status*/
+       { 0, 30, 0xb132 }, /* pga*/
+       { 0, 30, 0xb000 },
+       { 0, 30, 0xb001 },
+       { 0, 30, 0xb1be }, /* snr*/
+       { 0, 30, 0xb1f1 },
+       { 0, 30, 0xb1d3 },
+       { 0, 30, 0xb065 },
+       { 0, 30, 0xb1bb },
+       { 0, 30, 0xb1bc },
+       { 0, 30, 0xb1bf },
+       { 0, 30, 0xb1b3 },
+       /* port 1 */
+       { 0, 30, 0xb311 },
+       { 0, 30, 0xb316 },
+       { 0, 30, 0xb317 },
+       { 0, 30, 0xb242 },
+       { 0, 30, 0xb332 },
+       { 0, 30, 0xb200 },
+       { 0, 30, 0xb201 },
+       { 0, 30, 0xb3be },
+       { 0, 30, 0xb3f1 },
+       { 0, 30, 0xb3d3 },
+       { 0, 30, 0xb265 },
+       { 0, 30, 0xb3bb },
+       { 0, 30, 0xb3bc },
+       { 0, 30, 0xb3bf },
+       { 0, 30, 0xb3b3 },
+       /* port 2 */
+       { 0, 30, 0xb511 },
+       { 0, 30, 0xb516 },
+       { 0, 30, 0xb517 },
+       { 0, 30, 0xb442 },
+       { 0, 30, 0xb532 },
+       { 0, 30, 0xb400 },
+       { 0, 30, 0xb401 },
+       { 0, 30, 0xb5be },
+       { 0, 30, 0xb5f1 },
+       { 0, 30, 0xb5d3 },
+       { 0, 30, 0xb465 },
+       { 0, 30, 0xb5bb },
+       { 0, 30, 0xb5bc },
+       { 0, 30, 0xb5bf },
+       { 0, 30, 0xb5b3 },
+       /* port 3 */
+       { 0, 30, 0xb711 },
+       { 0, 30, 0xb716 },
+       { 0, 30, 0xb717 },
+       { 0, 30, 0xb642 },
+       { 0, 30, 0xb732 },
+       { 0, 30, 0xb600 },
+       { 0, 30, 0xb601 },
+       { 0, 30, 0xb7be },
+       { 0, 30, 0xb7f1 },
+       { 0, 30, 0xb7d3 },
+       { 0, 30, 0xb665 },
+       { 0, 30, 0xb7bb },
+       { 0, 30, 0xb7bc },
+       { 0, 30, 0xb7bf },
+       { 0, 30, 0xb7b3 },
+       { 0, 30, 0xb753 },
+       { 0, 30, 0xb780 },
+       { 0, 30, 0xb7d4 },
+       { 0, 30, 0xb7d6 },
+       { 0, 30, 0xb7ea },
+       /* host side */
+       { 0, 30, 0x9041 },
+       /* port 0 */
+       { 0, 30, 0x8111 },
+       { 0, 30, 0x8116 },
+       { 0, 30, 0x8117 },
+       { 0, 30, 0x8042 },
+       { 0, 30, 0x8132 },
+       { 0, 30, 0x81d1 },
+       /* port 1 */
+       { 0, 30, 0x8311 },
+       { 0, 30, 0x8316 },
+       { 0, 30, 0x8317 },
+       { 0, 30, 0x8242 },
+       { 0, 30, 0x8332 },
+       { 0, 30, 0x83d1 },
+       /* port 2 */
+       { 0, 30, 0x8511 },
+       { 0, 30, 0x8516 },
+       { 0, 30, 0x8517 },
+       { 0, 30, 0x8442 },
+       { 0, 30, 0x8532 },
+       { 0, 30, 0x85d1 },
+       /* port 3 */
+       { 0, 30, 0x8711 },
+       { 0, 30, 0x8716 },
+       { 0, 30, 0x8717 },
+       { 0, 30, 0x8642 },
+       { 0, 30, 0x8732 },
+       { 0, 30, 0x87d1 },
+};
+
+static const char *get_name(u16 device_id)
+{
+       switch (device_id) {
+       case MGD_2140M_A1:
+               return "MV88X2140M_A1";
+       case MGD_2242_B0:
+               return "MV88X2242_B0";
+       case MGD_2242M_A0:
+               return "MV88X2242M_A0";
+       case MGD_2242M_B0:
+               return "MV88X2242M_B0";
+       case MGD_2222_B0:
+               return "MV88X2222_B0";
+       case MGD_2222M_B0:
+               return "MV88X2222M_B0";
+       case MGD_2242P_A0:
+               return "MV88X2242P_A0";
+       case MGD_2222P_A0:
+               return "MV88X2222P_A0";
+       }
+
+       return "";
+}
+
+static int get_num_ports(u16 device_id)
+{
+       int res = -1;
+
+       switch (device_id) {
+       case MGD_2140M_A1:
+       case MGD_2242M_A0:
+       case MGD_2242M_B0:
+       case MGD_2242_B0:
+       case MGD_2242P_A0:
+               res = 4;
+               break;
+
+       case MGD_2222P_A0:
+       case MGD_2222_B0:
+       case MGD_2222M_B0:
+               res = 2;
+               break;
+       };
+
+       return res;
+}
+
+/*
+ * 88X2 takes 4 consecutive addresses on the mdio bus
+ * this provides access to these port instances
+ */
+static int m88x2_read(struct phy_device *phydev, int port, int devad,
+                     int regnum)
+{
+       struct mii_dev *bus = phydev->bus;
+
+       return bus->read(bus, phydev->addr + port, devad, regnum);
+}
+
+static int m88x2_write(struct phy_device *phydev, int port, int devad,
+                      int regnum, u16 val)
+{
+       struct mii_dev *bus = phydev->bus;
+
+       return bus->write(bus, phydev->addr + port, devad, regnum, val);
+}
+
+static int m88x2_write_mask(struct phy_device *phydev, int port, int devad,
+                           int regnum, u16 mask, u16 val)
+{
+       u16 reg;
+       struct mii_dev *bus = phydev->bus;
+
+       reg = bus->read(bus, phydev->addr + port, devad, regnum);
+       reg &= ~mask;
+       reg |= val;
+
+       return bus->write(bus, phydev->addr + port, devad, regnum, reg);
+}
+
+static int m88x2_write_all(struct phy_device *phydev, int devad, int regnum,
+                          u16 val)
+{
+       unsigned int k;
+
+       for (k = 0; k < 4; ++k) {
+               int res = m88x2_write(phydev, k, devad, regnum, val);
+
+               if (res < 0)
+                       return res;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_MV88X2_DEBUG_REGS
+static void m88x2_dump_debug(struct phy_device *phydev)
+{
+       unsigned int k;
+
+       for (k = 0; k < ARRAY_SIZE(debug_registers); ++k) {
+               int val;
+               struct debug_register *reg = &debug_registers[k];
+
+               val = m88x2_read(phydev, reg->port, reg->devad, reg->regnum);
+               printf("%2x.%04x %04x\n", reg->devad, reg->regnum, val);
+       }
+}
+#endif
+
+/* 88X2 has a weird way of mixing revision and device id encoding */
+static int get_device_id(struct phy_device *phydev)
+{
+       u16 id2 = phy_read(phydev, 1, 3);
+       u16 device_id_raw = id2 & 0x3FF;
+       u16 device_id_base = device_id_raw >> 4;
+       u16 revision = device_id_raw & 0xF;
+       int res = -1;
+
+       switch (device_id_base) {
+       case MGD_22x2M:
+               switch (revision) {
+               case 1:
+                       res = MGD_2242_B0;
+                       break;
+               case 2:
+                       res = MGD_2242M_A0;
+                       break;
+               case 3:
+                       res = MGD_2242M_B0;
+                       break;
+               case 5:
+                       res = MGD_2222_B0;
+                       break;
+               case 7:
+                       res = MGD_2222M_B0;
+                       break;
+               }
+               break;
+       case MGD_22x2P:
+               switch (revision) {
+               case 1:
+                       res = MGD_2242P_A0;
+                       break;
+               case 9:
+                       res = MGD_2222P_A0;
+                       break;
+               }
+               break;
+       }
+
+       return res;
+}
+
+static int configure_host_lanes(struct phy_device *phydev, int num_ports)
+{
+       printf("       setup host lane mux for %d ports\n", num_ports);
+
+#ifdef CONFIG_MV88X2_HOST_LANE_MUX_4_PORT
+       if (num_ports == 4) {
+               m88x2_write(phydev, 0, 31, 0xf402,
+                           CONFIG_MV88X2_HOST_LANE_MUX_4_PORT);
+       }
+#endif
+
+#ifdef CONFIG_MV88X2_HOST_LANE_MUX_2_PORT
+       if (num_ports == 2)
+               m88x2_write(phydev, 0, 31, 0xf402,
+                           CONFIG_MV88X2_HOST_LANE_MUX_2_PORT);
+#endif
+
+       m88x2_write_mask(phydev, 0, 31, 0xf404, 0x8000, 0x8000);
+
+       m88x2_write_all(phydev, 31, 0xf003, 0x0080);
+
+       return 0;
+}
+
+static int configure_options(struct phy_device *phydev)
+{
+       struct mv88x2_config_data data;
+
+       printf("       setup board specific options\n");
+
+       memset(&data, 0, sizeof(struct mv88x2_config_data));
+
+       data.led0_blink = CONFIG_MV88X2_LED0_BLINK;
+       data.led0_solid = CONFIG_MV88X2_LED0_SOLID;
+       data.led1_blink = CONFIG_MV88X2_LED1_BLINK;
+       data.led1_solid = CONFIG_MV88X2_LED1_SOLID;
+       data.sfi_pol = CONFIG_MV88X2_SFI_POL;
+       data.xfi_pol = CONFIG_MV88X2_XFI_POL;
+
+#ifdef CONFIG_BOARD_MV88X2_CONFIG
+       board_mv88x2_config(&data);
+#endif
+
+       if (data.sfi_pol)
+               m88x2_write(phydev, 0, 31, 0xf407, data.sfi_pol);
+
+       m88x2_write(phydev, 0, 31, 0xf406, data.xfi_pol);
+
+#ifdef CONFIG_MV88X2_X2_DISPARITY
+       m88x2_write_all(phydev, 4, 0x9000, 0x0002);
+#endif
+
+       m88x2_write_all(phydev, 31, 0xf020, data.led0_blink << 8 |
+                                           data.led0_solid << 4);
+
+       m88x2_write_all(phydev, 31, 0xf021, data.led1_blink << 8 |
+                                           data.led1_solid << 4);
+       return 0;
+}
+
+static int port_power_up(struct phy_device *phydev, unsigned int device_id,
+                        unsigned int port)
+{
+       printf("       power up port %u\n", port);
+
+       /* power up all ports in config reg */
+       m88x2_write(phydev, 0, 31, 0xf403, 0x0000);
+
+       if (device_id == MGD_2242M_B0 ||
+           device_id == MGD_2222_B0 ||
+           device_id == MGD_2222M_B0 ||
+           device_id == MGD_2242_B0) {
+               m88x2_write(phydev, 0, 31, 0xf400, 0xba98);
+               m88x2_write(phydev, 0, 31, 0xf401, 0xba98);
+       }
+
+       /* power up primary ports */
+       m88x2_write(phydev, port, 3, 0, 0x2040);
+       m88x2_write(phydev, port, 4, 0, 0x2040);
+
+       /* port reset */
+       m88x2_write(phydev, port, 31, 0xf003, 0x8080);
+
+       /* power up line side */
+       m88x2_write_mask(phydev, port, 31, 0xf003, BIT(14), 0);
+       /* power up host side */
+       m88x2_write_mask(phydev, port, 31, 0xf003, BIT(6), 0);
+
+       /* SFI */
+       m88x2_write_mask(phydev, port, 1, 0, BIT(11), 0);
+#ifdef CONFIG_MV88X2_LINE_10GBASE_R
+       m88x2_write_mask(phydev, port, 3, 0, BIT(11), 0);
+#endif
+
+       /* XFI */
+#if defined(CONFIG_MV88X2_HOST_10GBASE_R)
+       m88x2_write_mask(phydev, port, 4, 0, BIT(11), 0);
+#elif defined(CONFIG_MV88X2_HOST_10GBASE_X2) || \
+       defined(CONFIG_MV88X2_HOST_10GBASE_X4)
+       m88x2_write_mask(phydev, port, 4, 0x1000, BIT(11), 0);
+#endif
+
+       return 0;
+}
+
+#if defined(CONFIG_MV88X2_LINE_10GBASE_R) && \
+       defined(CONFIG_MV88X2_HOST_10GBASE_X2)
+/* initialization sequence from Marvell MGD driver Release 2.4 */
+static int init_port_mode_2242m_b0_10gr_10gx2(struct phy_device *phydev,
+                                             unsigned int port)
+{
+       printf("       init_port_mode_2242m_b0_10gr_10gx2 port %u\n", port);
+
+       m88x2_write(phydev, 0, 30, 0xb841, 0x0000);
+       m88x2_write(phydev, 0, 30, 0x9041, 0x03aa);
+       udelay(100);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb040 + port * 0x200, 0x4000, 0x4000);
+
+       m88x2_write_mask(phydev, 0, 30, 0x8040 + port * 0x400, 0x4000, 0x4000);
+       udelay(100);
+
+       /* 10GR-10GX2 */
+       m88x2_write(phydev, port, 3, 0xf002,
+                   (0x80 << 8) | MGD_PCS_SPEED_10GBASE_R);
+       m88x2_write(phydev, port, 4, 0xf002,
+                   (0x80 << 8) | MGD_PCS_SPEED_10GBASE_X2);
+
+       m88x2_write(phydev, 0, 30, 0xb108 + port * 0x200, 0xf8d0);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb1e7 + port * 0x200, 0xc000, 0x0000);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb1e8 + port * 0x200, 0x7f00, 0x2000);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb042 + port * 0x200, 0x00f0, 0x00b0);
+
+       m88x2_write(phydev, 0, 30, 0xb1a2 + port * 0x200, 0x00b0);
+       m88x2_write(phydev, 0, 30, 0xb19c + port * 0x200, 0x00a0);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb1b5 + port * 0x200, 0x1000, 0x0000);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb1b4 + port * 0x200, 0x1000, 0x1000);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb181 + port * 0x200, 0xff00, 0x3300);
+
+       m88x2_write(phydev, 0, 30, 0x8141 + port * 0x400, 0x8f1a);
+       m88x2_write(phydev, 0, 30, 0x8131 + port * 0x400, 0x8f1a);
+       m88x2_write(phydev, 0, 30, 0x8077 + port * 0x400, 0x820a);
+
+       m88x2_write_mask(phydev, 0, 30, 0x80a0 + port * 0x400, 0x01ff, 0x0100);
+
+       m88x2_write_mask(phydev, 0, 30, 0x8085 + port * 0x400, 0x3f00, 0x0700);
+
+       m88x2_write_mask(phydev, 0, 30, 0x807b + port * 0x400, 0x0fff, 0x02b2);
+
+       m88x2_write_mask(phydev, 0, 30, 0x80b0 + port * 0x400, 0x0770, 0x0220);
+
+       m88x2_write_mask(phydev, 0, 30, 0x80b1 + port * 0x400, 0x0c30, 0x0820);
+
+       m88x2_write_mask(phydev, 0, 30, 0x8093 + port * 0x400, 0x7f00, 0x4600);
+
+       m88x2_write_mask(phydev, 0, 30, 0x809f + port * 0x400, 0x007f, 0x0079);
+
+       m88x2_write(phydev, 0, 30, 0x805c + port * 0x400, 0x4759);
+       m88x2_write(phydev, 0, 30, 0x805d + port * 0x400, 0x5900);
+       m88x2_write(phydev, port, 31, 0xf016, 0x0010);
+       /* CQ: 00006634 03/21/2016 */
+       m88x2_write_mask(phydev, 0, 30, 0xb153 + port * 0x200, 0x0070, 0x0070);
+       m88x2_write(phydev, port, 31, 0xf003, 0x8080);
+
+       return 0;
+}
+
+/* initialization sequence from Marvell MGD driver Release 2.4 */
+static int init_mode_2242m_b0_10gr_10gx2(struct phy_device *phydev,
+                                        int num_ports)
+{
+       ulong mv88x2cfg = getenv_hex("mv88x2cfg", 0xe7);
+
+       printf("       init_mode_2242m_b0_10gr_10gx2\n");
+
+       /* Chip Hardware Reset */
+       m88x2_write(phydev, 0, 31, 0xf404, 0x4000);
+       udelay(100);
+
+       configure_host_lanes(phydev, num_ports);
+       if (mv88x2cfg & BIT(7))
+               configure_options(phydev);
+
+       /* set pcs speed mode */
+       m88x2_write_all(phydev, 31, 0xf002, 0x7172);
+
+       /* Couple Lane Writing Line Side */
+       m88x2_write(phydev, 0, 30, 0xb841, 0xe000);
+       /* Couple Lane Writing Host Side */
+       m88x2_write(phydev, 0, 30, 0x9041, 0x03fe);
+
+       /* LINE: Analog BW and ETA TxPLL */
+       m88x2_write(phydev, 0, 30, 0xb108, 0xf8d0);
+
+       m88x2_write_mask(phydev, 0, 30, 0xb1e7, 0xc000, 0x0000);
+
+       /* Hack pga table index for testing boost adjustment */
+       m88x2_write_mask(phydev, 0, 30, 0xb1e8, 0x7f00, 0x2000);
+
+       /* Enable ramping detection */
+       m88x2_write_mask(phydev, 0, 30, 0xb042, 0x00f0, 0x00b0);
+
+       /* Initial gain */
+       m88x2_write(phydev, 0, 30, 0xb1a2, 0x00b0);
+       /* Initial FFE */
+       m88x2_write(phydev, 0, 30, 0xb19c, 0x00a0);
+
+       /* freeze FFE main tap adaptation */
+       m88x2_write_mask(phydev, 0, 30, 0xb1b5, 0x1000, 0x0000);
+       m88x2_write_mask(phydev, 0, 30, 0xb1b4, 0x1000, 0x1000);
+
+       /* lower FFE mu */
+       m88x2_write_mask(phydev, 0, 30, 0xb181, 0xff00, 0x3300);
+
+       /* Host: Analog BW and ETA TxPLL */
+       m88x2_write(phydev, 0, 30, 0x8141, 0x8f1a);
+       /* Host: Analog BW and ETA RxPll */
+       m88x2_write(phydev, 0, 30, 0x8131, 0x8f1a);
+       /* Change DFE threshold */
+       m88x2_write(phydev, 0, 30, 0x8077, 0x820a);
+
+       /* 3/24/2015 updates */
+       /* Force BLW gain to zero */
+       m88x2_write_mask(phydev, 0, 30, 0x80a0, 0x01ff, 0x0100);
+
+       /* Constrain C0 min */
+       m88x2_write_mask(phydev, 0, 30, 0x8085, 0x3f00, 0x0700);
+
+       /* change C0 min for best veo search */
+       m88x2_write_mask(phydev, 0, 30, 0x807b, 0x0fff, 0x02b2);
+
+       /* Force EQR/EQC value */
+       m88x2_write_mask(phydev, 0, 30, 0x80b0, 0x0770, 0x0220);
+       m88x2_write_mask(phydev, 0, 30, 0x80b1, 0x0c30, 0x0820);
+
+       /* peakdet threshold change before and after startup */
+       m88x2_write_mask(phydev, 0, 30, 0x8093, 0x7f00, 0x4600);
+       m88x2_write_mask(phydev, 0, 30, 0x809f, 0x007f, 0x0079);
+
+       /* kpkf for 6G */
+       m88x2_write(phydev, 0, 30, 0x805c, 0x4759);
+       m88x2_write(phydev, 0, 30, 0x805d, 0x5900);
+
+       /* enable sfp tx_fault, rx_los and mod_abs functions */
+       m88x2_write_all(phydev, 31, 0xf014, 0x0777);
+
+       /* enable sfp tx_disable function */
+       m88x2_write_all(phydev, 31, 0xf016, 0x0010);
+
+       /* enable sfp tx_fault, rx_los and mod_abs interrupts*/
+       m88x2_write_all(phydev, 31, 0xf010, 0x0007);
+
+       /* CQ: 00006634 03/21/2016 */
+       m88x2_write_mask(phydev, 0, 30, 0xb153, 0x0070, 0x0070);
+       m88x2_write_mask(phydev, 0, 30, 0xb353, 0x0070, 0x0070);
+       m88x2_write_mask(phydev, 0, 30, 0xb553, 0x0070, 0x0070);
+       m88x2_write_mask(phydev, 0, 30, 0xb753, 0x0070, 0x0070);
+
+       /* PCS Reset */
+       m88x2_write_all(phydev, 31, 0xf003, 0x8080);
+
+       udelay(2000);
+
+       /* release FFE main tap adaptation */
+       m88x2_write_mask(phydev, 0, 30, 0xb1b5, 0x1000, 0x1000);
+       m88x2_write_mask(phydev, 0, 30, 0xb1b4, 0x1000, 0x1000);
+
+       return 0;
+}
+#endif
+
+static int disable_tx_rx_reset(struct phy_device *phydev, unsigned int port)
+{
+       printf("       %s port %u\n", __func__, port);
+
+       m88x2_write_mask(phydev, port, 3, 0xf074, 0x2000, 0);
+
+       return 0;
+}
+
+static int m88x2_config(struct phy_device *phydev)
+{
+       unsigned int k;
+       int device_id;
+       int num_ports;
+       int sfi_pol;
+
+       /*
+        * mv88x2cfg environment variable encoding, default: 0xe7
+        * bit 0: execute init_mode()
+        * bit 1: execute init_port_mode()
+        * bit 2: execute port_power_up()
+        * bit 3: initialize sfi_pol, xfi_pol and disparity before
+        *        port_power_up()
+        * bit 4: initialize sfi_pol, xfi_pol and disparity after
+        *        port_power_up()
+        * bit 5: initialize lane_mux
+        * bit 6: disable tx/rx reset mechanism
+        * bit 7: initialize sfi_pol, xfi_pol and disparity ASAP()
+        */
+       ulong mv88x2cfg = getenv_hex("mv88x2cfg", 0xe7);
+
+       device_id = get_device_id(phydev);
+       if (device_id < 0)
+               return -1;
+
+       num_ports = get_num_ports(device_id);
+       if (num_ports < 0)
+               return -1;
+
+       printf("%s (%d ports, %02x on %s):\n", get_name(device_id), num_ports,
+              phydev->addr, phydev->bus->name);
+
+#ifdef CONFIG_MV88X2_LINE_10GBASE_R
+#ifdef CONFIG_MV88X2_HOST_10GBASE_X2
+       if (mv88x2cfg & BIT(0))
+               init_mode_2242m_b0_10gr_10gx2(phydev, num_ports);
+#endif
+#endif
+
+       if (mv88x2cfg & BIT(3))
+               configure_options(phydev);
+
+       for (k = 0; k < 4; ++k) {
+               /* on devices with 2 ports, ports 0 and 2 are active */
+               if ((k % 2) && (num_ports == 2))
+                       continue;
+
+#ifdef CONFIG_MV88X2_LINE_10GBASE_R
+#ifdef CONFIG_MV88X2_HOST_10GBASE_X2
+               if (mv88x2cfg & BIT(1))
+                       init_port_mode_2242m_b0_10gr_10gx2(phydev, k);
+#endif
+#endif
+
+               if (mv88x2cfg & BIT(6))
+                       disable_tx_rx_reset(phydev, k);
+
+               if (mv88x2cfg & BIT(2))
+                       port_power_up(phydev, device_id, k);
+       }
+
+       if (mv88x2cfg & BIT(4))
+               configure_options(phydev);
+
+       sfi_pol = m88x2_read(phydev, 0, 31, 0xf407);
+
+       /* Marvells magic SFI input polarity inversion fix */
+       if (sfi_pol & 0x0f00) {
+               puts("       ");
+               puts("apply Marvells magic SFI input polarity inversion fix\n");
+               m88x2_write(phydev, 0, 30, 0xb800, 0);
+               m88x2_write_mask(phydev, 0, 30, 0xb133, 0x0020, 0x0020);
+               m88x2_write_mask(phydev, 0, 30, 0xb333, 0x0020, 0x0020);
+               m88x2_write_mask(phydev, 0, 30, 0xb533, 0x0020, 0x0020);
+               m88x2_write_mask(phydev, 0, 30, 0xb733, 0x0020, 0x0020);
+       }
+
+       return 0;
+};
+
+static int m88x2_startup(struct phy_device *phydev)
+{
+#ifdef CONFIG_MV88X2_DEBUG_REGS
+       m88x2_dump_debug(phydev);
+#endif
+
+       return gen10g_startup(phydev);
+}
+
+static struct phy_driver M88X2_driver = {
+       .name = "Marvell 88X2",
+       .uid = 0x01410f10,
+       .mask = 0xffffff0,
+       .features = PHY_10G_FEATURES,
+       .config = &m88x2_config,
+       .startup = &m88x2_startup,
+       .shutdown = &gen10g_shutdown,
+};
+
+int phy_m88x2_init(void)
+{
+       phy_register(&M88X2_driver);
+
+       return 0;
+}
+
+static int sfi_test(struct phy_device *phydev, int portmask)
+{
+       int port;
+
+       /* PCS Reset */
+       m88x2_write_all(phydev, 31, 0xf003, 0x8080);
+
+       mdelay(2000);
+
+       for (port = 0; port < 4; ++port) {
+               if (!(portmask & BIT(port)))
+                       continue;
+               /* pseudo random byte with crc */
+               m88x2_write(phydev, port, 3, 0xf011, 0x00a0);
+               /* continuously generate packets */
+               m88x2_write(phydev, port, 3, 0xf017, 0xffff);
+               /* enable generator and checker */
+               m88x2_write(phydev, port, 3, 0xf010, 0x0003);
+               udelay(200);
+               /* clear counters */
+               m88x2_write(phydev, port, 3, 0xf010, 0x0043);
+       }
+
+       mdelay(4000);
+
+       for (port = 0; port < 4; ++port) {
+               unsigned long long rxcnt, errcnt;
+
+               if (!(portmask & BIT(port)))
+                       continue;
+
+               rxcnt = m88x2_read(phydev, port, 3, 0xf021);
+               rxcnt |= (unsigned long long)m88x2_read(phydev, port, 3,
+                                                       0xf022) << 16;
+               rxcnt |= (unsigned long long)m88x2_read(phydev, port, 3,
+                                                       0xf023) << 32;
+
+               errcnt = m88x2_read(phydev, port, 3, 0xf027);
+               errcnt |= (unsigned long long)m88x2_read(phydev, port, 3,
+                                                        0xf028) << 16;
+               errcnt |= (unsigned long long)m88x2_read(phydev, port, 3,
+                                                        0xf029) << 32;
+
+               if (errcnt > 1)
+                       printf("\033[49;1;31m");
+
+               printf("%d: %llu\033[0m/%llu ", port, errcnt, rxcnt);
+       }
+
+       printf("\n");
+
+       for (port = 0; port < 4; ++port) {
+               if (!(portmask & BIT(port)))
+                       continue;
+
+               /* disable generator */
+               m88x2_write(phydev, port, 3, 0xf010, 0x0001);
+       }
+
+       return 0;
+}
+
+static int do_mv88x2(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       char op[2];
+       int portmask = 0xf;
+       int pos = argc - 1;
+       struct mii_dev *bus;
+       struct phy_device *phydev = NULL;
+
+       if (argc < 2)
+               return CMD_RET_USAGE;
+
+       /*
+        * We use the last specified parameters, unless new ones are
+        * entered.
+        */
+       op[0] = argv[1][0];
+
+       bus = mdio_get_current_dev();
+
+       switch (op[0]) {
+       case 's':
+               if (pos > 3)
+                       portmask = simple_strtoul(argv[pos--], NULL, 16);
+               break;
+       }
+
+       if (pos > 2)
+               bus = miiphy_get_dev_by_name(argv[pos - 1]);
+       if (!bus)
+               return -1;
+
+       if (pos > 1) {
+               unsigned int addr = simple_strtoul(argv[pos], NULL, 16);
+
+               if (addr > 31)
+                       return -1;
+               phydev = phy_find_by_mask_c45(bus, BIT(addr),
+                                             PHY_INTERFACE_MODE_XGMII);
+       }
+       if (!phydev)
+               return -1;
+
+       switch (op[0]) {
+       case 's':
+               sfi_test(phydev, portmask);
+               break;
+       }
+
+       return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+       mv88x2, 6,      0,      do_mv88x2,
+       "MV88X2 utility commands",
+       "sfitest <busname> <addr> [<portmask>] - use PHY packet 
generator/checker to test SFI ports at <portmask>\n"
+);
diff --git a/drivers/net/phy/mv88x2.h b/drivers/net/phy/mv88x2.h
new file mode 100644
index 0000000000..6cd2073cae
--- /dev/null
+++ b/drivers/net/phy/mv88x2.h
@@ -0,0 +1,12 @@
+struct mv88x2_config_data {
+       ulong sfi_pol;
+       ulong xfi_pol;
+       u16 led0_blink;
+       u16 led0_solid;
+       u16 led1_blink;
+       u16 led1_solid;
+};
+
+#ifdef CONFIG_BOARD_MV88X2_CONFIG
+int board_mv88x2_config(struct mv88x2_config_data *data);
+#endif
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index a16bb6ca89..f39ab39061 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -493,6 +493,9 @@ int phy_init(void)
 #ifdef CONFIG_PHY_MICREL
        phy_micrel_init();
 #endif
+#ifdef CONFIG_PHY_MV88X2
+       phy_m88x2_init();
+#endif
 #ifdef CONFIG_PHY_NATSEMI
        phy_natsemi_init();
 #endif
diff --git a/include/phy.h b/include/phy.h
index 75a9ae3314..1c94f511a7 100644
--- a/include/phy.h
+++ b/include/phy.h
@@ -275,6 +275,7 @@ int phy_et1011c_init(void);
 int phy_lxt_init(void);
 int phy_marvell_init(void);
 int phy_micrel_init(void);
+int phy_m88x2_init(void);
 int phy_natsemi_init(void);
 int phy_realtek_init(void);
 int phy_smsc_init(void);
-- 
2.11.0

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to