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@xxxxxxxxxxxxxx>
---
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@xxxxxxxxx>
* Author: Frank <Frank.Sae@xxxxxxxxxxxxxx>
@@ -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