
Siflower SF21A6826/SF21H8898 are a family of RISC-V SoCs with: * Quad-core T-Head C908 (1.125G for SF21A6826, 1.25G for SF21H8898) * DDR3/DDR4 memory controller * 1 QSGMII 4x1G * 1 SGMII/2500Base-X 2.5G * 1 additional RGMII on SF21H8898 * Network offloading engine for L2 switching and L3 NAT * 2 PCIE Gen2 lanes, operating in either one PCIE Gen2x2 or two PCIE Gen2x1 mode * 1 USB2.0 Link: https://github.com/openwrt/openwrt/pull/17115 Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
754 lines
21 KiB
Diff
754 lines
21 KiB
Diff
From: Frank Sae <Frank.Sae@motor-comm.com>
|
|
Date: Sun, 1 Sep 2024 01:35:26 -0700
|
|
Subject: [PATCH 03/20] net: phy: Add driver for Motorcomm yt8821 2.5G ethernet
|
|
phy
|
|
|
|
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..
|
|
|
|
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).
|
|
|
|
Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
|
|
Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
|
|
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
|
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
|
|
|
(cherry picked from commit b671105b88c3bb9acc1fb61a3ee2ca0ece60cb8d)
|
|
---
|
|
drivers/net/phy/motorcomm.c | 671 +++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 667 insertions(+), 4 deletions(-)
|
|
|
|
--- 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>
|
|
@@ -17,8 +17,8 @@
|
|
#define PHY_ID_YT8521 0x0000011a
|
|
#define PHY_ID_YT8531 0x4f51e91b
|
|
#define PHY_ID_YT8531S 0x4f51e91a
|
|
-
|
|
-/* YT8521/YT8531S Register Overview
|
|
+#define PHY_ID_YT8821 0x4f51ea19
|
|
+/* YT8521/YT8531S/YT8821 Register Overview
|
|
* UTP Register space | FIBER Register space
|
|
* ------------------------------------------------------------
|
|
* | UTP MII | FIBER MII |
|
|
@@ -51,6 +51,8 @@
|
|
#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 YTPHY_SSR_SPEED_2500M ((0x0 << 14) | BIT(9))
|
|
#define YTPHY_SSR_DUPLEX_OFFSET 13
|
|
#define YTPHY_SSR_DUPLEX BIT(13)
|
|
#define YTPHY_SSR_PAGE_RECEIVED BIT(12)
|
|
@@ -269,12 +271,89 @@
|
|
#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_VCO_LDO_EN BIT(15)
|
|
+#define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN BIT(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_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_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_TH_20DB_2500_CTRL_REG 0x36A
|
|
+#define YT8821_UTP_EXT_TH_20DB_2500 GENMASK(15, 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_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)
|
|
+
|
|
+#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_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_TXGE_NFR_FR_THP_CTRL_REG 0x660
|
|
+#define YT8821_UTP_EXT_NFR_TX_ABILITY BIT(3)
|
|
/* 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 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
|
|
@@ -2250,6 +2329,572 @@ static int yt8521_get_features(struct ph
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * 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 ret;
|
|
+
|
|
+ ret = genphy_c45_pma_read_ext_abilities(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ 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 (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) ==
|
|
+ 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)
|
|
+{
|
|
+ return yt8521_aneg_done_paged(phydev, YT8521_RSSR_UTP_SPACE);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * yt8821_serdes_init() - serdes init
|
|
+ * @phydev: a pointer to a &struct phy_device
|
|
+ *
|
|
+ * Returns: 0 or negative errno code
|
|
+ */
|
|
+static int yt8821_serdes_init(struct phy_device *phydev)
|
|
+{
|
|
+ int old_page;
|
|
+ int ret = 0;
|
|
+ u16 mask;
|
|
+ u16 set;
|
|
+
|
|
+ old_page = phy_select_page(phydev, YT8521_RSSR_FIBER_SPACE);
|
|
+ if (old_page < 0) {
|
|
+ phydev_err(phydev, "Failed to select page: %d\n",
|
|
+ old_page);
|
|
+ goto err_restore_page;
|
|
+ }
|
|
+
|
|
+ ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_SDS_EXT_CSR_VCO_LDO_EN |
|
|
+ YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN;
|
|
+ set = YT8821_SDS_EXT_CSR_VCO_LDO_EN;
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, mask,
|
|
+ set);
|
|
+
|
|
+err_restore_page:
|
|
+ return phy_restore_page(phydev, old_page, ret);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * yt8821_utp_init() - utp init
|
|
+ * @phydev: a pointer to a &struct phy_device
|
|
+ *
|
|
+ * Returns: 0 or negative errno code
|
|
+ */
|
|
+static int yt8821_utp_init(struct phy_device *phydev)
|
|
+{
|
|
+ int old_page;
|
|
+ int ret = 0;
|
|
+ u16 mask;
|
|
+ u16 save;
|
|
+ u16 set;
|
|
+
|
|
+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
|
|
+ if (old_page < 0) {
|
|
+ phydev_err(phydev, "Failed to select page: %d\n",
|
|
+ old_page);
|
|
+ goto err_restore_page;
|
|
+ }
|
|
+
|
|
+ mask = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
|
|
+ YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 |
|
|
+ YT8821_UTP_EXT_RPDN_IPR_SHT_2500;
|
|
+ set = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
|
|
+ YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500;
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_RPDN_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER |
|
|
+ YT8821_UTP_EXT_VGA_LPF1_CAP_2500;
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG,
|
|
+ mask, 0);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER |
|
|
+ YT8821_UTP_EXT_VGA_LPF2_CAP_2500;
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG,
|
|
+ mask, 0);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 |
|
|
+ YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500, 0x5a) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500, 0x3c);
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_IPR_LNG_2500;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_IPR_LNG_2500, 0x6c);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000, 0x2a);
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000, 0x22);
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_TH_20DB_2500;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_TH_20DB_2500, 0x8000);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_MU_COARSE_FR_F_FFE |
|
|
+ YT8821_UTP_EXT_MU_COARSE_FR_F_FBE;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FFE, 0x7) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FBE, 0x7);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_MU_FINE_FR_F_FFE |
|
|
+ YT8821_UTP_EXT_MU_FINE_FR_F_FBE;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FFE, 0x2) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FBE, 0x2);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ /* save YT8821_UTP_EXT_PI_CTRL_REG's val for use later */
|
|
+ ret = ytphy_read_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ save = ret;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE |
|
|
+ YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE |
|
|
+ YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE |
|
|
+ YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE |
|
|
+ YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE;
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG,
|
|
+ mask, 0);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ /* restore YT8821_UTP_EXT_PI_CTRL_REG's val */
|
|
+ ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, save);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_FECHO_AMP_TH_HUGE;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_FECHO_AMP_TH_HUGE, 0x38);
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_VCT_CFG6_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_NFR_TX_ABILITY;
|
|
+ set = YT8821_UTP_EXT_NFR_TX_ABILITY;
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_PLL_SPARE_CFG;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_PLL_SPARE_CFG, 0xe9);
|
|
+ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG |
|
|
+ YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG, 0x64) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG, 0x64);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG |
|
|
+ YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG, 0x64) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG, 0x64);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG |
|
|
+ YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG, 0x64) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG, 0x64);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG,
|
|
+ mask, set);
|
|
+ if (ret < 0)
|
|
+ goto err_restore_page;
|
|
+
|
|
+ mask = YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG |
|
|
+ YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG;
|
|
+ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG, 0x64) |
|
|
+ FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG, 0x64);
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG,
|
|
+ mask, set);
|
|
+
|
|
+err_restore_page:
|
|
+ return phy_restore_page(phydev, old_page, ret);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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) {
|
|
+ phydev_err(phydev, "Failed to select page: %d\n",
|
|
+ old_page);
|
|
+ goto err_restore_page;
|
|
+ }
|
|
+
|
|
+ ret = ytphy_modify_ext(phydev,
|
|
+ YT8521_EXTREG_SLEEP_CONTROL1_REG,
|
|
+ YT8521_ESC1R_SLEEP_SW,
|
|
+ enable ? 1 : 0);
|
|
+
|
|
+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)
|
|
+{
|
|
+ u8 mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII;
|
|
+ int ret;
|
|
+ u16 set;
|
|
+
|
|
+ if (phydev->interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
+ mode = YT8821_CHIP_MODE_FORCE_BX2500;
|
|
+
|
|
+ set = FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, mode);
|
|
+ ret = ytphy_modify_ext_with_lock(phydev,
|
|
+ YT8521_CHIP_CONFIG_REG,
|
|
+ YT8521_CCR_MODE_SEL_MASK,
|
|
+ set);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
|
|
+ phydev->possible_interfaces);
|
|
+
|
|
+ if (mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) {
|
|
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
|
|
+ phydev->possible_interfaces);
|
|
+
|
|
+ phydev->rate_matching = RATE_MATCH_NONE;
|
|
+ } else if (mode == YT8821_CHIP_MODE_FORCE_BX2500) {
|
|
+ phydev->rate_matching = RATE_MATCH_PAUSE;
|
|
+ }
|
|
+
|
|
+ ret = yt8821_serdes_init(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = yt8821_utp_init(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* disable auto sleep */
|
|
+ ret = yt8821_auto_sleep_config(phydev, false);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* soft reset */
|
|
+ return yt8821_soft_reset(phydev);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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, duplex;
|
|
+ int speed_mode;
|
|
+
|
|
+ duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val);
|
|
+ speed_mode = val & YTPHY_SSR_SPEED_MASK;
|
|
+ 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 YTPHY_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)
|
|
+{
|
|
+ int link;
|
|
+ int ret;
|
|
+ int val;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ if (phydev->autoneg_complete) {
|
|
+ ret = genphy_c45_read_lpa(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ val = ret;
|
|
+
|
|
+ link = val & YTPHY_SSR_LINK;
|
|
+ if (link)
|
|
+ yt8821_adjust_status(phydev, val);
|
|
+
|
|
+ if (link) {
|
|
+ if (phydev->link == 0)
|
|
+ phydev_dbg(phydev,
|
|
+ "%s, phy addr: %d, link up\n",
|
|
+ __func__, phydev->mdio.addr);
|
|
+ phydev->link = 1;
|
|
+ } else {
|
|
+ if (phydev->link == 1)
|
|
+ phydev_dbg(phydev,
|
|
+ "%s, phy addr: %d, link down\n",
|
|
+ __func__, phydev->mdio.addr);
|
|
+ phydev->link = 0;
|
|
+ }
|
|
+
|
|
+ val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
|
|
+ if (val < 0)
|
|
+ return val;
|
|
+
|
|
+ if (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) ==
|
|
+ YT8821_CHIP_MODE_AUTO_BX2500_SGMII)
|
|
+ yt8821_update_interface(phydev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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),
|
|
@@ -2305,11 +2950,28 @@ static struct phy_driver motorcomm_phy_d
|
|
.suspend = yt8521_suspend,
|
|
.resume = yt8521_resume,
|
|
},
|
|
+ {
|
|
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8821),
|
|
+ .name = "YT8821 2.5Gbps PHY",
|
|
+ .get_features = yt8821_get_features,
|
|
+ .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");
|
|
@@ -2319,6 +2981,7 @@ static const struct mdio_device_id __may
|
|
{ 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 */ }
|
|
};
|
|
|