Message ID | 20240727092031.1108690-1-Frank.Sae@motor-comm.com (mailing list archive) |
---|---|
State | Deferred |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | net: phy: Add driver for Motorcomm yt8821 2.5G ethernet phy | expand |
On Sat, Jul 27, 2024 at 02:20:31AM -0700, Frank.Sae wrote: > Add a driver for the motorcomm yt8821 2.5G ethernet phy. > Verified the driver on > BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board, > which is developed by Guangdong Bipai Technology Co., Ltd.. > On the board, yt8821 2.5G ethernet phy works in > AUTO_BX2500_SGMII or FORCE_BX2500 interface, > supports 2.5G/1000M/100M/10M speeds, and wol(magic package). > Since some functions of yt8821 are similar to YT8521 > so some functions for yt8821 can be reused. No leading space please. > > Signed-off-by: Frank.Sae <Frank.Sae@motor-comm.com> > --- > drivers/net/phy/motorcomm.c | 639 +++++++++++++++++++++++++++++++++++- > 1 file changed, 636 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c > index 7a11fdb687cc..a432b27dd849 100644 > --- a/drivers/net/phy/motorcomm.c > +++ b/drivers/net/phy/motorcomm.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0+ > /* > - * Motorcomm 8511/8521/8531/8531S PHY driver. > + * Motorcomm 8511/8521/8531/8531S/8821 PHY driver. > * > * Author: Peter Geis <pgwipeout@gmail.com> > * Author: Frank <Frank.Sae@motor-comm.com> > @@ -16,7 +16,7 @@ > #define PHY_ID_YT8521 0x0000011a > #define PHY_ID_YT8531 0x4f51e91b > #define PHY_ID_YT8531S 0x4f51e91a > - > +#define PHY_ID_YT8821 0x4f51ea19 > /* YT8521/YT8531S Register Overview > * UTP Register space | FIBER Register space > * ------------------------------------------------------------ > @@ -52,6 +52,15 @@ > #define YTPHY_SSR_SPEED_10M 0x0 > #define YTPHY_SSR_SPEED_100M 0x1 > #define YTPHY_SSR_SPEED_1000M 0x2 > +/* bit9 as speed_mode[2], bit15:14 as Speed_mode[1:0] > + * Speed_mode[2:0]: > + * 100 = 2P5G > + * 011 = 10G > + * 010 = 1000 Mbps > + * 001 = 100 Mbps > + * 000 = 10 Mbps > + */ > +#define YT8821_SSR_SPEED_2500M 0x4 If these bits are spread around, why 0x4? Ahh, because you extract the bits and reform the value. Maybe: #define YTPHY_SSR_SPEED_10M (0x0 << 14) #define YTPHY_SSR_SPEED_100M (0x1 << 14) #define YTPHY_SSR_SPEED_1000M (0x2 << 14) #define YTPHY_SSR_SPEED_10G (0x3 << 14) #define YT8821_SSR_SPEED_2500M (0x0 << 14) | BIT(9) #define YTPHY_SSR_SPEED_MASK (0x3 << 14) | BIT(9) > +#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 > +#define YT8821_SDS_EXT_CSR_PLL_SETTING 0x8605 > +#define YT8821_UTP_EXT_FFE_IPR_CTRL_REG 0x34E > +#define YT8821_UTP_EXT_FFE_SETTING 0x8080 > +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 > +#define YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING 0x5200 > +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 > +#define YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING 0x5200 > +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 > +#define YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING 0x5A3C > +#define YT8821_UTP_EXT_IPR_CTRL_REG 0x374 > +#define YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING 0x7C6C > +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 > +#define YT8821_UTP_EXT_ECHO_SETTING 0xAA0A > +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 > +#define YT8821_UTP_EXT_AGC_MED_GAIN_SETTING 0x3022 > +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A > +#define YT8821_UTP_EXT_TH_20DB_2500_SETTING 0x8000 > +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 > +#define YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING 0x7711 > +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 > +#define YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING 0x2211 > +#define YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG 0x56 > +#define YT8821_UTP_EXT_ANALOG_CFG7_RESET 0x20 > +#define YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE 0x3F > +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 > +#define YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING 0x380C > +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 > +#define YT8821_UTP_EXT_TXGE_NFR_FR_SETTING 0x112A > +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 > +#define YT8821_UTP_EXT_PLL_SPARE_SETTING 0xE9 > +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG 0x466 > +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING 0x6464 > +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG 0x467 > +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING 0x6464 > +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG 0x468 > +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING 0x6464 > +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG 0x469 > +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING 0x6464 All these _SETTING are magic numbers. Can you document any of them? > +/** > + * yt8821_probe() - read dts to get chip mode > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code kerneldoc requires a : after returns. > + */ > +static int yt8821_probe(struct phy_device *phydev) > +{ > + struct device_node *node = phydev->mdio.dev.of_node; > + struct device *dev = &phydev->mdio.dev; > + struct yt8821_priv *priv; > + u8 chip_mode; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + phydev->priv = priv; > + > + if (of_property_read_u8(node, "motorcomm,chip-mode", &chip_mode)) > + chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; > + > + switch (chip_mode) { > + case YT8821_CHIP_MODE_AUTO_BX2500_SGMII: > + priv->chip_mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII; > + break; > + case YT8821_CHIP_MODE_FORCE_BX2500: > + priv->chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; > + break; > + default: > + phydev_warn(phydev, "chip_mode err:%d\n", chip_mode); > + return -EINVAL; Didn't the binding say it defaults to forced? Yet here it gives an error? > + * yt8821_get_rate_matching - read register to get phy chip mode Why? You have it in priv? > +/** > + * yt8821gen_init_paged() - generic initialization according to page > + * @phydev: a pointer to a &struct phy_device > + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to > + * operate. > + * > + * returns 0 or negative errno code > + */ > +static int yt8821gen_init_paged(struct phy_device *phydev, int page) > +{ > + int old_page; > + int ret = 0; > + > + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); > + if (old_page < 0) > + goto err_restore_page; > + > + if (page & YT8521_RSSR_SPACE_MASK) { > + /* sds init */ > + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); > + if (ret < 0) > + goto err_restore_page; > + > + ret = ytphy_write_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, > + YT8821_SDS_EXT_CSR_PLL_SETTING); > + if (ret < 0) > + goto err_restore_page; > + } else { > + /* utp init */ > + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_FFE_IPR_CTRL_REG, > + YT8821_UTP_EXT_FFE_SETTING); > + if (ret < 0) > + goto err_restore_page; > + ... > + } > + > +err_restore_page: > + return phy_restore_page(phydev, old_page, ret); > +} > + > +/** > + * yt8821gen_init() - generic initialization > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8821gen_init(struct phy_device *phydev) > +{ > + int ret = 0; > + > + ret = yt8821gen_init_paged(phydev, YT8521_RSSR_FIBER_SPACE); > + if (ret < 0) > + return ret; > + > + return yt8821gen_init_paged(phydev, YT8521_RSSR_UTP_SPACE); That is odd. Why not have two functions, rather than one with a parameter. You get better functions names then, making it clearer what each function is doing. > +} > + > +/** > + * yt8821_auto_sleep_config() - phy auto sleep config > + * @phydev: a pointer to a &struct phy_device > + * @enable: true enable auto sleep, false disable auto sleep > + * > + * returns 0 or negative errno code > + */ > +static int yt8821_auto_sleep_config(struct phy_device *phydev, bool enable) > +{ > + int old_page; > + int ret = 0; > + > + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (old_page < 0) > + goto err_restore_page; > + > + ret = ytphy_modify_ext(phydev, > + YT8521_EXTREG_SLEEP_CONTROL1_REG, > + YT8521_ESC1R_SLEEP_SW, > + enable ? 1 : 0); So each page has its own extension registers? > + if (ret < 0) > + goto err_restore_page; > + > +err_restore_page: > + return phy_restore_page(phydev, old_page, ret); > +} > + > +/** > + * yt8821_config_init() - phy initializatioin > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8821_config_init(struct phy_device *phydev) > +{ > + struct yt8821_priv *priv = phydev->priv; > + int ret, val; > + > + phydev->irq = PHY_POLL; Why do this? > + > + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); > + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { > + ret = ytphy_modify_ext_with_lock(phydev, > + YT8521_CHIP_CONFIG_REG, > + YT8521_CCR_MODE_SEL_MASK, > + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); > + if (ret < 0) > + return ret; > + > + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, > + phydev->possible_interfaces, > + true); > + __assign_bit(PHY_INTERFACE_MODE_SGMII, > + phydev->possible_interfaces, > + true); > + > + phydev->rate_matching = RATE_MATCH_NONE; > + } else if (priv->chip_mode == YT8821_CHIP_MODE_FORCE_BX2500) { > + ret = ytphy_modify_ext_with_lock(phydev, > + YT8521_CHIP_CONFIG_REG, > + YT8521_CCR_MODE_SEL_MASK, > + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 1)); > + if (ret < 0) > + return ret; > + > + phydev->rate_matching = RATE_MATCH_PAUSE; > + } The idea of this phydev->possible_interfaces is to allow the core to figure out what mode is most appropriate. So i would drop the mode in DT, default to auto, and let the core tell you it wants 2500 BaseX if that is all the MAC can do. > +static int yt8821_read_status(struct phy_device *phydev) > +{ > + struct yt8821_priv *priv = phydev->priv; > + int old_page; > + int ret = 0; > + int link; > + int val; > + > + if (phydev->autoneg == AUTONEG_ENABLE) { > + int lpadv = phy_read_mmd(phydev, > + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); > + > + if (lpadv < 0) > + return lpadv; > + > + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, > + lpadv); > + } > + > + ret = ytphy_write_ext_with_lock(phydev, > + YT8521_REG_SPACE_SELECT_REG, > + YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + ret = genphy_read_status(phydev); > + if (ret < 0) > + return ret; > + > + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (old_page < 0) > + goto err_restore_page; > + > + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (val < 0) { > + ret = val; > + goto err_restore_page; > + } > + > + link = val & YTPHY_SSR_LINK; > + if (link) > + yt8821_adjust_status(phydev, val); > + > + if (link) { > + if (phydev->link == 0) > + phydev_info(phydev, > + "%s, phy addr: %d, link up, mii reg 0x%x = 0x%x\n", > + __func__, phydev->mdio.addr, > + YTPHY_SPECIFIC_STATUS_REG, > + (unsigned int)val); phydev_dbg()? > + phydev->link = 1; > + } else { > + if (phydev->link == 1) > + phydev_info(phydev, "%s, phy addr: %d, link down\n", > + __func__, phydev->mdio.addr); phydev_dbg()? Andrew
On Sat, Jul 27, 2024 at 02:20:31AM -0700, Frank.Sae wrote: > Add a driver for the motorcomm yt8821 2.5G ethernet phy. > Verified the driver on > BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board, > which is developed by Guangdong Bipai Technology Co., Ltd.. > On the board, yt8821 2.5G ethernet phy works in > AUTO_BX2500_SGMII or FORCE_BX2500 interface, > supports 2.5G/1000M/100M/10M speeds, and wol(magic package). > Since some functions of yt8821 are similar to YT8521 > so some functions for yt8821 can be reused. > > Signed-off-by: Frank.Sae <Frank.Sae@motor-comm.com> Hi Frank, This is not a full review. And setting up expectations, as per the form letter below, net-next is currently closed. But nonetheless I've provided some minor review below. ## Form letter - net-next-closed (Adapted from text by Jakub) The merge window for v6.11 has begun and therefore net-next is closed for new drivers, features, code refactoring and optimizations. We are currently accepting bug fixes only. Please repost when net-next reopens after 29th July. RFC patches sent for review only are welcome at any time. See: https://www.kernel.org/doc/html/next/process/maintainer-netdev.html#development-cycle pw-bot: defer > --- > drivers/net/phy/motorcomm.c | 639 +++++++++++++++++++++++++++++++++++- > 1 file changed, 636 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c ... > +/** > + * yt8821_probe() - read dts to get chip mode > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ nit: please document return values using a "Return:" or "Returns:" section. Flagged by W=1 allmodconfig builds and ./scripts/kernel-doc -none -Warn ... > +/** > + * yt8821_config_init() - phy initializatioin > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8821_config_init(struct phy_device *phydev) > +{ > + struct yt8821_priv *priv = phydev->priv; > + int ret, val; > + > + phydev->irq = PHY_POLL; > + > + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); val is set here but otherwise unused. Should val be checked for an error here? Flagged by W=1 builds. > + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { > + ret = ytphy_modify_ext_with_lock(phydev, > + YT8521_CHIP_CONFIG_REG, > + YT8521_CCR_MODE_SEL_MASK, > + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); > + if (ret < 0) > + return ret; > + > + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, > + phydev->possible_interfaces, > + true); > + __assign_bit(PHY_INTERFACE_MODE_SGMII, > + phydev->possible_interfaces, > + true); > + > + phydev->rate_matching = RATE_MATCH_NONE; > + } else if (priv->chip_mode == YT8821_CHIP_MODE_FORCE_BX2500) { > + ret = ytphy_modify_ext_with_lock(phydev, > + YT8521_CHIP_CONFIG_REG, > + YT8521_CCR_MODE_SEL_MASK, > + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 1)); > + if (ret < 0) > + return ret; > + > + phydev->rate_matching = RATE_MATCH_PAUSE; > + } > + > + ret = yt8821gen_init(phydev); > + if (ret < 0) > + return ret; > + > + /* disable auto sleep */ > + ret = yt8821_auto_sleep_config(phydev, false); > + if (ret < 0) > + return ret; > + > + /* soft reset */ > + yt8821_soft_reset(phydev); > + > + return ret; > +} > + > +/** > + * yt8821_adjust_status() - update speed and duplex to phydev > + * @phydev: a pointer to a &struct phy_device > + * @val: read from YTPHY_SPECIFIC_STATUS_REG > + */ > +static void yt8821_adjust_status(struct phy_device *phydev, int val) > +{ > + int speed_mode, duplex; > + int speed_mode_low, speed_mode_high; > + int speed = SPEED_UNKNOWN; nit: Please consider arranging local variables in reverse xmas tree order - longest line to shortest. This can be helpful: https://github.com/ecree-solarflare/xmastree > + > + duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val); > + > + speed_mode_low = FIELD_GET(GENMASK(15, 14), val); > + speed_mode_high = FIELD_GET(BIT(9), val); > + speed_mode = FIELD_PREP(BIT(2), speed_mode_high) | > + FIELD_PREP(GENMASK(1, 0), speed_mode_low); > + switch (speed_mode) { > + case YTPHY_SSR_SPEED_10M: > + speed = SPEED_10; > + break; > + case YTPHY_SSR_SPEED_100M: > + speed = SPEED_100; > + break; > + case YTPHY_SSR_SPEED_1000M: > + speed = SPEED_1000; > + break; > + case YT8821_SSR_SPEED_2500M: > + speed = SPEED_2500; > + break; > + default: > + speed = SPEED_UNKNOWN; > + break; > + } > + > + phydev->speed = speed; > + phydev->duplex = duplex; > +} ...
On Sat, Jul 27, 2024 at 02:20:31AM -0700, Frank.Sae wrote: > +/** > + * yt8821_config_init() - phy initializatioin > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8821_config_init(struct phy_device *phydev) > +{ > + struct yt8821_priv *priv = phydev->priv; > + int ret, val; > + > + phydev->irq = PHY_POLL; > + > + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); > + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { > + ret = ytphy_modify_ext_with_lock(phydev, > + YT8521_CHIP_CONFIG_REG, > + YT8521_CCR_MODE_SEL_MASK, > + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); > + if (ret < 0) > + return ret; > + > + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, > + phydev->possible_interfaces, > + true); > + __assign_bit(PHY_INTERFACE_MODE_SGMII, > + phydev->possible_interfaces, > + true); Before each and every call to .config_init, phydev->possible_interfaces will be cleared. So, please use __set_bit() here. > +static int yt8821_read_status(struct phy_device *phydev) > +{ > + struct yt8821_priv *priv = phydev->priv; > + int old_page; > + int ret = 0; > + int link; > + int val; > + > + if (phydev->autoneg == AUTONEG_ENABLE) { > + int lpadv = phy_read_mmd(phydev, > + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); > + > + if (lpadv < 0) > + return lpadv; > + > + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, > + lpadv); > + } > + > + ret = ytphy_write_ext_with_lock(phydev, > + YT8521_REG_SPACE_SELECT_REG, > + YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + ret = genphy_read_status(phydev); > + if (ret < 0) > + return ret; > + > + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (old_page < 0) > + goto err_restore_page; > + > + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (val < 0) { > + ret = val; > + goto err_restore_page; > + } > + > + link = val & YTPHY_SSR_LINK; What link status is this reporting? For interface switching to work, phydev->link must _only_ indicate whether the _media_ side interface is up or down. It must _not_ include the status of the MAC facing interface from the PHY. Why? The interface configuration of the MAC is only performed when the _media_ link comes up, denoted by phydev->link becoming true. If the MAC interface configuration mismatches the PHY interface configuration, then the MAC facing interface of the PHY will remain down, and if phydev->link is forced to false, then the link will never come up. So, I hope that this isn't testing the MAC facing interface status of the PHY!
On Sat, Jul 27, 2024 at 01:36:25PM +0200, Andrew Lunn wrote: > The idea of this phydev->possible_interfaces is to allow the core to > figure out what mode is most appropriate. So i would drop the mode in > DT, default to auto, and let the core tell you it wants 2500 BaseX if > that is all the MAC can do. phydev->possible_interfaces reports the bitmap of interfaces that the PHY _will_ switch between on its MAC facing side depending on the negotiated media-side link. It would be nice if this driver always filled in phydev->possible_interfaces, even if there is only one interface when it's using rate matching.
On 7/27/24 04:36, Andrew Lunn wrote: > On Sat, Jul 27, 2024 at 02:20:31AM -0700, Frank.Sae wrote: >> Add a driver for the motorcomm yt8821 2.5G ethernet phy. >> Verified the driver on >> BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board, >> which is developed by Guangdong Bipai Technology Co., Ltd.. >> On the board, yt8821 2.5G ethernet phy works in >> AUTO_BX2500_SGMII or FORCE_BX2500 interface, >> supports 2.5G/1000M/100M/10M speeds, and wol(magic package). >> Since some functions of yt8821 are similar to YT8521 >> so some functions for yt8821 can be reused. > No leading space please. > >> Signed-off-by: Frank.Sae <Frank.Sae@motor-comm.com> >> --- >> drivers/net/phy/motorcomm.c | 639 +++++++++++++++++++++++++++++++++++- >> 1 file changed, 636 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c >> index 7a11fdb687cc..a432b27dd849 100644 >> --- a/drivers/net/phy/motorcomm.c >> +++ b/drivers/net/phy/motorcomm.c >> @@ -1,6 +1,6 @@ >> // SPDX-License-Identifier: GPL-2.0+ >> /* >> - * Motorcomm 8511/8521/8531/8531S PHY driver. >> + * Motorcomm 8511/8521/8531/8531S/8821 PHY driver. >> * >> * Author: Peter Geis <pgwipeout@gmail.com> >> * Author: Frank <Frank.Sae@motor-comm.com> >> @@ -16,7 +16,7 @@ >> #define PHY_ID_YT8521 0x0000011a >> #define PHY_ID_YT8531 0x4f51e91b >> #define PHY_ID_YT8531S 0x4f51e91a >> - >> +#define PHY_ID_YT8821 0x4f51ea19 >> /* YT8521/YT8531S Register Overview >> * UTP Register space | FIBER Register space >> * ------------------------------------------------------------ >> @@ -52,6 +52,15 @@ >> #define YTPHY_SSR_SPEED_10M 0x0 >> #define YTPHY_SSR_SPEED_100M 0x1 >> #define YTPHY_SSR_SPEED_1000M 0x2 >> +/* bit9 as speed_mode[2], bit15:14 as Speed_mode[1:0] >> + * Speed_mode[2:0]: >> + * 100 = 2P5G >> + * 011 = 10G >> + * 010 = 1000 Mbps >> + * 001 = 100 Mbps >> + * 000 = 10 Mbps >> + */ >> +#define YT8821_SSR_SPEED_2500M 0x4 > If these bits are spread around, why 0x4? Ahh, because you extract the > bits and reform the value. Maybe: > > #define YTPHY_SSR_SPEED_10M (0x0 << 14) > #define YTPHY_SSR_SPEED_100M (0x1 << 14) > #define YTPHY_SSR_SPEED_1000M (0x2 << 14) > #define YTPHY_SSR_SPEED_10G (0x3 << 14) > #define YT8821_SSR_SPEED_2500M (0x0 << 14) | BIT(9) > #define YTPHY_SSR_SPEED_MASK (0x3 << 14) | BIT(9) > please help to confirm it is ok? #define YT8821_SSR_SPEED_2500M ((BIT(9) >> 9) << 2) | ((0x0 << 14) >> 14) >> +#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 >> +#define YT8821_SDS_EXT_CSR_PLL_SETTING 0x8605 >> +#define YT8821_UTP_EXT_FFE_IPR_CTRL_REG 0x34E >> +#define YT8821_UTP_EXT_FFE_SETTING 0x8080 >> +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 >> +#define YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING 0x5200 >> +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 >> +#define YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING 0x5200 >> +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 >> +#define YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING 0x5A3C >> +#define YT8821_UTP_EXT_IPR_CTRL_REG 0x374 >> +#define YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING 0x7C6C >> +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 >> +#define YT8821_UTP_EXT_ECHO_SETTING 0xAA0A >> +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 >> +#define YT8821_UTP_EXT_AGC_MED_GAIN_SETTING 0x3022 >> +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A >> +#define YT8821_UTP_EXT_TH_20DB_2500_SETTING 0x8000 >> +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 >> +#define YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING 0x7711 >> +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 >> +#define YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING 0x2211 >> +#define YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG 0x56 >> +#define YT8821_UTP_EXT_ANALOG_CFG7_RESET 0x20 >> +#define YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE 0x3F >> +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 >> +#define YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING 0x380C >> +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 >> +#define YT8821_UTP_EXT_TXGE_NFR_FR_SETTING 0x112A >> +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 >> +#define YT8821_UTP_EXT_PLL_SPARE_SETTING 0xE9 >> +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG 0x466 >> +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING 0x6464 >> +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG 0x467 >> +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING 0x6464 >> +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG 0x468 >> +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING 0x6464 >> +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG 0x469 >> +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING 0x6464 > All these _SETTING are magic numbers. Can you document any of them? > please help to confirm it is ok? #define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 #define YT8821_SDS_EXT_CSR_VCO_LDO_EN BIT(15) #define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN BIT(8) #define YT8821_UTP_EXT_RPDN_CTRL_REG 0x34E #define YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 BIT(15) #define YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 BIT(7) #define YT8821_UTP_EXT_RPDN_IPR_SHT_2500 GENMASK(6, 0) #define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 #define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4) #define YT8821_UTP_EXT_VGA_LPF1_CAP_2500 GENMASK(3, 0) #define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 #define YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER GENMASK(7, 4) #define YT8821_UTP_EXT_VGA_LPF2_CAP_2500 GENMASK(3, 0) #define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 #define YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 GENMASK(14, 8) #define YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500 GENMASK(6, 0) #define YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG 0x374 #define YT8821_UTP_EXT_ALPHA_SHT_2500 GENMASK(14, 8) #define YT8821_UTP_EXT_IPR_LNG_2500 GENMASK(6, 0) #define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 #define YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000 GENMASK(14, 8) #define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 #define YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000 GENMASK(6, 0) #define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A #define YT8821_UTP_EXT_TH_20DB_2500 GENMASK(15, 0) #define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 #define YT8821_UTP_EXT_MU_COARSE_FR_F_FFE GENMASK(14, 12) #define YT8821_UTP_EXT_MU_COARSE_FR_F_FBE GENMASK(10, 8) #define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 #define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12) #define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8) #define YT8821_UTP_EXT_PI_CTRL_REG 0x56 #define YT8821_UTP_EXT_PI_RST_N_FIFO BIT(5) #define YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE BIT(4) #define YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE BIT(3) #define YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE BIT(2) #define YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE BIT(1) #define YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE BIT(0) #define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 #define YT8821_UTP_EXT_FECHO_AMP_TH_HUGE GENMASK(15, 8) #define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 #define YT8821_UTP_EXT_NFR_TX_ABILITY BIT(3) #define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 #define YT8821_UTP_EXT_PLL_SPARE_CFG GENMASK(7, 0) #define YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG 0x466 #define YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG GENMASK(14, 8) #define YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG GENMASK(6, 0) #define YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG 0x467 #define YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG GENMASK(14, 8) #define YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG GENMASK(6, 0) #define YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG 0x468 #define YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG GENMASK(14, 8) #define YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG GENMASK(6, 0) #define YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG 0x469 #define YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG GENMASK(14, 8) #define YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG GENMASK(6, 0) >> +/** >> + * yt8821_probe() - read dts to get chip mode >> + * @phydev: a pointer to a &struct phy_device >> + * >> + * returns 0 or negative errno code > kerneldoc requires a : after returns. > >> + */ >> +static int yt8821_probe(struct phy_device *phydev) >> +{ >> + struct device_node *node = phydev->mdio.dev.of_node; >> + struct device *dev = &phydev->mdio.dev; >> + struct yt8821_priv *priv; >> + u8 chip_mode; >> + >> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >> + if (!priv) >> + return -ENOMEM; >> + >> + phydev->priv = priv; >> + >> + if (of_property_read_u8(node, "motorcomm,chip-mode", &chip_mode)) >> + chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; >> + >> + switch (chip_mode) { >> + case YT8821_CHIP_MODE_AUTO_BX2500_SGMII: >> + priv->chip_mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII; >> + break; >> + case YT8821_CHIP_MODE_FORCE_BX2500: >> + priv->chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; >> + break; >> + default: >> + phydev_warn(phydev, "chip_mode err:%d\n", chip_mode); >> + return -EINVAL; > Didn't the binding say it defaults to forced? Yet here it gives an > error? > >> + * yt8821_get_rate_matching - read register to get phy chip mode > Why? You have it in priv? > >> +/** >> + * yt8821gen_init_paged() - generic initialization according to page >> + * @phydev: a pointer to a &struct phy_device >> + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to >> + * operate. >> + * >> + * returns 0 or negative errno code >> + */ >> +static int yt8821gen_init_paged(struct phy_device *phydev, int page) >> +{ >> + int old_page; >> + int ret = 0; >> + >> + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); >> + if (old_page < 0) >> + goto err_restore_page; >> + >> + if (page & YT8521_RSSR_SPACE_MASK) { >> + /* sds init */ >> + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); >> + if (ret < 0) >> + goto err_restore_page; >> + >> + ret = ytphy_write_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, >> + YT8821_SDS_EXT_CSR_PLL_SETTING); >> + if (ret < 0) >> + goto err_restore_page; >> + } else { >> + /* utp init */ >> + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_FFE_IPR_CTRL_REG, >> + YT8821_UTP_EXT_FFE_SETTING); >> + if (ret < 0) >> + goto err_restore_page; >> + > ... > >> + } >> + >> +err_restore_page: >> + return phy_restore_page(phydev, old_page, ret); >> +} >> + >> +/** >> + * yt8821gen_init() - generic initialization >> + * @phydev: a pointer to a &struct phy_device >> + * >> + * returns 0 or negative errno code >> + */ >> +static int yt8821gen_init(struct phy_device *phydev) >> +{ >> + int ret = 0; >> + >> + ret = yt8821gen_init_paged(phydev, YT8521_RSSR_FIBER_SPACE); >> + if (ret < 0) >> + return ret; >> + >> + return yt8821gen_init_paged(phydev, YT8521_RSSR_UTP_SPACE); > That is odd. Why not have two functions, rather than one with a > parameter. You get better functions names then, making it clearer what > each function is doing. In next commit, yt8821gen_init_paged(phydev, YT8521_RSSR_FIBER_SPACE); will be replaced with yt8821_serdes_init(phydev);, yt8821gen_init_paged(phydev, YT8521_RSSR_UTP_SPACE); will be replaced with yt8821_utp_init(phydev); please help to confirm it is ok? >> +} >> + >> +/** >> + * yt8821_auto_sleep_config() - phy auto sleep config >> + * @phydev: a pointer to a &struct phy_device >> + * @enable: true enable auto sleep, false disable auto sleep >> + * >> + * returns 0 or negative errno code >> + */ >> +static int yt8821_auto_sleep_config(struct phy_device *phydev, bool enable) >> +{ >> + int old_page; >> + int ret = 0; >> + >> + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); >> + if (old_page < 0) >> + goto err_restore_page; >> + >> + ret = ytphy_modify_ext(phydev, >> + YT8521_EXTREG_SLEEP_CONTROL1_REG, >> + YT8521_ESC1R_SLEEP_SW, >> + enable ? 1 : 0); > So each page has its own extension registers? Yes > >> + if (ret < 0) >> + goto err_restore_page; >> + >> +err_restore_page: >> + return phy_restore_page(phydev, old_page, ret); >> +} >> + >> +/** >> + * yt8821_config_init() - phy initializatioin >> + * @phydev: a pointer to a &struct phy_device >> + * >> + * returns 0 or negative errno code >> + */ >> +static int yt8821_config_init(struct phy_device *phydev) >> +{ >> + struct yt8821_priv *priv = phydev->priv; >> + int ret, val; >> + >> + phydev->irq = PHY_POLL; > Why do this? phydev->irq = PHY_POLL; will be removed in next commit. > >> + >> + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); >> + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { >> + ret = ytphy_modify_ext_with_lock(phydev, >> + YT8521_CHIP_CONFIG_REG, >> + YT8521_CCR_MODE_SEL_MASK, >> + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); >> + if (ret < 0) >> + return ret; >> + >> + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, >> + phydev->possible_interfaces, >> + true); >> + __assign_bit(PHY_INTERFACE_MODE_SGMII, >> + phydev->possible_interfaces, >> + true); >> + >> + phydev->rate_matching = RATE_MATCH_NONE; >> + } else if (priv->chip_mode == YT8821_CHIP_MODE_FORCE_BX2500) { >> + ret = ytphy_modify_ext_with_lock(phydev, >> + YT8521_CHIP_CONFIG_REG, >> + YT8521_CCR_MODE_SEL_MASK, >> + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 1)); >> + if (ret < 0) >> + return ret; >> + >> + phydev->rate_matching = RATE_MATCH_PAUSE; >> + } > The idea of this phydev->possible_interfaces is to allow the core to > figure out what mode is most appropriate. So i would drop the mode in > DT, default to auto, and let the core tell you it wants 2500 BaseX if > that is all the MAC can do. > >> +static int yt8821_read_status(struct phy_device *phydev) >> +{ >> + struct yt8821_priv *priv = phydev->priv; >> + int old_page; >> + int ret = 0; >> + int link; >> + int val; >> + >> + if (phydev->autoneg == AUTONEG_ENABLE) { >> + int lpadv = phy_read_mmd(phydev, >> + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); >> + >> + if (lpadv < 0) >> + return lpadv; >> + >> + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, >> + lpadv); >> + } >> + >> + ret = ytphy_write_ext_with_lock(phydev, >> + YT8521_REG_SPACE_SELECT_REG, >> + YT8521_RSSR_UTP_SPACE); >> + if (ret < 0) >> + return ret; >> + >> + ret = genphy_read_status(phydev); >> + if (ret < 0) >> + return ret; >> + >> + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); >> + if (old_page < 0) >> + goto err_restore_page; >> + >> + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); >> + if (val < 0) { >> + ret = val; >> + goto err_restore_page; >> + } >> + >> + link = val & YTPHY_SSR_LINK; >> + if (link) >> + yt8821_adjust_status(phydev, val); >> + >> + if (link) { >> + if (phydev->link == 0) >> + phydev_info(phydev, >> + "%s, phy addr: %d, link up, mii reg 0x%x = 0x%x\n", >> + __func__, phydev->mdio.addr, >> + YTPHY_SPECIFIC_STATUS_REG, >> + (unsigned int)val); > phydev_dbg()? > phydev_dbg() instead in next commit. >> + phydev->link = 1; >> + } else { >> + if (phydev->link == 1) >> + phydev_info(phydev, "%s, phy addr: %d, link down\n", >> + __func__, phydev->mdio.addr); > phydev_dbg()? > > Andrew
On 7/29/24 04:41, Russell King (Oracle) wrote: > On Sat, Jul 27, 2024 at 02:20:31AM -0700, Frank.Sae wrote: >> +/** >> + * yt8821_config_init() - phy initializatioin >> + * @phydev: a pointer to a &struct phy_device >> + * >> + * returns 0 or negative errno code >> + */ >> +static int yt8821_config_init(struct phy_device *phydev) >> +{ >> + struct yt8821_priv *priv = phydev->priv; >> + int ret, val; >> + >> + phydev->irq = PHY_POLL; >> + >> + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); >> + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { >> + ret = ytphy_modify_ext_with_lock(phydev, >> + YT8521_CHIP_CONFIG_REG, >> + YT8521_CCR_MODE_SEL_MASK, >> + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); >> + if (ret < 0) >> + return ret; >> + >> + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, >> + phydev->possible_interfaces, >> + true); >> + __assign_bit(PHY_INTERFACE_MODE_SGMII, >> + phydev->possible_interfaces, >> + true); > Before each and every call to .config_init, phydev->possible_interfaces > will be cleared. So, please use __set_bit() here. > >> +static int yt8821_read_status(struct phy_device *phydev) >> +{ >> + struct yt8821_priv *priv = phydev->priv; >> + int old_page; >> + int ret = 0; >> + int link; >> + int val; >> + >> + if (phydev->autoneg == AUTONEG_ENABLE) { >> + int lpadv = phy_read_mmd(phydev, >> + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); >> + >> + if (lpadv < 0) >> + return lpadv; >> + >> + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, >> + lpadv); >> + } >> + >> + ret = ytphy_write_ext_with_lock(phydev, >> + YT8521_REG_SPACE_SELECT_REG, >> + YT8521_RSSR_UTP_SPACE); >> + if (ret < 0) >> + return ret; >> + >> + ret = genphy_read_status(phydev); >> + if (ret < 0) >> + return ret; >> + >> + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); >> + if (old_page < 0) >> + goto err_restore_page; >> + >> + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); >> + if (val < 0) { >> + ret = val; >> + goto err_restore_page; >> + } >> + >> + link = val & YTPHY_SSR_LINK; > What link status is this reporting? For interface switching to work, > phydev->link must _only_ indicate whether the _media_ side interface > is up or down. It must _not_ include the status of the MAC facing > interface from the PHY. > > Why? The interface configuration of the MAC is only performed when > the _media_ link comes up, denoted by phydev->link becoming true. > If the MAC interface configuration mismatches the PHY interface > configuration, then the MAC facing interface of the PHY will > remain down, and if phydev->link is forced to false, then the link > will never come up. > > So, I hope that this isn't testing the MAC facing interface status > of the PHY! > MAC facing interface will be switched according to phy interface. when phy media side state is link down, the mac facing interface will not be configured, this refers to Marvell10g.c(mv3310_update_interface) and Realtek.c(rtl822xb_update_interface).
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 7a11fdb687cc..a432b27dd849 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Motorcomm 8511/8521/8531/8531S PHY driver. + * Motorcomm 8511/8521/8531/8531S/8821 PHY driver. * * Author: Peter Geis <pgwipeout@gmail.com> * Author: Frank <Frank.Sae@motor-comm.com> @@ -16,7 +16,7 @@ #define PHY_ID_YT8521 0x0000011a #define PHY_ID_YT8531 0x4f51e91b #define PHY_ID_YT8531S 0x4f51e91a - +#define PHY_ID_YT8821 0x4f51ea19 /* YT8521/YT8531S Register Overview * UTP Register space | FIBER Register space * ------------------------------------------------------------ @@ -52,6 +52,15 @@ #define YTPHY_SSR_SPEED_10M 0x0 #define YTPHY_SSR_SPEED_100M 0x1 #define YTPHY_SSR_SPEED_1000M 0x2 +/* bit9 as speed_mode[2], bit15:14 as Speed_mode[1:0] + * Speed_mode[2:0]: + * 100 = 2P5G + * 011 = 10G + * 010 = 1000 Mbps + * 001 = 100 Mbps + * 000 = 10 Mbps + */ +#define YT8821_SSR_SPEED_2500M 0x4 #define YTPHY_SSR_DUPLEX_OFFSET 13 #define YTPHY_SSR_DUPLEX BIT(13) #define YTPHY_SSR_PAGE_RECEIVED BIT(12) @@ -270,12 +279,59 @@ #define YT8531_SCR_CLK_SRC_REF_25M 4 #define YT8531_SCR_CLK_SRC_SSC_25M 5 +#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 +#define YT8821_SDS_EXT_CSR_PLL_SETTING 0x8605 +#define YT8821_UTP_EXT_FFE_IPR_CTRL_REG 0x34E +#define YT8821_UTP_EXT_FFE_SETTING 0x8080 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING 0x5200 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING 0x5200 +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 +#define YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING 0x5A3C +#define YT8821_UTP_EXT_IPR_CTRL_REG 0x374 +#define YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING 0x7C6C +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 +#define YT8821_UTP_EXT_ECHO_SETTING 0xAA0A +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 +#define YT8821_UTP_EXT_AGC_MED_GAIN_SETTING 0x3022 +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A +#define YT8821_UTP_EXT_TH_20DB_2500_SETTING 0x8000 +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 +#define YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING 0x7711 +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 +#define YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING 0x2211 +#define YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG 0x56 +#define YT8821_UTP_EXT_ANALOG_CFG7_RESET 0x20 +#define YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE 0x3F +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 +#define YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING 0x380C +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 +#define YT8821_UTP_EXT_TXGE_NFR_FR_SETTING 0x112A +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 +#define YT8821_UTP_EXT_PLL_SPARE_SETTING 0xE9 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG 0x466 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG 0x467 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG 0x468 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG 0x469 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING 0x6464 /* Extended Register end */ #define YTPHY_DTS_OUTPUT_CLK_DIS 0 #define YTPHY_DTS_OUTPUT_CLK_25M 25000000 #define YTPHY_DTS_OUTPUT_CLK_125M 125000000 +#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0 +#define YT8821_CHIP_MODE_FORCE_BX2500 1 + +struct yt8821_priv { + /* chip mode: AUTO_BX2500_SGMII/FORCE_BX2500 */ + u32 chip_mode; +}; + struct yt8521_priv { /* combo_advertising is used for case of YT8521 in combo mode, * this means that yt8521 may work in utp or fiber mode which depends @@ -2252,6 +2308,564 @@ static int yt8521_get_features(struct phy_device *phydev) return ret; } +/** + * yt8821_probe() - read dts to get chip mode + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_probe(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; + struct yt8821_priv *priv; + u8 chip_mode; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + if (of_property_read_u8(node, "motorcomm,chip-mode", &chip_mode)) + chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; + + switch (chip_mode) { + case YT8821_CHIP_MODE_AUTO_BX2500_SGMII: + priv->chip_mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII; + break; + case YT8821_CHIP_MODE_FORCE_BX2500: + priv->chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; + break; + default: + phydev_warn(phydev, "chip_mode err:%d\n", chip_mode); + return -EINVAL; + } + + return 0; +} + +/** + * yt8821_get_features - read mmd register to get 2.5G capability + * @phydev: target phy_device struct + * + * returns 0 or negative errno code + */ +static int yt8821_get_features(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_NG_EXTABLE_2_5GBT); + + return genphy_read_abilities(phydev); +} + +/** + * yt8821_get_rate_matching - read register to get phy chip mode + * @phydev: target phy_device struct + * @iface: PHY data interface type + * + * returns rate matching type or negative errno code + */ +static int yt8821_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) +{ + int val; + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (val < 0) + return val; + + if ((val & YT8521_CCR_MODE_SEL_MASK) == + YT8821_CHIP_MODE_FORCE_BX2500) { + return RATE_MATCH_PAUSE; + } + + return RATE_MATCH_NONE; +} + +/** + * yt8821_aneg_done() - determines the auto negotiation result + * @phydev: a pointer to a &struct phy_device + * + * returns 0(no link)or 1(utp link) or negative errno code + */ +static int yt8821_aneg_done(struct phy_device *phydev) +{ + int link; + + link = yt8521_aneg_done_paged(phydev, YT8521_RSSR_UTP_SPACE); + + return link; +} + +/** + * yt8821gen_init_paged() - generic initialization according to page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8821gen_init_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + if (page & YT8521_RSSR_SPACE_MASK) { + /* sds init */ + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, + YT8821_SDS_EXT_CSR_PLL_SETTING); + if (ret < 0) + goto err_restore_page; + } else { + /* utp init */ + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_FFE_IPR_CTRL_REG, + YT8821_UTP_EXT_FFE_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG, + YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG, + YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG, + YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_IPR_CTRL_REG, + YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG, + YT8821_UTP_EXT_ECHO_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG, + YT8821_UTP_EXT_AGC_MED_GAIN_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG, + YT8821_UTP_EXT_TH_20DB_2500_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG, + YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG, + YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG, + YT8821_UTP_EXT_ANALOG_CFG7_RESET); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG, + YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VCT_CFG6_CTRL_REG, + YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG, + YT8821_UTP_EXT_TXGE_NFR_FR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG, + YT8821_UTP_EXT_PLL_SPARE_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG, + YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG, + YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821gen_init() - generic initialization + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821gen_init(struct phy_device *phydev) +{ + int ret = 0; + + ret = yt8821gen_init_paged(phydev, YT8521_RSSR_FIBER_SPACE); + if (ret < 0) + return ret; + + return yt8821gen_init_paged(phydev, YT8521_RSSR_UTP_SPACE); +} + +/** + * yt8821_auto_sleep_config() - phy auto sleep config + * @phydev: a pointer to a &struct phy_device + * @enable: true enable auto sleep, false disable auto sleep + * + * returns 0 or negative errno code + */ +static int yt8821_auto_sleep_config(struct phy_device *phydev, bool enable) +{ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + ret = ytphy_modify_ext(phydev, + YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, + enable ? 1 : 0); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821_soft_reset() - soft reset utp and serdes + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_soft_reset(struct phy_device *phydev) +{ + return ytphy_modify_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); +} + +/** + * yt8821_config_init() - phy initializatioin + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_config_init(struct phy_device *phydev) +{ + struct yt8821_priv *priv = phydev->priv; + int ret, val; + + phydev->irq = PHY_POLL; + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_CHIP_CONFIG_REG, + YT8521_CCR_MODE_SEL_MASK, + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); + if (ret < 0) + return ret; + + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, + phydev->possible_interfaces, + true); + __assign_bit(PHY_INTERFACE_MODE_SGMII, + phydev->possible_interfaces, + true); + + phydev->rate_matching = RATE_MATCH_NONE; + } else if (priv->chip_mode == YT8821_CHIP_MODE_FORCE_BX2500) { + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_CHIP_CONFIG_REG, + YT8521_CCR_MODE_SEL_MASK, + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 1)); + if (ret < 0) + return ret; + + phydev->rate_matching = RATE_MATCH_PAUSE; + } + + ret = yt8821gen_init(phydev); + if (ret < 0) + return ret; + + /* disable auto sleep */ + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + /* soft reset */ + yt8821_soft_reset(phydev); + + return ret; +} + +/** + * yt8821_adjust_status() - update speed and duplex to phydev + * @phydev: a pointer to a &struct phy_device + * @val: read from YTPHY_SPECIFIC_STATUS_REG + */ +static void yt8821_adjust_status(struct phy_device *phydev, int val) +{ + int speed_mode, duplex; + int speed_mode_low, speed_mode_high; + int speed = SPEED_UNKNOWN; + + duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val); + + speed_mode_low = FIELD_GET(GENMASK(15, 14), val); + speed_mode_high = FIELD_GET(BIT(9), val); + speed_mode = FIELD_PREP(BIT(2), speed_mode_high) | + FIELD_PREP(GENMASK(1, 0), speed_mode_low); + switch (speed_mode) { + case YTPHY_SSR_SPEED_10M: + speed = SPEED_10; + break; + case YTPHY_SSR_SPEED_100M: + speed = SPEED_100; + break; + case YTPHY_SSR_SPEED_1000M: + speed = SPEED_1000; + break; + case YT8821_SSR_SPEED_2500M: + speed = SPEED_2500; + break; + default: + speed = SPEED_UNKNOWN; + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; +} + +/** + * yt8821_update_interface() - update interface per current speed + * @phydev: a pointer to a &struct phy_device + */ +static void yt8821_update_interface(struct phy_device *phydev) +{ + if (!phydev->link) + return; + + switch (phydev->speed) { + case SPEED_2500: + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + break; + case SPEED_1000: + case SPEED_100: + case SPEED_10: + phydev->interface = PHY_INTERFACE_MODE_SGMII; + break; + default: + phydev_warn(phydev, "phy speed err:%d\n", phydev->speed); + break; + } +} + +/** + * yt8821_read_status() - determines the negotiated speed and duplex + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_read_status(struct phy_device *phydev) +{ + struct yt8821_priv *priv = phydev->priv; + int old_page; + int ret = 0; + int link; + int val; + + if (phydev->autoneg == AUTONEG_ENABLE) { + int lpadv = phy_read_mmd(phydev, + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + + if (lpadv < 0) + return lpadv; + + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, + lpadv); + } + + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + ret = genphy_read_status(phydev); + if (ret < 0) + return ret; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (val < 0) { + ret = val; + goto err_restore_page; + } + + link = val & YTPHY_SSR_LINK; + if (link) + yt8821_adjust_status(phydev, val); + + if (link) { + if (phydev->link == 0) + phydev_info(phydev, + "%s, phy addr: %d, link up, mii reg 0x%x = 0x%x\n", + __func__, phydev->mdio.addr, + YTPHY_SPECIFIC_STATUS_REG, + (unsigned int)val); + phydev->link = 1; + } else { + if (phydev->link == 1) + phydev_info(phydev, "%s, phy addr: %d, link down\n", + __func__, phydev->mdio.addr); + phydev->link = 0; + } + + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) + yt8821_update_interface(phydev); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register + * @phydev: the phy_device struct + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * + * returns 0 or negative errno code + */ +static int yt8821_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask, + u16 set) +{ + int ret; + + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE, + mask, set); + if (ret < 0) + return ret; + + return yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE, + mask, set); +} + +/** + * yt8821_suspend() - suspend the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_suspend(struct phy_device *phydev) +{ + int wol_config; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8821_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN); +} + +/** + * yt8821_resume() - resume the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_resume(struct phy_device *phydev) +{ + int wol_config; + int ret; + + /* disable auto sleep */ + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); +} + static struct phy_driver motorcomm_phy_drvs[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511), @@ -2307,11 +2921,29 @@ static struct phy_driver motorcomm_phy_drvs[] = { .suspend = yt8521_suspend, .resume = yt8521_resume, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8821), + .name = "YT8821 2.5Gbps PHY", + .get_features = yt8821_get_features, + .probe = yt8821_probe, + .read_page = yt8521_read_page, + .write_page = yt8521_write_page, + .get_wol = ytphy_get_wol, + .set_wol = ytphy_set_wol, + .config_aneg = genphy_config_aneg, + .aneg_done = yt8821_aneg_done, + .config_init = yt8821_config_init, + .get_rate_matching = yt8821_get_rate_matching, + .read_status = yt8821_read_status, + .soft_reset = yt8821_soft_reset, + .suspend = yt8821_suspend, + .resume = yt8821_resume, + }, }; module_phy_driver(motorcomm_phy_drvs); -MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver"); +MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver"); MODULE_AUTHOR("Peter Geis"); MODULE_AUTHOR("Frank"); MODULE_LICENSE("GPL"); @@ -2321,6 +2953,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8821) }, { /* sentinel */ } };
Add a driver for the motorcomm yt8821 2.5G ethernet phy. Verified the driver on BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board, which is developed by Guangdong Bipai Technology Co., Ltd.. On the board, yt8821 2.5G ethernet phy works in AUTO_BX2500_SGMII or FORCE_BX2500 interface, supports 2.5G/1000M/100M/10M speeds, and wol(magic package). Since some functions of yt8821 are similar to YT8521 so some functions for yt8821 can be reused. Signed-off-by: Frank.Sae <Frank.Sae@motor-comm.com> --- drivers/net/phy/motorcomm.c | 639 +++++++++++++++++++++++++++++++++++- 1 file changed, 636 insertions(+), 3 deletions(-)