Message ID | 20190417133420.18396-1-kavyasree.kotagiri@microchip.com |
---|---|
State | Changes Requested |
Delegated to: | David Miller |
Headers | show |
Series | net: phy: mscc: Improvements to VSC8514 PHY driver. | expand |
On 17.04.2019 15:34, Kavyasree.Kotagiri@microchip.com wrote: > From: Kavya Sree Kotagiri <kavyasree.kotagiri@microchip.com> > > The VSC8514 PHY is a 4-ports PHY that is 10/100/1000BASE-T, 100BASE-FX, > 1000BASE-X, can communicate with the MAC via QSGMII. > The MAC interface protocol for each port within QSGMII can > be either 1000BASE-X or SGMII, if the QSGMII MAC that the VSC8514 is > connecting to supports this functionality. > VSC8514 also supports SGMII MAC-side autonegotiation on each individual > port, downshifting, can set the blinking pattern of each of its 4 LEDs, > SyncE, 1000BASE-T Ring Resiliency as well as HP Auto-MDIX detection. > > This adds support for 10BASE-T, 100BASE-TX, and 1000BASE-T, > QSGMII link with the MAC, downshifting, HP Auto-MDIX detection > and blinking pattern for its 4 LEDs. > > 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. > > If the PHYs haven't been reset before booting the Linux kernel and were > configured to use interrupts for e.g. link status updates, it is > required to clear the interrupts mask register of all PHYs before being > able to use interrupts with any PHY. The first PHY of the package that > will be init will take care of clearing all PHYs interrupts mask > registers. Thus, we need to keep track of the init sequence in the > package, if it's already been done or if it's to be done. > > Most of the init sequence of a PHY of the package is common to all PHYs > in the package, thus we use the SMI broadcast feature which enables us > to propagate a write in one register of one PHY to all PHYs in the same > package. > > Signed-off-by: Kavya Sree Kotagiri <kavyasree.kotagiri@microchip.com> > Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com> > Co-developed-by: Quentin Schulz <quentin.schulz@bootlin.com> > --- > > Changes in v6: > - Added proper return value in vsc85xx_csr_ctrl_phy_read(). > - Replaced __mdiobus_write and__mdiobus_read with __phy_write and __phy_read resp. > - Replaced register addresses in 8514_config_init() with proper constants. > > Changes in v5: > - Added return statements in functions calling vsc85xx_csr_ctrl_phy_read(). > - Added comments in vsc85xx_csr_ctrl_phy_read() and vsc85xx_csr_ctrl_phy_write(). > > Changes in v4: > - Removed features and aneg_done settings. > > Changes in v3: > - Used BIT(x) instead of hex values. > - Replaced magic numbers with constants. > - Handled delays and timeouts. > - Added comments where needed. > > Changes in v2: > - Sorted variable declarations. > > drivers/net/phy/Kconfig | 2 +- > drivers/net/phy/mscc.c | 471 ++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 472 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index 520657945b82..89085e87ecab 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -397,7 +397,7 @@ config MICROCHIP_T1_PHY > config MICROSEMI_PHY > tristate "Microsemi PHYs" > ---help--- > - Currently supports VSC8530, VSC8531, VSC8540 and VSC8541 PHYs > + Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs > > config NATIONAL_PHY > tristate "National Semiconductor PHYs" > diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c > index db50efb30df5..e71fef0f788a 100644 > --- a/drivers/net/phy/mscc.c > +++ b/drivers/net/phy/mscc.c > @@ -85,12 +85,48 @@ enum rgmii_rx_clock_delay { > #define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x)) > #define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x)) > > +#define MSCC_EXT_PAGE_CSR_CNTL_17 17 > +#define MSCC_EXT_PAGE_CSR_CNTL_18 18 > + > +#define MSCC_EXT_PAGE_CSR_CNTL_19 19 > +#define MSCC_PHY_CSR_CNTL_19_REG_ADDR(x) (x) > +#define MSCC_PHY_CSR_CNTL_19_TARGET(x) ((x) << 12) > +#define MSCC_PHY_CSR_CNTL_19_READ BIT(14) > +#define MSCC_PHY_CSR_CNTL_19_CMD BIT(15) > + > +#define MSCC_EXT_PAGE_CSR_CNTL_20 20 > +#define MSCC_PHY_CSR_CNTL_20_TARGET(x) (x) > + > +#define PHY_MCB_TARGET 0x07 > +#define PHY_MCB_S6G_WRITE BIT(31) > +#define PHY_MCB_S6G_READ BIT(30) > + > +#define PHY_MCB_S6G_CFG 0x3f > +#define PHY_S6G_LCPLL_CFG 0x11 > +#define PHY_S6G_PLL5G_CFG0 0x06 > +#define PHY_S6G_PLL_CFG 0x2b > +#define PHY_S6G_PLL_STATUS 0x31 > +#define PHY_S6G_COMMON_CFG 0x2c > +#define PHY_S6G_MISC_CFG 0x3b > +#define PHY_S6G_GPC_CFG 0x2e > +#define PHY_S6G_IB_STATUS0 0x2f > + > +#define PHY_S6G_SYS_RST_POS 31 > +#define PHY_S6G_ENA_LANE_POS 18 > +#define PHY_S6G_ENA_LOOP_POS 8 > +#define PHY_S6G_QRATE_POS 6 > +#define PHY_S6G_IF_MODE_POS 4 > +#define PHY_S6G_PLL_ENA_OFFS_POS 21 > +#define PHY_S6G_PLL_FSM_CTRL_DATA_POS 8 > +#define PHY_S6G_PLL_FSM_ENA_POS 7 > + > #define MSCC_EXT_PAGE_ACCESS 31 > #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ > #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ > #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */ > #define MSCC_PHY_PAGE_EXTENDED_3 0x0003 /* Extended reg - page 3 */ > #define MSCC_PHY_PAGE_EXTENDED_4 0x0004 /* Extended reg - page 4 */ > +#define MSCC_PHY_PAGE_CSR_CNTL MSCC_PHY_PAGE_EXTENDED_4 > /* Extended reg - GPIO; this is a bank of registers that are shared for all PHYs > * in the same package. > */ > @@ -216,6 +252,7 @@ enum rgmii_rx_clock_delay { > #define MSCC_PHY_TR_MSB 18 > > /* Microsemi PHY ID's */ > +#define PHY_ID_VSC8514 0x00070670 > #define PHY_ID_VSC8530 0x00070560 > #define PHY_ID_VSC8531 0x00070570 > #define PHY_ID_VSC8540 0x00070760 > @@ -1742,6 +1779,391 @@ static int vsc8584_did_interrupt(struct phy_device *phydev) > return (rc < 0) ? 0 : rc & MII_VSC85XX_INT_MASK_MASK; > } > > +static int vsc8514_config_pre_init(struct phy_device *phydev) > +{ > + /* These are the settings to override the silicon default > + * values to handle hardware performance of PHY. They > + * are set at Power-On state and remain until PHY Reset. > + */ > + const struct reg_val pre_init1[] = { > + {0x0f90, 0x00688980}, > + {0x0786, 0x00000003}, > + {0x07fa, 0x0050100f}, > + {0x0f82, 0x0012b002}, > + {0x1686, 0x00000004}, > + {0x168c, 0x00d2c46f}, > + {0x17a2, 0x00000620}, > + {0x16a0, 0x00eeffdd}, > + {0x16a6, 0x00071448}, > + {0x16a4, 0x0013132f}, > + {0x16a8, 0x00000000}, > + {0x0ffc, 0x00c0a028}, > + {0x0fe8, 0x0091b06c}, > + {0x0fea, 0x00041600}, > + {0x0f80, 0x00fffaff}, > + {0x0fec, 0x00901809}, > + {0x0ffe, 0x00b01007}, > + {0x16b0, 0x00eeff00}, > + {0x16b2, 0x00007000}, > + {0x16b4, 0x00000814}, > + }; > + unsigned int i; > + u16 reg; > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); > + > + /* all writes below are broadcasted to all PHYs in the same package */ > + reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); > + reg |= SMI_BROADCAST_WR_EN; > + phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg); > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); > + > + reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); > + reg |= BIT(15); > + phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); > + > + for (i = 0; i < ARRAY_SIZE(pre_init1); i++) > + vsc8584_csr_write(phydev, pre_init1[i].reg, pre_init1[i].val); > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); > + > + reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); > + reg &= ~BIT(15); > + phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); > + > + reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); > + reg &= ~SMI_BROADCAST_WR_EN; > + phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg); > + > + return 0; > +} > + > +static u32 vsc85xx_csr_ctrl_phy_read(struct phy_device *phydev, > + u32 target, u32 reg) > +{ > + unsigned long deadline; > + u32 val, val_l, val_h; > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); > + > + /* CSR registers are grouped under different Target IDs. > + * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and > + * MSCC_EXT_PAGE_CSR_CNTL_19 registers. > + * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 > + * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. > + */ > + > + /* Setup the Target ID */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, > + MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); > + > + /* Trigger CSR Action - Read into the CSR's */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, > + MSCC_PHY_CSR_CNTL_19_CMD | MSCC_PHY_CSR_CNTL_19_READ | > + MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | > + MSCC_PHY_CSR_CNTL_19_TARGET(target & 0x3)); > + > + /* Wait for register access*/ > + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); > + do { > + usleep_range(500, 1000); > + val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); > + } while (time_before(jiffies, deadline) && > + !(val & MSCC_PHY_CSR_CNTL_19_CMD)); > + > + if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) > + return 0xffffffff; > + > + /* Read the Least Significant Word (LSW) (17) */ > + val_l = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_17); > + > + /* Read the Most Significant Word (MSW) (18) */ > + val_h = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_18); > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, > + MSCC_PHY_PAGE_STANDARD); > + > + return (val_h << 16) | val_l; > +} > + > +static int vsc85xx_csr_ctrl_phy_write(struct phy_device *phydev, > + u32 target, u32 reg, u32 val) > +{ > + unsigned long deadline; > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); > + > + /* CSR registers are grouped under different Target IDs. > + * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and > + * MSCC_EXT_PAGE_CSR_CNTL_19 registers. > + * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 > + * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. > + */ > + > + /* Setup the Target ID */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, > + MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); > + > + /* Write the Least Significant Word (LSW) (17) */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_17, (u16)val); > + > + /* Write the Most Significant Word (MSW) (18) */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_18, (u16)(val >> 16)); > + > + /* Trigger CSR Action - Write into the CSR's */ > + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, > + MSCC_PHY_CSR_CNTL_19_CMD | > + MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | > + MSCC_PHY_CSR_CNTL_19_TARGET(target & 0x3)); > + > + /* Wait for register access */ > + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); > + do { > + usleep_range(500, 1000); > + val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); > + } while (time_before(jiffies, deadline) && > + !(val & MSCC_PHY_CSR_CNTL_19_CMD)); > + > + if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) > + return -ETIMEDOUT; > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, > + MSCC_PHY_PAGE_STANDARD); > + > + return 0; > +} > + > +static int __phy_write_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb, > + u32 op) > +{ > + unsigned long deadline; > + u32 val; > + int ret; > + > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, reg, > + op | (1 << mcb)); > + if (ret) > + return -EINVAL; > + > + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); > + do { > + usleep_range(500, 1000); > + val = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, reg); > + > + if (val == 0xffffffff) > + return -EINVAL; > + > + } while (time_before(jiffies, deadline) && (val & op)); > + > + if (val & op) > + return -ETIMEDOUT; > + > + return 0; > +} > + > +/* Trigger a read to the spcified MCB */ > +static int phy_update_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) > +{ > + return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_READ); > +} > + > +/* Trigger a write to the spcified MCB */ > +static int phy_commit_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) > +{ > + return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_WRITE); > +} > + > +static int vsc8514_config_init(struct phy_device *phydev) > +{ > + struct vsc8531_private *vsc8531 = phydev->priv; > + unsigned long deadline; > + u16 val, addr; > + int ret, i; > + u32 reg; > + > + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; > + > + mutex_lock(&phydev->mdio.bus->mdio_lock); > + > + __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); > + addr = __phy_read(phydev, > + MSCC_PHY_EXT_PHY_CNTL_4); This should be on one line, same a few lines later. > + addr >>= PHY_CNTL_4_ADDR_POS; > + > + val = __phy_read(phydev, > + MSCC_PHY_ACTIPHY_CNTL); > + > + if (val & PHY_ADDR_REVERSED) > + vsc8531->base_addr = phydev->mdio.addr + addr; > + else > + vsc8531->base_addr = phydev->mdio.addr - addr; > + > + /* Some parts of the init sequence are identical for every PHY in the > + * package. Some parts are modifying the GPIO register bank which is a > + * set of registers that are affecting all PHYs, a few resetting the > + * microprocessor common to all PHYs. > + * All PHYs' interrupts mask register has to be zeroed before enabling > + * any PHY's interrupt in this register. > + * For all these reasons, we need to do the init sequence once and only > + * once whatever is the first PHY in the package that is initialized and > + * do the correct init sequence for all PHYs that are package-critical > + * in this pre-init function. > + */ > + if (!vsc8584_is_pkg_init(phydev, val & PHY_ADDR_REVERSED ? 1 : 0)) { > + if ((phydev->phy_id & phydev->drv->phy_id_mask) == > + (PHY_ID_VSC8514 & phydev->drv->phy_id_mask)) Why do you think this is needed? If this function is used for VSC8514 only, then this is always true. > + ret = vsc8514_config_pre_init(phydev); > + else > + ret = -EINVAL; > + > + if (ret) > + goto err; > + } > + > + vsc8531->pkg_init = true; > + > + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, > + MSCC_PHY_PAGE_EXTENDED_GPIO); > + > + val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); > + > + val &= ~MAC_CFG_MASK; > + val |= MAC_CFG_QSGMII; > + ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); > + > + if (ret) > + goto err; > + > + ret = vsc8584_cmd(phydev, > + PROC_CMD_MCB_ACCESS_MAC_CONF | > + PROC_CMD_RST_CONF_PORT | > + PROC_CMD_READ_MOD_WRITE_PORT | PROC_CMD_QSGMII_MAC); > + if (ret) > + goto err; > + > + /* 6g mcb */ > + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, 0); > + /* lcpll mcb */ > + phy_update_mcb_s6g(phydev, PHY_S6G_LCPLL_CFG, 0); > + /* pll5gcfg0 */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_PLL5G_CFG0, 0x7036f145); > + if (ret) > + goto err; > + > + phy_commit_mcb_s6g(phydev, PHY_S6G_LCPLL_CFG, 0); > + /* pllcfg */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_PLL_CFG, > + (3 << PHY_S6G_PLL_ENA_OFFS_POS) | > + (120 << PHY_S6G_PLL_FSM_CTRL_DATA_POS) > + | (0 << PHY_S6G_PLL_FSM_ENA_POS)); > + if (ret) > + goto err; > + > + /* commoncfg */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_COMMON_CFG, > + (0 << PHY_S6G_SYS_RST_POS) | > + (0 << PHY_S6G_ENA_LANE_POS) | > + (0 << PHY_S6G_ENA_LOOP_POS) | > + (0 << PHY_S6G_QRATE_POS) | > + (3 << PHY_S6G_IF_MODE_POS)); > + if (ret) > + goto err; > + > + /* misccfg */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_MISC_CFG, 1); > + if (ret) > + goto err; > + > + /* gpcfg */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_GPC_CFG, 768); > + if (ret) > + goto err; > + > + phy_commit_mcb_s6g(phydev, 0x3e, 0); > + > + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); > + do { > + usleep_range(500, 1000); > + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, > + 0); /* read 6G MCB into CSRs */ > + reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, > + PHY_S6G_PLL_STATUS); > + if (reg == 0xffffffff) > + goto err; > + > + } while (time_before(jiffies, deadline) && (reg & BIT(12))); > + > + if (reg & BIT(12)) { > + mutex_unlock(&phydev->mdio.bus->mdio_lock); > + return -ETIMEDOUT; > + } > + > + /* misccfg */ > + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, > + PHY_S6G_MISC_CFG, 0); > + if (ret) > + goto err; > + > + phy_commit_mcb_s6g(phydev, PHY_MCB_S6G_CFG, 0); > + > + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); > + do { > + usleep_range(500, 1000); > + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, > + 0); /* read 6G MCB into CSRs */ > + reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, > + PHY_S6G_IB_STATUS0); > + if (reg == 0xffffffff) > + goto err; When jumping to err then value of variable ret is returned. But you don't set ret. In general I would say: Take a little bit more time when preparing a new version and be more careful. Several review comments are related to trivial errors. > + > + } while (time_before(jiffies, deadline) && !(reg & BIT(8))); > + > + if (!(reg & BIT(8))) { > + mutex_unlock(&phydev->mdio.bus->mdio_lock); > + return -ETIMEDOUT; > + } > + > + mutex_unlock(&phydev->mdio.bus->mdio_lock); > + > + ret = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); > + if (ret) > + goto err; > + > + val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); > + val &= ~MEDIA_OP_MODE_MASK; > + val |= MEDIA_OP_MODE_COPPER; > + ret = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, val); I think I mentioned it earlier, phy_modify() can be used here. > + if (ret) > + goto err; > + > + ret = genphy_soft_reset(phydev); > + > + if (ret) > + return ret; > + > + for (i = 0; i < vsc8531->nleds; i++) { > + ret = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]); > + if (ret) > + return ret; > + } > + > + return ret; > + > +err: > + mutex_unlock(&phydev->mdio.bus->mdio_lock); > + return ret; > +} > + > static int vsc85xx_ack_interrupt(struct phy_device *phydev) > { > int rc = 0; > @@ -1791,6 +2213,31 @@ static int vsc85xx_read_status(struct phy_device *phydev) > return genphy_read_status(phydev); > } > > +static int vsc8514_probe(struct phy_device *phydev) > +{ > + struct vsc8531_private *vsc8531; > + u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, > + VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, > + VSC8531_DUPLEX_COLLISION}; > + > + vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL); > + if (!vsc8531) > + return -ENOMEM; > + > + phydev->priv = vsc8531; > + > + vsc8531->nleds = 4; > + vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; > + vsc8531->hw_stats = vsc85xx_hw_stats; > + vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); > + vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, > + sizeof(u64), GFP_KERNEL); > + if (!vsc8531->stats) > + return -ENOMEM; > + > + return vsc85xx_dt_led_modes_get(phydev, default_mode); > +} > + > static int vsc8574_probe(struct phy_device *phydev) > { > struct vsc8531_private *vsc8531; > @@ -1878,6 +2325,29 @@ static int vsc85xx_probe(struct phy_device *phydev) > > /* Microsemi VSC85xx PHYs */ > static struct phy_driver vsc85xx_driver[] = { > +{ > + .phy_id = PHY_ID_VSC8514, > + .name = "Microsemi GE VSC8514 SyncE", > + .phy_id_mask = 0xfffffff0, > + .soft_reset = &genphy_soft_reset, > + .config_init = &vsc8514_config_init, > + .config_aneg = &vsc85xx_config_aneg, > + .read_status = &vsc85xx_read_status, > + .ack_interrupt = &vsc85xx_ack_interrupt, > + .config_intr = &vsc85xx_config_intr, > + .suspend = &genphy_suspend, > + .resume = &genphy_resume, > + .probe = &vsc8514_probe, > + .set_wol = &vsc85xx_wol_set, > + .get_wol = &vsc85xx_wol_get, > + .get_tunable = &vsc85xx_get_tunable, > + .set_tunable = &vsc85xx_set_tunable, > + .read_page = &vsc85xx_phy_read_page, > + .write_page = &vsc85xx_phy_write_page, > + .get_sset_count = &vsc85xx_get_sset_count, > + .get_strings = &vsc85xx_get_strings, > + .get_stats = &vsc85xx_get_stats, > +}, > { > .phy_id = PHY_ID_VSC8530, > .name = "Microsemi FE VSC8530", > @@ -2034,6 +2504,7 @@ static struct phy_driver vsc85xx_driver[] = { > module_phy_driver(vsc85xx_driver); > > static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { > + { PHY_ID_VSC8514, 0xfffffff0, }, > { PHY_ID_VSC8530, 0xfffffff0, }, > { PHY_ID_VSC8531, 0xfffffff0, }, > { PHY_ID_VSC8540, 0xfffffff0, }, >
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 520657945b82..89085e87ecab 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -397,7 +397,7 @@ config MICROCHIP_T1_PHY config MICROSEMI_PHY tristate "Microsemi PHYs" ---help--- - Currently supports VSC8530, VSC8531, VSC8540 and VSC8541 PHYs + Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index db50efb30df5..e71fef0f788a 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -85,12 +85,48 @@ enum rgmii_rx_clock_delay { #define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x)) #define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x)) +#define MSCC_EXT_PAGE_CSR_CNTL_17 17 +#define MSCC_EXT_PAGE_CSR_CNTL_18 18 + +#define MSCC_EXT_PAGE_CSR_CNTL_19 19 +#define MSCC_PHY_CSR_CNTL_19_REG_ADDR(x) (x) +#define MSCC_PHY_CSR_CNTL_19_TARGET(x) ((x) << 12) +#define MSCC_PHY_CSR_CNTL_19_READ BIT(14) +#define MSCC_PHY_CSR_CNTL_19_CMD BIT(15) + +#define MSCC_EXT_PAGE_CSR_CNTL_20 20 +#define MSCC_PHY_CSR_CNTL_20_TARGET(x) (x) + +#define PHY_MCB_TARGET 0x07 +#define PHY_MCB_S6G_WRITE BIT(31) +#define PHY_MCB_S6G_READ BIT(30) + +#define PHY_MCB_S6G_CFG 0x3f +#define PHY_S6G_LCPLL_CFG 0x11 +#define PHY_S6G_PLL5G_CFG0 0x06 +#define PHY_S6G_PLL_CFG 0x2b +#define PHY_S6G_PLL_STATUS 0x31 +#define PHY_S6G_COMMON_CFG 0x2c +#define PHY_S6G_MISC_CFG 0x3b +#define PHY_S6G_GPC_CFG 0x2e +#define PHY_S6G_IB_STATUS0 0x2f + +#define PHY_S6G_SYS_RST_POS 31 +#define PHY_S6G_ENA_LANE_POS 18 +#define PHY_S6G_ENA_LOOP_POS 8 +#define PHY_S6G_QRATE_POS 6 +#define PHY_S6G_IF_MODE_POS 4 +#define PHY_S6G_PLL_ENA_OFFS_POS 21 +#define PHY_S6G_PLL_FSM_CTRL_DATA_POS 8 +#define PHY_S6G_PLL_FSM_ENA_POS 7 + #define MSCC_EXT_PAGE_ACCESS 31 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */ #define MSCC_PHY_PAGE_EXTENDED_3 0x0003 /* Extended reg - page 3 */ #define MSCC_PHY_PAGE_EXTENDED_4 0x0004 /* Extended reg - page 4 */ +#define MSCC_PHY_PAGE_CSR_CNTL MSCC_PHY_PAGE_EXTENDED_4 /* Extended reg - GPIO; this is a bank of registers that are shared for all PHYs * in the same package. */ @@ -216,6 +252,7 @@ enum rgmii_rx_clock_delay { #define MSCC_PHY_TR_MSB 18 /* Microsemi PHY ID's */ +#define PHY_ID_VSC8514 0x00070670 #define PHY_ID_VSC8530 0x00070560 #define PHY_ID_VSC8531 0x00070570 #define PHY_ID_VSC8540 0x00070760 @@ -1742,6 +1779,391 @@ static int vsc8584_did_interrupt(struct phy_device *phydev) return (rc < 0) ? 0 : rc & MII_VSC85XX_INT_MASK_MASK; } +static int vsc8514_config_pre_init(struct phy_device *phydev) +{ + /* These are the settings to override the silicon default + * values to handle hardware performance of PHY. They + * are set at Power-On state and remain until PHY Reset. + */ + const struct reg_val pre_init1[] = { + {0x0f90, 0x00688980}, + {0x0786, 0x00000003}, + {0x07fa, 0x0050100f}, + {0x0f82, 0x0012b002}, + {0x1686, 0x00000004}, + {0x168c, 0x00d2c46f}, + {0x17a2, 0x00000620}, + {0x16a0, 0x00eeffdd}, + {0x16a6, 0x00071448}, + {0x16a4, 0x0013132f}, + {0x16a8, 0x00000000}, + {0x0ffc, 0x00c0a028}, + {0x0fe8, 0x0091b06c}, + {0x0fea, 0x00041600}, + {0x0f80, 0x00fffaff}, + {0x0fec, 0x00901809}, + {0x0ffe, 0x00b01007}, + {0x16b0, 0x00eeff00}, + {0x16b2, 0x00007000}, + {0x16b4, 0x00000814}, + }; + unsigned int i; + u16 reg; + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + + /* all writes below are broadcasted to all PHYs in the same package */ + reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); + reg |= SMI_BROADCAST_WR_EN; + phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg); + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); + + reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); + reg |= BIT(15); + phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); + + for (i = 0; i < ARRAY_SIZE(pre_init1); i++) + vsc8584_csr_write(phydev, pre_init1[i].reg, pre_init1[i].val); + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); + + reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); + reg &= ~BIT(15); + phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + + reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); + reg &= ~SMI_BROADCAST_WR_EN; + phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg); + + return 0; +} + +static u32 vsc85xx_csr_ctrl_phy_read(struct phy_device *phydev, + u32 target, u32 reg) +{ + unsigned long deadline; + u32 val, val_l, val_h; + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); + + /* CSR registers are grouped under different Target IDs. + * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and + * MSCC_EXT_PAGE_CSR_CNTL_19 registers. + * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 + * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. + */ + + /* Setup the Target ID */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, + MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); + + /* Trigger CSR Action - Read into the CSR's */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, + MSCC_PHY_CSR_CNTL_19_CMD | MSCC_PHY_CSR_CNTL_19_READ | + MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | + MSCC_PHY_CSR_CNTL_19_TARGET(target & 0x3)); + + /* Wait for register access*/ + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + usleep_range(500, 1000); + val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); + } while (time_before(jiffies, deadline) && + !(val & MSCC_PHY_CSR_CNTL_19_CMD)); + + if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) + return 0xffffffff; + + /* Read the Least Significant Word (LSW) (17) */ + val_l = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_17); + + /* Read the Most Significant Word (MSW) (18) */ + val_h = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_18); + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + + return (val_h << 16) | val_l; +} + +static int vsc85xx_csr_ctrl_phy_write(struct phy_device *phydev, + u32 target, u32 reg, u32 val) +{ + unsigned long deadline; + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); + + /* CSR registers are grouped under different Target IDs. + * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and + * MSCC_EXT_PAGE_CSR_CNTL_19 registers. + * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 + * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. + */ + + /* Setup the Target ID */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, + MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); + + /* Write the Least Significant Word (LSW) (17) */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_17, (u16)val); + + /* Write the Most Significant Word (MSW) (18) */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_18, (u16)(val >> 16)); + + /* Trigger CSR Action - Write into the CSR's */ + phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, + MSCC_PHY_CSR_CNTL_19_CMD | + MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | + MSCC_PHY_CSR_CNTL_19_TARGET(target & 0x3)); + + /* Wait for register access */ + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + usleep_range(500, 1000); + val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); + } while (time_before(jiffies, deadline) && + !(val & MSCC_PHY_CSR_CNTL_19_CMD)); + + if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) + return -ETIMEDOUT; + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + + return 0; +} + +static int __phy_write_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb, + u32 op) +{ + unsigned long deadline; + u32 val; + int ret; + + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, reg, + op | (1 << mcb)); + if (ret) + return -EINVAL; + + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + usleep_range(500, 1000); + val = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, reg); + + if (val == 0xffffffff) + return -EINVAL; + + } while (time_before(jiffies, deadline) && (val & op)); + + if (val & op) + return -ETIMEDOUT; + + return 0; +} + +/* Trigger a read to the spcified MCB */ +static int phy_update_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) +{ + return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_READ); +} + +/* Trigger a write to the spcified MCB */ +static int phy_commit_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) +{ + return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_WRITE); +} + +static int vsc8514_config_init(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + unsigned long deadline; + u16 val, addr; + int ret, i; + u32 reg; + + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + + mutex_lock(&phydev->mdio.bus->mdio_lock); + + __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); + addr = __phy_read(phydev, + MSCC_PHY_EXT_PHY_CNTL_4); + addr >>= PHY_CNTL_4_ADDR_POS; + + val = __phy_read(phydev, + MSCC_PHY_ACTIPHY_CNTL); + + if (val & PHY_ADDR_REVERSED) + vsc8531->base_addr = phydev->mdio.addr + addr; + else + vsc8531->base_addr = phydev->mdio.addr - addr; + + /* Some parts of the init sequence are identical for every PHY in the + * package. Some parts are modifying the GPIO register bank which is a + * set of registers that are affecting all PHYs, a few resetting the + * microprocessor common to all PHYs. + * All PHYs' interrupts mask register has to be zeroed before enabling + * any PHY's interrupt in this register. + * For all these reasons, we need to do the init sequence once and only + * once whatever is the first PHY in the package that is initialized and + * do the correct init sequence for all PHYs that are package-critical + * in this pre-init function. + */ + if (!vsc8584_is_pkg_init(phydev, val & PHY_ADDR_REVERSED ? 1 : 0)) { + if ((phydev->phy_id & phydev->drv->phy_id_mask) == + (PHY_ID_VSC8514 & phydev->drv->phy_id_mask)) + ret = vsc8514_config_pre_init(phydev); + else + ret = -EINVAL; + + if (ret) + goto err; + } + + vsc8531->pkg_init = true; + + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXTENDED_GPIO); + + val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); + + val &= ~MAC_CFG_MASK; + val |= MAC_CFG_QSGMII; + ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); + + if (ret) + goto err; + + ret = vsc8584_cmd(phydev, + PROC_CMD_MCB_ACCESS_MAC_CONF | + PROC_CMD_RST_CONF_PORT | + PROC_CMD_READ_MOD_WRITE_PORT | PROC_CMD_QSGMII_MAC); + if (ret) + goto err; + + /* 6g mcb */ + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, 0); + /* lcpll mcb */ + phy_update_mcb_s6g(phydev, PHY_S6G_LCPLL_CFG, 0); + /* pll5gcfg0 */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_PLL5G_CFG0, 0x7036f145); + if (ret) + goto err; + + phy_commit_mcb_s6g(phydev, PHY_S6G_LCPLL_CFG, 0); + /* pllcfg */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_PLL_CFG, + (3 << PHY_S6G_PLL_ENA_OFFS_POS) | + (120 << PHY_S6G_PLL_FSM_CTRL_DATA_POS) + | (0 << PHY_S6G_PLL_FSM_ENA_POS)); + if (ret) + goto err; + + /* commoncfg */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_COMMON_CFG, + (0 << PHY_S6G_SYS_RST_POS) | + (0 << PHY_S6G_ENA_LANE_POS) | + (0 << PHY_S6G_ENA_LOOP_POS) | + (0 << PHY_S6G_QRATE_POS) | + (3 << PHY_S6G_IF_MODE_POS)); + if (ret) + goto err; + + /* misccfg */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_MISC_CFG, 1); + if (ret) + goto err; + + /* gpcfg */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_GPC_CFG, 768); + if (ret) + goto err; + + phy_commit_mcb_s6g(phydev, 0x3e, 0); + + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + usleep_range(500, 1000); + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, + 0); /* read 6G MCB into CSRs */ + reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, + PHY_S6G_PLL_STATUS); + if (reg == 0xffffffff) + goto err; + + } while (time_before(jiffies, deadline) && (reg & BIT(12))); + + if (reg & BIT(12)) { + mutex_unlock(&phydev->mdio.bus->mdio_lock); + return -ETIMEDOUT; + } + + /* misccfg */ + ret = vsc85xx_csr_ctrl_phy_write(phydev, PHY_MCB_TARGET, + PHY_S6G_MISC_CFG, 0); + if (ret) + goto err; + + phy_commit_mcb_s6g(phydev, PHY_MCB_S6G_CFG, 0); + + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + usleep_range(500, 1000); + phy_update_mcb_s6g(phydev, PHY_MCB_S6G_CFG, + 0); /* read 6G MCB into CSRs */ + reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, + PHY_S6G_IB_STATUS0); + if (reg == 0xffffffff) + goto err; + + } while (time_before(jiffies, deadline) && !(reg & BIT(8))); + + if (!(reg & BIT(8))) { + mutex_unlock(&phydev->mdio.bus->mdio_lock); + return -ETIMEDOUT; + } + + mutex_unlock(&phydev->mdio.bus->mdio_lock); + + ret = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + if (ret) + goto err; + + val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); + val &= ~MEDIA_OP_MODE_MASK; + val |= MEDIA_OP_MODE_COPPER; + ret = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, val); + if (ret) + goto err; + + ret = genphy_soft_reset(phydev); + + if (ret) + return ret; + + for (i = 0; i < vsc8531->nleds; i++) { + ret = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]); + if (ret) + return ret; + } + + return ret; + +err: + mutex_unlock(&phydev->mdio.bus->mdio_lock); + return ret; +} + static int vsc85xx_ack_interrupt(struct phy_device *phydev) { int rc = 0; @@ -1791,6 +2213,31 @@ static int vsc85xx_read_status(struct phy_device *phydev) return genphy_read_status(phydev); } +static int vsc8514_probe(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531; + u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, + VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, + VSC8531_DUPLEX_COLLISION}; + + vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL); + if (!vsc8531) + return -ENOMEM; + + phydev->priv = vsc8531; + + vsc8531->nleds = 4; + vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; + vsc8531->hw_stats = vsc85xx_hw_stats; + vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); + vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, + sizeof(u64), GFP_KERNEL); + if (!vsc8531->stats) + return -ENOMEM; + + return vsc85xx_dt_led_modes_get(phydev, default_mode); +} + static int vsc8574_probe(struct phy_device *phydev) { struct vsc8531_private *vsc8531; @@ -1878,6 +2325,29 @@ static int vsc85xx_probe(struct phy_device *phydev) /* Microsemi VSC85xx PHYs */ static struct phy_driver vsc85xx_driver[] = { +{ + .phy_id = PHY_ID_VSC8514, + .name = "Microsemi GE VSC8514 SyncE", + .phy_id_mask = 0xfffffff0, + .soft_reset = &genphy_soft_reset, + .config_init = &vsc8514_config_init, + .config_aneg = &vsc85xx_config_aneg, + .read_status = &vsc85xx_read_status, + .ack_interrupt = &vsc85xx_ack_interrupt, + .config_intr = &vsc85xx_config_intr, + .suspend = &genphy_suspend, + .resume = &genphy_resume, + .probe = &vsc8514_probe, + .set_wol = &vsc85xx_wol_set, + .get_wol = &vsc85xx_wol_get, + .get_tunable = &vsc85xx_get_tunable, + .set_tunable = &vsc85xx_set_tunable, + .read_page = &vsc85xx_phy_read_page, + .write_page = &vsc85xx_phy_write_page, + .get_sset_count = &vsc85xx_get_sset_count, + .get_strings = &vsc85xx_get_strings, + .get_stats = &vsc85xx_get_stats, +}, { .phy_id = PHY_ID_VSC8530, .name = "Microsemi FE VSC8530", @@ -2034,6 +2504,7 @@ static struct phy_driver vsc85xx_driver[] = { module_phy_driver(vsc85xx_driver); static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { + { PHY_ID_VSC8514, 0xfffffff0, }, { PHY_ID_VSC8530, 0xfffffff0, }, { PHY_ID_VSC8531, 0xfffffff0, }, { PHY_ID_VSC8540, 0xfffffff0, },