net: phy: mscc: add support for VSC8584 PHY
authorQuentin Schulz <quentin.schulz@bootlin.com>
Wed, 31 Oct 2018 10:20:37 +0000 (11:20 +0100)
committerJoe Hershberger <joe.hershberger@ni.com>
Mon, 5 Nov 2018 16:41:58 +0000 (10:41 -0600)
The VSC8584 PHY is a 4-port PHY that is 10/100/1000BASE-T, 100BASE-FX,
1000BASE-X and triple-speed copper SFP capable, can communicate with the
MAC via SGMII, QSGMII or 1000BASE-X, supports downshifting and can set
the blinking pattern of each of its 4 LEDs, supports hardware offloading
of MACsec and supports SyncE as well as HP Auto-MDIX detection.

This adds support for 10/100/1000BASE-T and SGMII/QSGMII link with the
MAC.

The VSC8584 has also an internal Intel 8051 microcontroller whose
firmware needs to be patched when the PHY is reset. If the 8051's
firmware has the expected CRC, its patching can be skipped. The
microcontroller can be accessed from any port of the PHY, though the CRC
function can only be done through the PHY that is the base PHY of the
package (internal address 0) due to a limitation of the firmware.

The GPIO register bank is a set of registers that are common to all PHYs
in the package. So any modification in any register of this bank affects
all PHYs of the package.

The revA of the VSC8584 PHY (which is not and will not be publicly
released) should NOT patch the firmware of the microcontroller or it'll
make things worse, the easiest way is just to not support it.

Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
drivers/net/phy/mscc.c

index 6f102f134edb7e95c60f4b1e8a886c1b8e11b7d1..86d882dd08887fdbe46ba4f62f9071ec43e1008c 100644 (file)
 
 #include <miiphy.h>
 #include <bitfield.h>
+#include <time.h>
+#include <linux/delay.h>
 
 /* Microsemi PHY ID's */
 #define PHY_ID_VSC8530                  0x00070560
 #define PHY_ID_VSC8531                  0x00070570
 #define PHY_ID_VSC8540                  0x00070760
 #define PHY_ID_VSC8541                  0x00070770
+#define PHY_ID_VSC8584                  0x000707c0
 
 /* Microsemi VSC85xx PHY Register Pages */
 #define MSCC_EXT_PAGE_ACCESS            31     /* Page Access Register */
 #define MSCC_PHY_PAGE_TEST             0x2A30 /* TEST Page registers */
 #define MSCC_PHY_PAGE_TR               0x52B5 /* Token Ring Page registers */
 
+/* Std Page Register 18 */
+#define MSCC_PHY_BYPASS_CONTROL           18
+#define PARALLEL_DET_IGNORE_ADVERTISED    BIT(3)
+
+/* Std Page Register 22 */
+#define MSCC_PHY_EXT_CNTL_STATUS          22
+#define SMI_BROADCAST_WR_EN              BIT(0)
+
 /* Std Page Register 28 - PHY AUX Control/Status */
 #define MIIM_AUX_CNTRL_STAT_REG                28
 #define MIIM_AUX_CNTRL_STAT_ACTIPHY_TO (0x0004)
 #define MAC_IF_SELECTION_RGMII         (2)
 #define MAC_IF_SELECTION_POS           (11)
 #define MAC_IF_SELECTION_WIDTH         (2)
+#define VSC8584_MAC_IF_SELECTION_MASK     BIT(12)
+#define VSC8584_MAC_IF_SELECTION_SGMII    0
+#define VSC8584_MAC_IF_SELECTION_1000BASEX 1
+#define VSC8584_MAC_IF_SELECTION_POS      12
+#define MEDIA_OP_MODE_MASK               GENMASK(10, 8)
+#define MEDIA_OP_MODE_COPPER             0
+#define MEDIA_OP_MODE_SERDES             1
+#define MEDIA_OP_MODE_1000BASEX                  2
+#define MEDIA_OP_MODE_100BASEFX                  3
+#define MEDIA_OP_MODE_AMS_COPPER_SERDES          5
+#define MEDIA_OP_MODE_AMS_COPPER_1000BASEX     6
+#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX     7
+#define MEDIA_OP_MODE_POS                8
+
+/* Extended Page 1 Register 20E1 */
+#define MSCC_PHY_ACTIPHY_CNTL            20
+#define PHY_ADDR_REVERSED                BIT(9)
+
+/* Extended Page 1 Register 23E1 */
+
+#define MSCC_PHY_EXT_PHY_CNTL_4           23
+#define PHY_CNTL_4_ADDR_POS              11
+
+/* Extended Page 1 Register 25E1 */
+#define MSCC_PHY_VERIPHY_CNTL_2                25
+
+/* Extended Page 1 Register 26E1 */
+#define MSCC_PHY_VERIPHY_CNTL_3                26
+
+/* Extended Page 2 Register 16E2 */
+#define MSCC_PHY_CU_PMD_TX_CNTL         16
 
 /* Extended Page 2 Register 20E2 */
 #define MSCC_PHY_RGMII_CNTL_REG                20
 #define RMII_CLK_OUT_ENABLE_WIDTH      (1)
 #define RMII_CLK_OUT_ENABLE_MASK       (0x10)
 
+/* Extended Page 3 Register 22E3 */
+#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
+
+/* Extended page GPIO register 00G */
+#define MSCC_DW8051_CNTL_STATUS                0
+#define MICRO_NSOFT_RESET              BIT(15)
+#define RUN_FROM_INT_ROM               BIT(14)
+#define AUTOINC_ADDR                   BIT(13)
+#define PATCH_RAM_CLK                  BIT(12)
+#define MICRO_PATCH_EN                 BIT(7)
+#define DW8051_CLK_EN                  BIT(4)
+#define MICRO_CLK_EN                   BIT(3)
+#define MICRO_CLK_DIVIDE(x)            ((x) >> 1)
+
+/* Extended page GPIO register 09G */
+#define MSCC_TRAP_ROM_ADDR(x)          ((x) * 2 + 1)
+
+/* Extended page GPIO register 10G */
+#define MSCC_PATCH_RAM_ADDR(x)         (((x) + 1) * 2)
+
+/* Extended page GPIO register 11G */
+#define MSCC_INT_MEM_ADDR              11
+
+/* Extended page GPIO register 12G */
+#define MSCC_INT_MEM_CNTL              12
+#define READ_SFR                       (BIT(14) | BIT(13))
+#define READ_PRAM                      BIT(14)
+#define READ_ROM                       BIT(13)
+#define READ_RAM                       (0x00 << 13)
+#define INT_MEM_WRITE_EN               BIT(12)
+#define EN_PATCH_RAM_TRAP_ADDR(x)      BIT((x) + 7)
+#define INT_MEM_DATA_M                 GENMASK(7, 0)
+#define INT_MEM_DATA(x)                        (INT_MEM_DATA_M & (x))
+
+/* Extended page GPIO register 18G */
+#define MSCC_PHY_PROC_CMD                18
+#define PROC_CMD_NCOMPLETED              BIT(15)
+#define PROC_CMD_FAILED                          BIT(14)
+#define PROC_CMD_SGMII_PORT(x)           ((x) << 8)
+#define PROC_CMD_FIBER_PORT(x)           BIT(8 + (x) % 4)
+#define PROC_CMD_QSGMII_PORT             (BIT(11) | BIT(10))
+#define PROC_CMD_RST_CONF_PORT           BIT(7)
+#define PROC_CMD_RECONF_PORT             (0 << 7)
+#define PROC_CMD_READ_MOD_WRITE_PORT     BIT(6)
+#define PROC_CMD_WRITE                   BIT(6)
+#define PROC_CMD_READ                    (0 << 6)
+#define PROC_CMD_FIBER_DISABLE           BIT(5)
+#define PROC_CMD_FIBER_100BASE_FX        BIT(4)
+#define PROC_CMD_FIBER_1000BASE_X        (0 << 4)
+#define PROC_CMD_SGMII_MAC               (BIT(5) | BIT(4))
+#define PROC_CMD_QSGMII_MAC              BIT(5)
+#define PROC_CMD_NO_MAC_CONF             (0x00 << 4)
+#define PROC_CMD_NOP                     GENMASK(3, 0)
+#define PROC_CMD_CRC16                   BIT(3)
+#define PROC_CMD_FIBER_MEDIA_CONF        BIT(0)
+#define PROC_CMD_MCB_ACCESS_MAC_CONF     (0x0000 << 0)
+#define PROC_CMD_NCOMPLETED_TIMEOUT_MS    500
+
+/* Extended page GPIO register 19G */
+#define MSCC_PHY_MAC_CFG_FASTLINK        19
+#define MAC_CFG_MASK                     GENMASK(15, 14)
+#define MAC_CFG_SGMII                    (0x00 << 14)
+#define MAC_CFG_QSGMII                   BIT(14)
+
+/* Test Registers */
+#define MSCC_PHY_TEST_PAGE_5           5
+
+#define MSCC_PHY_TEST_PAGE_8           8
+#define TR_CLK_DISABLE                 BIT(15)
+
 /* Token Ring Page 0x52B5 Registers */
 #define MSCC_PHY_REG_TR_ADDR_16                16
 #define MSCC_PHY_REG_TR_DATA_17                17
 #define MSCC_PHY_RESET_TIMEOUT         (100)
 #define MSCC_PHY_MICRO_TIMEOUT         (500)
 
+#define VSC8584_REVB           0x0001
+#define MSCC_DEV_REV_MASK      GENMASK(3, 0)
+
+#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR        0xe800
+#define MSCC_VSC8584_REVB_INT8051_FW_CRC       0xfb48
+
 /* RGMII/GMII Clock Delay (Skew) Options */ enum vsc_phy_rgmii_skew {
        VSC_PHY_RGMII_DELAY_200_PS,
        VSC_PHY_RGMII_DELAY_800_PS,
@@ -133,6 +251,340 @@ vsc_phy_clk_slew {
        VSC_PHY_CLK_SLEW_RATE_7,
 };
 
+static void vsc8584_csr_write(struct mii_dev *bus, int phy0, u16 addr, u32 val)
+{
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18,
+                  val >> 16);
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17,
+                  val & GENMASK(15, 0));
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+                  MSCC_PHY_TR_16_WRITE | addr);
+}
+
+static int vsc8584_cmd(struct mii_dev *bus, int phy, u16 val)
+{
+       unsigned long deadline;
+       u16 reg_val;
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_GPIO);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
+                  PROC_CMD_NCOMPLETED | val);
+
+       deadline = timer_get_us() + PROC_CMD_NCOMPLETED_TIMEOUT_MS * 1000;
+       do {
+               reg_val = bus->read(bus, phy, MDIO_DEVAD_NONE,
+                                   MSCC_PHY_PROC_CMD);
+       } while (timer_get_us() <= deadline &&
+                (reg_val & PROC_CMD_NCOMPLETED) &&
+                !(reg_val & PROC_CMD_FAILED));
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       if (reg_val & PROC_CMD_FAILED)
+               return -EIO;
+       if (reg_val & PROC_CMD_NCOMPLETED)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int vsc8584_micro_deassert_reset(struct mii_dev *bus, int phy,
+                                       bool patch_en)
+{
+       u32 enable, release;
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_GPIO);
+
+       enable = RUN_FROM_INT_ROM | MICRO_CLK_EN | DW8051_CLK_EN;
+       release = MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN |
+               MICRO_CLK_EN;
+
+       if (patch_en) {
+               enable |= MICRO_PATCH_EN;
+               release |= MICRO_PATCH_EN;
+
+               /* Clear all patches */
+               bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
+                          READ_RAM);
+       }
+
+       /*
+        * Enable 8051 Micro clock; CLEAR/SET patch present; disable PRAM clock
+        * override and addr. auto-incr; operate at 125 MHz
+        */
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, enable);
+       /* Release 8051 Micro SW reset */
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, release);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       return 0;
+}
+
+static int vsc8584_micro_assert_reset(struct mii_dev *bus, int phy)
+{
+       int ret;
+       u16 reg;
+
+       ret = vsc8584_cmd(bus, phy, PROC_CMD_NOP);
+       if (ret)
+               return ret;
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_GPIO);
+
+       reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+       reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_TRAP_ROM_ADDR(4), 0x005b);
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PATCH_RAM_ADDR(4), 0x005b);
+
+       reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+       reg |= EN_PATCH_RAM_TRAP_ADDR(4);
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD, PROC_CMD_NOP);
+
+       reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS);
+       reg &= ~MICRO_NSOFT_RESET;
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, reg);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
+                  PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_SGMII_PORT(0) |
+                  PROC_CMD_NO_MAC_CONF | PROC_CMD_READ);
+
+       reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+       reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       return 0;
+}
+
+static const u8 fw_patch_vsc8584[] = {
+       0xe8, 0x59, 0x02, 0xe8, 0x12, 0x02, 0xe8, 0x42, 0x02, 0xe8, 0x5a, 0x02,
+       0xe8, 0x5b, 0x02, 0xe8, 0x5c, 0xe5, 0x69, 0x54, 0x0f, 0x24, 0xf7, 0x60,
+       0x27, 0x24, 0xfc, 0x60, 0x23, 0x24, 0x08, 0x70, 0x14, 0xe5, 0x69, 0xae,
+       0x68, 0x78, 0x04, 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0xd8, 0xf8, 0x7e,
+       0x00, 0x54, 0x0f, 0x80, 0x00, 0x7b, 0x01, 0x7a, 0x00, 0x7d, 0xee, 0x7f,
+       0x92, 0x12, 0x50, 0xee, 0x22, 0xe4, 0xf5, 0x10, 0x85, 0x10, 0xfb, 0x7d,
+       0x1c, 0xe4, 0xff, 0x12, 0x59, 0xea, 0x05, 0x10, 0xe5, 0x10, 0xc3, 0x94,
+       0x04, 0x40, 0xed, 0x22, 0x22, 0x22, 0x22, 0x22,
+};
+
+static int vsc8584_get_fw_crc(struct mii_dev *bus, int phy, u16 start,
+                             u16 *crc, const u8 *fw_patch, int fw_size)
+{
+       int ret;
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_EXT1);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2, start);
+       /* Add one byte to size for the one added by the patch_fw function */
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_3,
+                  fw_size + 1);
+
+       ret = vsc8584_cmd(bus, phy, PROC_CMD_CRC16);
+       if (ret)
+               goto out;
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_EXT1);
+
+       *crc = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2);
+
+out:
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       return ret;
+}
+
+static int vsc8584_patch_fw(struct mii_dev *bus, int phy, const u8 *fw_patch,
+                           int fw_size)
+{
+       int i, ret;
+
+       ret = vsc8584_micro_assert_reset(bus, phy);
+       if (ret) {
+               pr_err("%s: failed to assert reset of micro\n", __func__);
+               return ret;
+       }
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_GPIO);
+
+       /*
+        * Hold 8051 Micro in SW Reset, Enable auto incr address and patch clock
+        * Disable the 8051 Micro clock
+        */
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS,
+                  RUN_FROM_INT_ROM | AUTOINC_ADDR | PATCH_RAM_CLK |
+                  MICRO_CLK_EN | MICRO_CLK_DIVIDE(2));
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_PRAM |
+                  INT_MEM_WRITE_EN | INT_MEM_DATA(2));
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_ADDR, 0x0000);
+
+       for (i = 0; i < fw_size; i++)
+               bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
+                          READ_PRAM | INT_MEM_WRITE_EN | fw_patch[i]);
+
+       /* Clear internal memory access */
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_RAM);
+
+       bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       return 0;
+}
+
+static int vsc8584_config_pre_init(struct mii_dev *bus, int phy0)
+{
+       u16 reg, crc;
+       int ret;
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       /* all writes below are broadcasted to all PHYs in the same package */
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+       reg |= SMI_BROADCAST_WR_EN;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+       /*
+        * The below register writes are tweaking analog and electrical
+        * configuration that were determined through characterization by PHY
+        * engineers. These don't mean anything more than "these are the best
+        * values".
+        */
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL);
+       reg |= PARALLEL_DET_IGNORE_ADVERTISED;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL, reg);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_EXT3);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_SERDES_TX_CRC_ERR_CNT,
+                  0x2000);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_TEST);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_5, 0x1f20);
+
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+       reg |= TR_CLK_DISABLE;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_TR);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0xafa4);
+
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18);
+       reg &= ~0x007f;
+       reg |= 0x0019;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18, reg);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0x8fa4);
+
+       vsc8584_csr_write(bus, phy0, 0x07fa, 0x0050100f);
+       vsc8584_csr_write(bus, phy0, 0x1688, 0x00049f81);
+       vsc8584_csr_write(bus, phy0, 0x0f90, 0x00688980);
+       vsc8584_csr_write(bus, phy0, 0x03a4, 0x0000d8f0);
+       vsc8584_csr_write(bus, phy0, 0x0fc0, 0x00000400);
+       vsc8584_csr_write(bus, phy0, 0x0f82, 0x0012b002);
+       vsc8584_csr_write(bus, phy0, 0x1686, 0x00000004);
+       vsc8584_csr_write(bus, phy0, 0x168c, 0x00d2c46f);
+       vsc8584_csr_write(bus, phy0, 0x17a2, 0x00000620);
+       vsc8584_csr_write(bus, phy0, 0x16a0, 0x00eeffdd);
+       vsc8584_csr_write(bus, phy0, 0x16a6, 0x00071448);
+       vsc8584_csr_write(bus, phy0, 0x16a4, 0x0013132f);
+       vsc8584_csr_write(bus, phy0, 0x16a8, 0x00000000);
+       vsc8584_csr_write(bus, phy0, 0x0ffc, 0x00c0a028);
+       vsc8584_csr_write(bus, phy0, 0x0fe8, 0x0091b06c);
+       vsc8584_csr_write(bus, phy0, 0x0fea, 0x00041600);
+       vsc8584_csr_write(bus, phy0, 0x0f80, 0x00fffaff);
+       vsc8584_csr_write(bus, phy0, 0x0fec, 0x00901809);
+       vsc8584_csr_write(bus, phy0, 0x0ffe, 0x00b01007);
+       vsc8584_csr_write(bus, phy0, 0x16b0, 0x00eeff00);
+       vsc8584_csr_write(bus, phy0, 0x16b2, 0x00007000);
+       vsc8584_csr_write(bus, phy0, 0x16b4, 0x00000814);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_EXT2);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_CU_PMD_TX_CNTL, 0x028e);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_TR);
+
+       vsc8584_csr_write(bus, phy0, 0x0486, 0x0008a518);
+       vsc8584_csr_write(bus, phy0, 0x0488, 0x006dc696);
+       vsc8584_csr_write(bus, phy0, 0x048a, 0x00000912);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_TEST);
+
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+       reg &= ~TR_CLK_DISABLE;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       /* end of write broadcasting */
+       reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+       reg &= ~SMI_BROADCAST_WR_EN;
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+       ret = vsc8584_get_fw_crc(bus, phy0,
+                                MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
+                                fw_patch_vsc8584,
+                                ARRAY_SIZE(fw_patch_vsc8584));
+       if (ret)
+               goto out;
+
+       if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) {
+               debug("FW CRC is not the expected one, patching FW...\n");
+               if (vsc8584_patch_fw(bus, phy0, fw_patch_vsc8584,
+                                    ARRAY_SIZE(fw_patch_vsc8584)))
+                       pr_warn("failed to patch FW, expect non-optimal device\n");
+       }
+
+       vsc8584_micro_deassert_reset(bus, phy0, false);
+
+       ret = vsc8584_get_fw_crc(bus, phy0,
+                                MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
+                                fw_patch_vsc8584,
+                                ARRAY_SIZE(fw_patch_vsc8584));
+       if (ret)
+               goto out;
+
+       if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC)
+               pr_warn("FW CRC after patching is not the expected one, expect non-optimal device\n");
+
+       ret = vsc8584_micro_assert_reset(bus, phy0);
+       if (ret)
+               goto out;
+
+       vsc8584_micro_deassert_reset(bus, phy0, true);
+
+out:
+       bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                  MSCC_PHY_PAGE_STD);
+
+       return ret;
+}
 
 static int mscc_vsc8531_vsc8541_init_scripts(struct phy_device *phydev)
 {
@@ -457,6 +909,96 @@ static int vsc8541_config(struct phy_device *phydev)
        return genphy_config_aneg(phydev);
 }
 
+static int vsc8584_config(struct phy_device *phydev)
+{
+       int ret;
+       u16 addr;
+       u16 reg_val;
+       u16 val;
+       u16 base_addr;
+
+       phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                 MSCC_PHY_PAGE_EXT1);
+       addr = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_4);
+       addr >>= PHY_CNTL_4_ADDR_POS;
+
+       val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_ACTIPHY_CNTL);
+       if (val & PHY_ADDR_REVERSED)
+               base_addr = phydev->addr + addr;
+       else
+               base_addr = phydev->addr - addr;
+
+       if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) {
+               pr_warn("VSC8584 revA not officially supported, skipping firmware patching. Use at your own risk.\n");
+       } else {
+               ret = vsc8584_config_pre_init(phydev->bus, base_addr);
+               if (ret)
+                       return ret;
+       }
+
+       phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                 MSCC_PHY_PAGE_GPIO);
+
+       if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
+               val = MAC_CFG_QSGMII;
+       else
+               val = MAC_CFG_SGMII;
+
+       reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK);
+       reg_val &= ~MAC_CFG_MASK;
+       reg_val |= val;
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK,
+                       reg_val);
+       if (ret)
+               return ret;
+
+       reg_val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT |
+               PROC_CMD_READ_MOD_WRITE_PORT;
+       if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
+               reg_val |= PROC_CMD_QSGMII_MAC;
+       else
+               reg_val |= PROC_CMD_SGMII_MAC;
+
+       ret = vsc8584_cmd(phydev->bus, phydev->addr, reg_val);
+       if (ret)
+               return ret;
+
+       mdelay(10);
+
+       /* Disable SerDes for 100Base-FX */
+       ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
+                         PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+                         PROC_CMD_READ_MOD_WRITE_PORT |
+                         PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX);
+       if (ret)
+               return ret;
+
+       /* Disable SerDes for 1000Base-X */
+       ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
+                         PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+                         PROC_CMD_READ_MOD_WRITE_PORT |
+                         PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X);
+       if (ret)
+               return ret;
+
+       phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+                 MSCC_PHY_PAGE_STD);
+       reg_val = phy_read(phydev, MDIO_DEVAD_NONE,
+                          MSCC_PHY_EXT_PHY_CNTL_1_REG);
+       reg_val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK);
+       reg_val |= MEDIA_OP_MODE_COPPER |
+               (VSC8584_MAC_IF_SELECTION_SGMII <<
+                VSC8584_MAC_IF_SELECTION_POS);
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_1_REG,
+                       reg_val);
+
+       ret = mscc_phy_soft_reset(phydev);
+       if (ret != 0)
+               return ret;
+
+       return genphy_config(phydev);
+}
+
 static struct phy_driver VSC8530_driver = {
        .name = "Microsemi VSC8530",
        .uid = PHY_ID_VSC8530,
@@ -497,12 +1039,23 @@ static struct phy_driver VSC8541_driver = {
        .shutdown = &genphy_shutdown,
 };
 
+static struct phy_driver VSC8584_driver = {
+       .name = "Microsemi VSC8584",
+       .uid = PHY_ID_VSC8584,
+       .mask = 0x000ffff0,
+       .features = PHY_GBIT_FEATURES,
+       .config = &vsc8584_config,
+       .startup = &mscc_startup,
+       .shutdown = &genphy_shutdown,
+};
+
 int phy_mscc_init(void)
 {
        phy_register(&VSC8530_driver);
        phy_register(&VSC8531_driver);
        phy_register(&VSC8540_driver);
        phy_register(&VSC8541_driver);
+       phy_register(&VSC8584_driver);
 
        return 0;
 }