Merge branch 'add-driver-for-motorcomm-yt8821-2-5g-ethernet-phy'

Frank Sae says:

====================
Add driver for Motorcomm yt8821 2.5G ethernet phy

yt8521 and yt8531s as Gigabit transceiver use bit15:14(bit9 reserved
default 0) as phy speed mask, yt8821 as 2.5G transceiver uses bit9 bit15:14
as phy speed mask.

Be compatible to yt8821, reform phy speed mask and phy speed macro.

Based on update above, add yt8821 2.5G phy driver.
====================

Link: https://patch.msgid.link/20240901083526.163784-1-Frank.Sae@motor-comm.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-09-05 15:21:14 +02:00
commit 22d6adac5b

View File

@ -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,8 +16,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 |
@ -46,12 +46,12 @@
/* Specific Status Register */
#define YTPHY_SPECIFIC_STATUS_REG 0x11
#define YTPHY_SSR_SPEED_MODE_OFFSET 14
#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14))
#define YTPHY_SSR_SPEED_10M 0x0
#define YTPHY_SSR_SPEED_100M 0x1
#define YTPHY_SSR_SPEED_1000M 0x2
#define YTPHY_SSR_SPEED_MASK ((0x3 << 14) | BIT(9))
#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)
@ -270,12 +270,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
@ -1187,8 +1264,7 @@ static int yt8521_adjust_status(struct phy_device *phydev, int status,
else
duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */
speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
YTPHY_SSR_SPEED_MODE_OFFSET;
speed_mode = status & YTPHY_SSR_SPEED_MASK;
switch (speed_mode) {
case YTPHY_SSR_SPEED_10M:
@ -2252,6 +2328,572 @@ static int yt8521_get_features(struct phy_device *phydev)
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),
@ -2307,11 +2949,28 @@ 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,
.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 +2980,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 */ }
};