|
@@ -11,12 +11,15 @@
|
|
|
|
|
|
#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 */
|
|
@@ -29,6 +32,14 @@
|
|
|
#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)
|
|
@@ -47,6 +58,37 @@
|
|
|
#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
|
|
@@ -72,6 +114,76 @@
|
|
|
#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
|
|
@@ -110,6 +222,12 @@
|
|
|
#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;
|
|
|
}
|