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