diff mbox

[U-Boot,09/51] phy: Support Marvell 88X2242

Message ID 20170714125537.14895-10-mario.six@gdsys.cc
State Rejected, archived
Delegated to: Mario Six
Headers show

Commit Message

Mario Six July 14, 2017, 12:54 p.m. UTC
From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
Signed-off-by: Mario Six <mario.six@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

Comments

Simon Glass July 18, 2017, 2:01 p.m. UTC | #1
Hi Mario,

On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
> Signed-off-by: Mario Six <mario.six@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
>

We should really be using driver model here. Is the generic phy uclass
good enough (generic-phy.h), or do we need a new uclass for Ethernet
PHY?

Regards,
Simon
Mario Six July 25, 2017, 8:22 a.m. UTC | #2
Hi Simon,

On Tue, Jul 18, 2017 at 4:01 PM, Simon Glass <sjg@chromium.org> wrote:
> Hi Mario,
>
> On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
>> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
>> Signed-off-by: Mario Six <mario.six@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
>>
>
> We should really be using driver model here. Is the generic phy uclass
> good enough (generic-phy.h), or do we need a new uclass for Ethernet
> PHY?
>

Wouldn't we also need a uclass for MDIO/MII interfaces then? I don't know the
network subsystem too well, so I can't really tell where a ethernet phy uclass
(or a MDIO uclass for that matter) would interface with the rest of it.

> Regards,
> Simon

Best regards,

Mario
Joe Hershberger July 27, 2017, 9:23 p.m. UTC | #3
On Tue, Jul 25, 2017 at 3:22 AM, Mario Six <mario.six@gdsys.cc> wrote:
> Hi Simon,
>
> On Tue, Jul 18, 2017 at 4:01 PM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Mario,
>>
>> On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
>>> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
>>> Signed-off-by: Mario Six <mario.six@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
>>>
>>
>> We should really be using driver model here. Is the generic phy uclass
>> good enough (generic-phy.h), or do we need a new uclass for Ethernet
>> PHY?
>>
>
> Wouldn't we also need a uclass for MDIO/MII interfaces then? I don't know the
> network subsystem too well, so I can't really tell where a ethernet phy uclass
> (or a MDIO uclass for that matter) would interface with the rest of it.

The reason I've avoided implementing the uclass for phys so far is
that the DTs of various boards are quite different in how they relate
MDIO, PHY, and ETH. I suspect we'll end up needing some sort of ETH /
platform / board specific DT parsing code and some way to hook into
that.

I don't think the generic-phy is appropriate. Quite unrelated to Ethernet phys.

>
>> Regards,
>> Simon
>
> Best regards,
>
> Mario
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> https://lists.denx.de/listinfo/u-boot
Simon Glass Aug. 6, 2017, 5:16 a.m. UTC | #4
Hi Joe,

On 27 July 2017 at 15:23, Joe Hershberger <joe.hershberger@ni.com> wrote:
> On Tue, Jul 25, 2017 at 3:22 AM, Mario Six <mario.six@gdsys.cc> wrote:
>> Hi Simon,
>>
>> On Tue, Jul 18, 2017 at 4:01 PM, Simon Glass <sjg@chromium.org> wrote:
>>> Hi Mario,
>>>
>>> On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
>>>> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
>>>> Signed-off-by: Mario Six <mario.six@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
>>>>
>>>
>>> We should really be using driver model here. Is the generic phy uclass
>>> good enough (generic-phy.h), or do we need a new uclass for Ethernet
>>> PHY?
>>>
>>
>> Wouldn't we also need a uclass for MDIO/MII interfaces then? I don't know the
>> network subsystem too well, so I can't really tell where a ethernet phy uclass
>> (or a MDIO uclass for that matter) would interface with the rest of it.
>
> The reason I've avoided implementing the uclass for phys so far is
> that the DTs of various boards are quite different in how they relate
> MDIO, PHY, and ETH. I suspect we'll end up needing some sort of ETH /
> platform / board specific DT parsing code and some way to hook into
> that.
>

Do you mean that we cannot have plain PHY driver (e.g. for MII) that
works with multiple bords? Don't we have something similar already?
What can we do to move this forward.

Regards,
Simon
Joe Hershberger Aug. 7, 2017, 4:37 p.m. UTC | #5
On Sun, Aug 6, 2017 at 12:16 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Joe,
>
> On 27 July 2017 at 15:23, Joe Hershberger <joe.hershberger@ni.com> wrote:
>> On Tue, Jul 25, 2017 at 3:22 AM, Mario Six <mario.six@gdsys.cc> wrote:
>>> Hi Simon,
>>>
>>> On Tue, Jul 18, 2017 at 4:01 PM, Simon Glass <sjg@chromium.org> wrote:
>>>> Hi Mario,
>>>>
>>>> On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
>>>>> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
>>>>> Signed-off-by: Mario Six <mario.six@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
>>>>>
>>>>
>>>> We should really be using driver model here. Is the generic phy uclass
>>>> good enough (generic-phy.h), or do we need a new uclass for Ethernet
>>>> PHY?
>>>>
>>>
>>> Wouldn't we also need a uclass for MDIO/MII interfaces then? I don't know the
>>> network subsystem too well, so I can't really tell where a ethernet phy uclass
>>> (or a MDIO uclass for that matter) would interface with the rest of it.
>>
>> The reason I've avoided implementing the uclass for phys so far is
>> that the DTs of various boards are quite different in how they relate
>> MDIO, PHY, and ETH. I suspect we'll end up needing some sort of ETH /
>> platform / board specific DT parsing code and some way to hook into
>> that.
>>
>
> Do you mean that we cannot have plain PHY driver (e.g. for MII) that
> works with multiple bords? Don't we have something similar already?
> What can we do to move this forward.

I think the driver is not the issue. If we want to be able to leverage
the same DT bindings that Linux does, then it's not going to be
generic. How the MDIO driver is associated to an ETH driver and how
that ETH driver is associated with a PHY driver will all be unique to
each ETH driver. By that I mean the drivers can be shared for the
PHYs, but how they are bound will be unique. We can't have global PHY
binding code in the PHY uclass.

Do we do something like this elsewhere? Maybe in PCI or something? I
haven't gone looking so far.

Cheers,
-Joe
Simon Glass Aug. 13, 2017, 9:35 p.m. UTC | #6
Hi Joe,

On 7 August 2017 at 10:37, Joe Hershberger <joe.hershberger@gmail.com> wrote:
> On Sun, Aug 6, 2017 at 12:16 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Joe,
>>
>> On 27 July 2017 at 15:23, Joe Hershberger <joe.hershberger@ni.com> wrote:
>>> On Tue, Jul 25, 2017 at 3:22 AM, Mario Six <mario.six@gdsys.cc> wrote:
>>>> Hi Simon,
>>>>
>>>> On Tue, Jul 18, 2017 at 4:01 PM, Simon Glass <sjg@chromium.org> wrote:
>>>>> Hi Mario,
>>>>>
>>>>> On 14 July 2017 at 05:54, Mario Six <mario.six@gdsys.cc> wrote:
>>>>>> From: Dirk Eibach <dirk.eibach@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@gdsys.cc>
>>>>>> Signed-off-by: Mario Six <mario.six@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
>>>>>>
>>>>>
>>>>> We should really be using driver model here. Is the generic phy uclass
>>>>> good enough (generic-phy.h), or do we need a new uclass for Ethernet
>>>>> PHY?
>>>>>
>>>>
>>>> Wouldn't we also need a uclass for MDIO/MII interfaces then? I don't know the
>>>> network subsystem too well, so I can't really tell where a ethernet phy uclass
>>>> (or a MDIO uclass for that matter) would interface with the rest of it.
>>>
>>> The reason I've avoided implementing the uclass for phys so far is
>>> that the DTs of various boards are quite different in how they relate
>>> MDIO, PHY, and ETH. I suspect we'll end up needing some sort of ETH /
>>> platform / board specific DT parsing code and some way to hook into
>>> that.
>>>
>>
>> Do you mean that we cannot have plain PHY driver (e.g. for MII) that
>> works with multiple bords? Don't we have something similar already?
>> What can we do to move this forward.
>
> I think the driver is not the issue. If we want to be able to leverage
> the same DT bindings that Linux does, then it's not going to be
> generic. How the MDIO driver is associated to an ETH driver and how
> that ETH driver is associated with a PHY driver will all be unique to
> each ETH driver. By that I mean the drivers can be shared for the
> PHYs, but how they are bound will be unique. We can't have global PHY
> binding code in the PHY uclass.
>
> Do we do something like this elsewhere? Maybe in PCI or something? I
> haven't gone looking so far.

Yes. That seems fine to me. We already do that in some other cases
(e.g. USB and PCI devices are detected at run-time and devices bound
accordingly).

How can we get started on this?

>
> Cheers,
> -Joe

Regards,
Simon
diff mbox

Patch

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@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);