On Fri, Sep 14, 2018 at 7:50 AM Quentin Schulz <quentin.sch...@bootlin.com> wrote: > > 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.sch...@bootlin.com> > --- > drivers/net/phy/mscc.c | 542 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 542 insertions(+) > > diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c > index 439f5e3c5c..0e54fe0eff 100644 > --- a/drivers/net/phy/mscc.c > +++ b/drivers/net/phy/mscc.c > @@ -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 0x0008 > + > +/* Std Page Register 22 */ > +#define MSCC_PHY_EXT_CNTL_STATUS 22 > +#define SMI_BROADCAST_WR_EN 0x0001 > + > /* 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 0x1000 > +#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 0x0700
Please use GENMASK() or BIT() for masks. > +#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 0x0200 > + > +/* 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,74 @@ > #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 0x8000 Bit definitions such as these should use BIT(). > +#define RUN_FROM_INT_ROM 0x4000 > +#define AUTOINC_ADDR 0x2000 > +#define PATCH_RAM_CLK 0x1000 > +#define MICRO_PATCH_EN 0x0080 > +#define DW8051_CLK_EN 0x0010 > +#define MICRO_CLK_EN 0x0008 > +#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 0x6000 > +#define READ_PRAM 0x4000 > +#define READ_ROM 0x2000 > +#define READ_RAM 0x0000 > +#define INT_MEM_WRITE_EN 0x1000 > +#define EN_PATCH_RAM_TRAP_ADDR(x) (0x0100 << ((x) - 1)) > +#define INT_MEM_DATA_M 0x00ff > +#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 0x8000 > +#define PROC_CMD_FAILED 0x4000 > +#define PROC_CMD_SGMII_PORT(x) ((x) << 8) > +#define PROC_CMD_FIBER_PORT(x) (0x0100 << (x) % 4) > +#define PROC_CMD_QSGMII_PORT 0x0c00 > +#define PROC_CMD_RST_CONF_PORT 0x0080 > +#define PROC_CMD_RECONF_PORT 0x0000 > +#define PROC_CMD_READ_MOD_WRITE_PORT 0x0040 > +#define PROC_CMD_WRITE 0x0040 > +#define PROC_CMD_READ 0x0000 > +#define PROC_CMD_FIBER_DISABLE 0x0020 > +#define PROC_CMD_FIBER_100BASE_FX 0x0010 > +#define PROC_CMD_FIBER_1000BASE_X 0x0000 > +#define PROC_CMD_SGMII_MAC 0x0030 > +#define PROC_CMD_QSGMII_MAC 0x0020 > +#define PROC_CMD_NO_MAC_CONF 0x0000 > +#define PROC_CMD_NOP 0x000f > +#define PROC_CMD_CRC16 0x0008 > +#define PROC_CMD_FIBER_MEDIA_CONF 0x0001 > +#define PROC_CMD_MCB_ACCESS_MAC_CONF 0x0000 > +#define PROC_CMD_NCOMPLETED_TIMEOUT_MS 500 > + > +/* Extended page GPIO register 19G */ > +#define MSCC_PHY_MAC_CFG_FASTLINK 19 > +#define MAC_CFG_MASK 0xc000 > +#define MAC_CFG_SGMII 0x0000 > +#define MAC_CFG_QSGMII 0x4000 > + > +/* Test Registers */ > +#define MSCC_PHY_TEST_PAGE_5 5 > +#define MSCC_PHY_TEST_PAGE_8 8 > + > /* Token Ring Page 0x52B5 Registers */ > #define MSCC_PHY_REG_TR_ADDR_16 16 > #define MSCC_PHY_REG_TR_DATA_17 17 > @@ -110,6 +220,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 +249,331 @@ 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 Please correct the comment style... "/*" on its own line got multi-line comments. > + * 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 Please use /* */ style comments. > + 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); > + > + 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); > + > + 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 |= 0x8000; Comment, use BIT(31), and use #define > + 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; Use GENMASK() and #define > + reg |= 0x0019; Magic number... use #define. > + 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); Magic number... use #define > + Comment what this is doing and link the errata that it came from... > + 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 &= ~0x8000; > + 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); > + > + 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 +898,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 +1028,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; > } > -- > 2.17.1 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > https://lists.denx.de/listinfo/u-boot _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot