[U-Boot] [PATCH 09/51] phy: Support Marvell 88X2242
Mario Six
mario.six at gdsys.cc
Fri Jul 14 12:54:55 UTC 2017
From: Dirk Eibach <dirk.eibach at 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.eibach at gdsys.cc>
Signed-off-by: Mario Six <mario.six at 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, eibach at 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
More information about the U-Boot
mailing list