diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 3d98df197a33..27268811f564 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -206,6 +206,21 @@ #define RTL8224_VND1_MDI_PAIR_SWAP 0xa90 #define RTL8224_VND1_MDI_POLARITY_SWAP 0xa94 +#define RTL8226_VND1_UNKNOWN_6A21 0x6a21 +#define RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN BIT(5) + +#define RTL8226_VND2_UNKNOWN_D068 0xd068 +#define RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG BIT(1) +#define RTL8226_VND2_UNKNOWN_D068_PAIR_SEL GENMASK(4, 3) +#define RTL8226_VND2_ADCCAL_OFFSET 0xd06a + +#define RTL8226_VND2_RG_LPF_CAP_XG_P0_P1 0xbd5a +#define RTL8226_VND2_RG_LPF_CAP_XG_P2_P3 0xbd5c +#define RTL8226_VND2_RG_LPF_CAP_P0_P1 0xbc18 +#define RTL8226_VND2_RG_LPF_CAP_P2_P3 0xbc1a +#define RTL8226_RG_LPF_CAP_PAIR_A_MASK GENMASK(4, 0) +#define RTL8226_RG_LPF_CAP_PAIR_B_MASK GENMASK(12, 8) + #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) @@ -1485,6 +1500,144 @@ static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg, return write_ret; } +static int rtl8226_set_mdi_swap(struct phy_device *phydev, bool swap_enable) +{ + u16 val = swap_enable ? RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN : 0; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND1, RTL8226_VND1_UNKNOWN_6A21, + RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN, val); +} + +static int rtl8226_swap_rg_lpf_cap(struct phy_device *phydev, u32 reg_p0_p1, u32 reg_p2_p3) +{ + u16 val_p0, val_p1, val_p2, val_p3; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1); + if (ret < 0) + return ret; + + val_p0 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret); + val_p1 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret); + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3); + if (ret < 0) + return ret; + + val_p2 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret); + val_p3 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret); + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1, + RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK, + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p3) | + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p2)); + if (ret < 0) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3, + RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK, + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p1) | + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p0)); +} + +static int rtl8226_patch_mdi_swap(struct phy_device *phydev, bool swap_enable) +{ + u16 adccal_offset[4]; + bool is_patched; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068); + if (ret < 0) + return ret; + + is_patched = !(ret & RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG); + + if (is_patched == swap_enable) { + /* Nothing to do */ + return 0; + } + + if (!swap_enable) { + /* Patching is only implemented one-way, see next comment. */ + phydev_err(phydev, "MDI swapping disabled, but PHY is already patched.\n"); + return -EINVAL; + } + + /* The exact meaning of these bits is unknown. We only know that bit 1 + * is used as a flag that swapping is already done. + */ + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, 0x7, 0x1); + if (ret < 0) + return ret; + + for (int i = 0; i < 4; i++) { + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, + RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, + FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i)); + if (ret < 0) + return ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET); + if (ret < 0) + return ret; + + adccal_offset[i] = ret; + } + + for (int i = 0; i < 4; i++) { + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, + RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, + FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i)); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET, + adccal_offset[3 - i]); + if (ret < 0) + return ret; + } + + ret = rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_XG_P0_P1, + RTL8226_VND2_RG_LPF_CAP_XG_P2_P3); + if (ret < 0) + return ret; + + return rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_P0_P1, + RTL8226_VND2_RG_LPF_CAP_P2_P3); +} + +static int rtl8226_config_mdi_order(struct phy_device *phydev) +{ + u32 order; + bool swap_enable; + int ret; + + ret = of_property_read_u32(phydev->mdio.dev.of_node, "enet-phy-pair-order", &order); + + /* Property not present, nothing to do */ + if (ret == -EINVAL || ret == -ENOSYS) + return 0; + + if (ret) + return ret; + + if (order & ~1) + return -EINVAL; + + swap_enable = !!(order & 1); + + ret = rtl8226_set_mdi_swap(phydev, swap_enable); + if (ret) + return ret; + + return rtl8226_patch_mdi_swap(phydev, swap_enable); +} + +static int rtl8226_probe(struct phy_device *phydev) +{ + return rtl8226_config_mdi_order(phydev); +} + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) { bool has_2500, has_sgmii; @@ -2684,6 +2837,7 @@ static struct phy_driver realtek_drvs[] = { .soft_reset = rtl822x_c45_soft_reset, .get_features = rtl822x_c45_get_features, .config_aneg = rtl822x_c45_config_aneg, + .probe = rtl8226_probe, .config_init = rtl822x_config_init, .inband_caps = rtl822x_inband_caps, .config_inband = rtl822x_config_inband,