#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)
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;
.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,