mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
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:
commit
22d6adac5b
@ -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 */ }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user