#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,
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)
{
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,
.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;
}