static const struct firmware rtl838x_8214fc_fw;
static const struct firmware rtl838x_8218b_fw;
-int rtl838x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
-int rtl838x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
-int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
-int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
-int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
-int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
-int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
-int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
-
-static int read_phy(u32 port, u32 page, u32 reg, u32 *val)
-{ switch (soc_info.family) {
- case RTL8380_FAMILY_ID:
- return rtl838x_read_phy(port, page, reg, val);
- case RTL8390_FAMILY_ID:
- return rtl839x_read_phy(port, page, reg, val);
- case RTL9300_FAMILY_ID:
- return rtl930x_read_phy(port, page, reg, val);
- case RTL9310_FAMILY_ID:
- return rtl931x_read_phy(port, page, reg, val);
- }
- return -1;
-}
-
-static int write_phy(u32 port, u32 page, u32 reg, u32 val)
-{
- switch (soc_info.family) {
- case RTL8380_FAMILY_ID:
- return rtl838x_write_phy(port, page, reg, val);
- case RTL8390_FAMILY_ID:
- return rtl839x_write_phy(port, page, reg, val);
- case RTL9300_FAMILY_ID:
- return rtl930x_write_phy(port, page, reg, val);
- case RTL9310_FAMILY_ID:
- return rtl931x_write_phy(port, page, reg, val);
- }
- return -1;
-}
-
-static int read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
-{
- switch (soc_info.family) {
- case RTL8380_FAMILY_ID:
- return rtl838x_read_mmd_phy(port, devnum, regnum, val);
- case RTL8390_FAMILY_ID:
- return rtl839x_read_mmd_phy(port, devnum, regnum, val);
- case RTL9300_FAMILY_ID:
- return rtl930x_read_mmd_phy(port, devnum, regnum, val);
- case RTL9310_FAMILY_ID:
- return rtl931x_read_mmd_phy(port, devnum, regnum, val);
- }
- return -1;
-}
-
-int write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val)
-{
- switch (soc_info.family) {
- case RTL8380_FAMILY_ID:
- return rtl838x_write_mmd_phy(port, devnum, reg, val);
- case RTL8390_FAMILY_ID:
- return rtl839x_write_mmd_phy(port, devnum, reg, val);
- case RTL9300_FAMILY_ID:
- return rtl930x_write_mmd_phy(port, devnum, reg, val);
- case RTL9310_FAMILY_ID:
- return rtl931x_write_mmd_phy(port, devnum, reg, val);
- }
- return -1;
-}
-
static u64 disable_polling(int port)
{
u64 saved_state;
return 0;
}
-static void rtl8380_int_phy_on_off(int mac, bool on)
+static void rtl8380_int_phy_on_off(struct phy_device *phydev, bool on)
{
- u32 val;
-
- read_phy(mac, 0, 0, &val);
- if (on)
- write_phy(mac, 0, 0, val & ~BIT(11));
- else
- write_phy(mac, 0, 0, val | BIT(11));
+ phy_modify(phydev, 0, BIT(11), on?0:BIT(11));
}
-static void rtl8380_rtl8214fc_on_off(int mac, bool on)
+static void rtl8380_rtl8214fc_on_off(struct phy_device *phydev, bool on)
{
- u32 val;
-
/* fiber ports */
- write_phy(mac, 4095, 30, 3);
- read_phy(mac, 0, 16, &val);
- if (on)
- write_phy(mac, 0, 16, val & ~BIT(11));
- else
- write_phy(mac, 0, 16, val | BIT(11));
+ phy_write_paged(phydev, 4095, 30, 3);
+ phy_modify(phydev, 16, BIT(11), on?0:BIT(11));
/* copper ports */
- write_phy(mac, 4095, 30, 1);
- read_phy(mac, 0, 16, &val);
- if (on)
- write_phy(mac, 0xa40, 16, val & ~BIT(11));
- else
- write_phy(mac, 0xa40, 16, val | BIT(11));
+ phy_write_paged(phydev, 4095, 30, 1);
+ phy_modify_paged(phydev, 0xa40, 16, BIT(11), on?0:BIT(11));
}
-static void rtl8380_phy_reset(int mac)
+static void rtl8380_phy_reset(struct phy_device *phydev)
{
- u32 val;
-
- read_phy(mac, 0, 0, &val);
- write_phy(mac, 0, 0, val | BIT(15));
+ phy_modify(phydev, 0, BIT(15), BIT(15));
}
// The access registers for SDS_MODE_SEL and the LSB for each SDS within
{
int ret = 0, i;
u32 val;
- int port = phydev->mdio.addr;
// TODO: ret = genphy_read_status(phydev);
// if (ret < 0) {
// Link status must be read twice
for (i = 0; i < 2; i++) {
- read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
+ val = phy_read_mmd(phydev, MMD_VEND2, 0xA402);
}
phydev->link = val & BIT(2) ? 1 : 0;
if (!phydev->link)
goto out;
// Read duplex status
- ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
- if (ret)
+ val = phy_read_mmd(phydev, MMD_VEND2, 0xA434);
+ if (val < 0)
goto out;
phydev->duplex = !!(val & BIT(3));
// Read speed
- ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
+ val = phy_read_mmd(phydev, MMD_VEND2, 0xA434);
switch (val & 0x0630) {
case 0x0000:
phydev->speed = SPEED_10;
{
int ret = 0;
u32 v;
- int port = phydev->mdio.addr;
pr_info("In %s\n", __func__);
- ret = read_mmd_phy(port, MMD_AN, 16, &v);
- if (ret)
+ v = phy_read_mmd(phydev, MMD_AN, 16);
+ if (v < 0)
goto out;
v |= BIT(5); // HD 10M
v |= BIT(7); // HD 100M
v |= BIT(8); // FD 100M
- ret = write_mmd_phy(port, MMD_AN, 16, v);
+ ret = phy_write_mmd(phydev, MMD_AN, 16, v);
// Allow 1GBit
- ret = read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
- if (ret)
+ v = phy_read_mmd(phydev, MMD_VEND2, 0xA412);
+ if (v < 0)
goto out;
v |= BIT(9); // FD 1000M
- ret = write_mmd_phy(port, MMD_VEND2, 0xA412, v);
- if (ret)
+ ret = phy_write_mmd(phydev, MMD_VEND2, 0xA412, v);
+ if (ret < 0)
goto out;
// Allow 2.5G
- ret = read_mmd_phy(port, MMD_AN, 32, &v);
- if (ret)
+ v = phy_read_mmd(phydev, MMD_AN, 32);
+ if (v < 0)
goto out;
v |= BIT(7);
- ret = write_mmd_phy(port, MMD_AN, 32, v);
+ ret = phy_write_mmd(phydev, MMD_AN, 32, v);
out:
return ret;
{
int ret = 0;
u32 v;
- int port = phydev->mdio.addr;
pr_debug("In %s\n", __func__);
if (phydev->autoneg == AUTONEG_ENABLE) {
if (ret)
goto out;
// AutoNegotiationEnable
- ret = read_mmd_phy(port, MMD_AN, 0, &v);
- if (ret)
+ v = phy_read_mmd(phydev, MMD_AN, 0);
+ if (v < 0)
goto out;
v |= BIT(12); // Enable AN
- ret = write_mmd_phy(port, MMD_AN, 0, v);
- if (ret)
+ ret = phy_write_mmd(phydev, MMD_AN, 0, v);
+ if (ret < 0)
goto out;
// RestartAutoNegotiation
- ret = read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
- if (ret)
+ v = phy_read_mmd(phydev, MMD_VEND2, 0xA400);
+ if (v < 0)
goto out;
v |= BIT(9);
- ret = write_mmd_phy(port, MMD_VEND2, 0xA400, v);
+ ret = phy_write_mmd(phydev, MMD_VEND2, 0xA400, v);
}
// TODO: ret = __genphy_config_aneg(phydev, ret);
pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
- read_mmd_phy(addr, MMD_AN, 60, &val);
+ val = phy_read_mmd(phydev, MMD_AN, 60);
if (e->eee_enabled) {
e->eee_enabled = !!(val & BIT(1));
if (!e->eee_enabled) {
- read_mmd_phy(addr, MMD_AN, 62, &val);
+ val = phy_read_mmd(phydev, MMD_AN, 62);
e->eee_enabled = !!(val & BIT(0));
}
}
poll_state = disable_polling(port);
// Remember aneg state
- read_mmd_phy(port, MMD_AN, 0, &val);
+ val = phy_read_mmd(phydev, MMD_AN, 0);
an_enabled = !!(val & BIT(12));
// Setup 100/1000MBit
- read_mmd_phy(port, MMD_AN, 60, &val);
+ val = phy_read_mmd(phydev, MMD_AN, 60);
if (e->eee_enabled)
val |= 0x6;
else
val &= 0x6;
- write_mmd_phy(port, MMD_AN, 60, val);
+ phy_write_mmd(phydev, MMD_AN, 60, val);
// Setup 2.5GBit
- read_mmd_phy(port, MMD_AN, 62, &val);
+ val = phy_read_mmd(phydev, MMD_AN, 62);
if (e->eee_enabled)
val |= 0x1;
else
val &= 0x1;
- write_mmd_phy(port, MMD_AN, 62, val);
+ phy_write_mmd(phydev, MMD_AN, 62, val);
// RestartAutoNegotiation
- read_mmd_phy(port, MMD_VEND2, 0xA400, &val);
+ val = phy_read_mmd(phydev, MMD_VEND2, 0xA400);
val |= BIT(9);
- write_mmd_phy(port, MMD_VEND2, 0xA400, val);
+ phy_write_mmd(phydev, MMD_VEND2, 0xA400, val);
resume_polling(poll_state);
static int rtl8390_configure_generic(struct phy_device *phydev)
{
- u32 val, phy_id;
int mac = phydev->mdio.addr;
+ u32 val, phy_id;
- read_phy(mac, 0, 2, &val);
+ val = phy_read(phydev, 2);
phy_id = val << 16;
- read_phy(mac, 0, 3, &val);
+ val = phy_read(phydev, 3);
phy_id |= val;
pr_debug("Phy on MAC %d: %x\n", mac, phy_id);
/* Read internal PHY ID */
- write_phy(mac, 31, 27, 0x0002);
- read_phy(mac, 31, 28, &val);
+ phy_write_paged(phydev, 31, 27, 0x0002);
+ val = phy_read_paged(phydev, 31, 28);
/* Internal RTL8218B, version 2 */
phydev_info(phydev, "Detected unknown %x\n", val);
u32 *rtl838x_6275B_intPhy_perport;
u32 *rtl8218b_6276B_hwEsd_perport;
-
- read_phy(mac, 0, 2, &val);
+ val = phy_read(phydev, 2);
phy_id = val << 16;
- read_phy(mac, 0, 3, &val);
+ val = phy_read(phydev, 3);
phy_id |= val;
pr_debug("Phy on MAC %d: %x\n", mac, phy_id);
/* Read internal PHY ID */
- write_phy(mac, 31, 27, 0x0002);
- read_phy(mac, 31, 28, &val);
+ phy_write_paged(phydev, 31, 27, 0x0002);
+ val = phy_read_paged(phydev, 31, 28);
if (val != 0x6275) {
phydev_err(phydev, "Expected internal RTL8218B, found PHY-ID %x\n", val);
return -1;
if (sw_r32(RTL838X_DMY_REG31) == 0x1)
ipd_flag = 1;
- read_phy(mac, 0, 0, &val);
- if (val & (1 << 11))
- rtl8380_int_phy_on_off(mac, true);
+ val = phy_read(phydev, 0);
+ if (val & BIT(11))
+ rtl8380_int_phy_on_off(phydev, true);
else
- rtl8380_phy_reset(mac);
+ rtl8380_phy_reset(phydev);
msleep(100);
/* Ready PHY for patch */
for (p = 0; p < 8; p++) {
- write_phy(mac + p, 0xfff, 0x1f, 0x0b82);
- write_phy(mac + p, 0xfff, 0x10, 0x0010);
+ phy_package_port_write_paged(phydev, p, 0xfff, 0x1f, 0x0b82);
+ phy_package_port_write_paged(phydev, p, 0xfff, 0x10, 0x0010);
}
msleep(500);
for (p = 0; p < 8; p++) {
for (i = 0; i < 100 ; i++) {
- read_phy(mac + p, 0x0b80, 0x10, &val);
+ val = phy_package_port_read_paged(phydev, p, 0x0b80, 0x10);
if (val & 0x40)
break;
}
for (p = 0; p < 8; p++) {
i = 0;
while (rtl838x_6275B_intPhy_perport[i * 2]) {
- write_phy(mac + p, 0xfff,
+ phy_package_port_write_paged(phydev, p, 0xfff,
rtl838x_6275B_intPhy_perport[i * 2],
rtl838x_6275B_intPhy_perport[i * 2 + 1]);
i++;
}
i = 0;
while (rtl8218b_6276B_hwEsd_perport[i * 2]) {
- write_phy(mac + p, 0xfff,
+ phy_package_port_write_paged(phydev, p, 0xfff,
rtl8218b_6276B_hwEsd_perport[i * 2],
rtl8218b_6276B_hwEsd_perport[i * 2 + 1]);
i++;
phydev_err(phydev, "External RTL8218B must have PHY-IDs 0 or 16!\n");
return -1;
}
- read_phy(mac, 0, 2, &val);
+ val = phy_read(phydev, 2);
phy_id = val << 16;
- read_phy(mac, 0, 3, &val);
+ val = phy_read(phydev, 3);
phy_id |= val;
pr_info("Phy on MAC %d: %x\n", mac, phy_id);
/* Read internal PHY ID */
- write_phy(mac, 31, 27, 0x0002);
- read_phy(mac, 31, 28, &val);
+ phy_write_paged(phydev, 31, 27, 0x0002);
+ val = phy_read_paged(phydev, 31, 28);
if (val != 0x6276) {
phydev_err(phydev, "Expected external RTL8218B, found PHY-ID %x\n", val);
return -1;
rtl8380_rtl8218b_perport = (void *)h + sizeof(struct fw_header)
+ h->parts[2].start;
- read_phy(mac, 0, 0, &val);
+ val = phy_read(phydev, 0);
if (val & (1 << 11))
- rtl8380_int_phy_on_off(mac, true);
+ rtl8380_int_phy_on_off(phydev, true);
else
- rtl8380_phy_reset(mac);
+ rtl8380_phy_reset(phydev);
+
msleep(100);
/* Get Chip revision */
- write_phy(mac, 0xfff, 0x1f, 0x0);
- write_phy(mac, 0xfff, 0x1b, 0x4);
- read_phy(mac, 0xfff, 0x1c, &val);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0);
+ phy_write_paged(phydev, 0xfff, 0x1b, 0x4);
+ val = phy_read_paged(phydev, 0xfff, 0x1c);
+
+ phydev_info(phydev, "Detected chip revision %04x\n", val);
i = 0;
while (rtl8380_rtl8218b_perchip[i * 3]
&& rtl8380_rtl8218b_perchip[i * 3 + 1]) {
- write_phy(mac + rtl8380_rtl8218b_perchip[i * 3],
+ phy_package_port_write_paged(phydev, rtl8380_rtl8218b_perchip[i * 3],
0xfff, rtl8380_rtl8218b_perchip[i * 3 + 1],
rtl8380_rtl8218b_perchip[i * 3 + 2]);
i++;
/* Enable PHY */
for (i = 0; i < 8; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0000);
- write_phy(mac + i, 0xfff, 0x00, 0x1140);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1f, 0x0000);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x00, 0x1140);
}
mdelay(100);
/* Request patch */
for (i = 0; i < 8; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0b82);
- write_phy(mac + i, 0xfff, 0x10, 0x0010);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1f, 0x0b82);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x10, 0x0010);
}
+
mdelay(300);
/* Verify patch readiness */
for (i = 0; i < 8; i++) {
for (l = 0; l < 100; l++) {
- read_phy(mac + i, 0xb80, 0x10, &val);
+ val = phy_package_port_read_paged(phydev, i, 0xb80, 0x10);
if (val & 0x40)
break;
}
}
/* Use Broadcast ID method for patching */
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0008);
- write_phy(mac, 0xfff, 0x1f, 0x0266);
- write_phy(mac, 0xfff, 0x16, 0xff00 + mac);
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0008);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0266);
+ phy_write_paged(phydev, 0xfff, 0x16, 0xff00 + mac);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0000);
mdelay(1);
- write_phy(mac, 0xfff, 30, 8);
- write_phy(mac, 0x26e, 17, 0xb);
- write_phy(mac, 0x26e, 16, 0x2);
+ phy_write_paged(phydev, 0xfff, 30, 8);
+ phy_write_paged(phydev, 0x26e, 17, 0xb);
+ phy_write_paged(phydev, 0x26e, 16, 0x2);
mdelay(1);
- read_phy(mac, 0x26e, 19, &ipd);
- write_phy(mac, 0, 30, 0);
- ipd = (ipd >> 4) & 0xf;
+ ipd = phy_read_paged(phydev, 0x26e, 19);
+ phy_write_paged(phydev, 0, 30, 0);
+ ipd = (ipd >> 4) & 0xf; /* unused ? */
i = 0;
while (rtl8218B_6276B_rtl8380_perport[i * 2]) {
- write_phy(mac, 0xfff, rtl8218B_6276B_rtl8380_perport[i * 2],
+ phy_write_paged(phydev, 0xfff, rtl8218B_6276B_rtl8380_perport[i * 2],
rtl8218B_6276B_rtl8380_perport[i * 2 + 1]);
i++;
}
/*Disable broadcast ID*/
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0008);
- write_phy(mac, 0xfff, 0x1f, 0x0266);
- write_phy(mac, 0xfff, 0x16, 0x00 + mac);
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0008);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0266);
+ phy_write_paged(phydev, 0xfff, 0x16, 0x00 + mac);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0000);
mdelay(1);
return 0;
return phydev->phy_id == PHY_ID_RTL8218B_E;
}
-static int rtl8218b_read_mmd(struct phy_device *phydev,
- int devnum, u16 regnum)
-{
- int ret;
- u32 val;
- int addr = phydev->mdio.addr;
-
- ret = read_mmd_phy(addr, devnum, regnum, &val);
- if (ret)
- return ret;
- return val;
-}
-
-static int rtl8218b_write_mmd(struct phy_device *phydev,
- int devnum, u16 regnum, u16 val)
+static void rtl8380_rtl8214fc_media_set(struct phy_device *phydev, bool set_fibre)
{
- int addr = phydev->mdio.addr;
-
- return rtl838x_write_mmd_phy(addr, devnum, regnum, val);
-}
-
-static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
-{
- int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
- int err;
- u32 val;
-
- err = read_mmd_phy(port, devnum, regnum, &val);
- if (err)
- return err;
- return val;
-}
-
-static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val)
-{
- int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
-
- return write_mmd_phy(port, devnum, regnum, val);
-}
+ int mac = phydev->mdio.addr;
-static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre)
-{
- int base = mac - (mac % 4);
static int reg[] = {16, 19, 20, 21};
int val, media, power;
pr_info("%s: port %d, set_fibre: %d\n", __func__, mac, set_fibre);
- write_phy(base, 0xfff, 29, 8);
- read_phy(base, 0x266, reg[mac % 4], &val);
+ phy_package_write_paged(phydev, 0xfff, 29, 8);
+ val = phy_package_read_paged(phydev, 0x266, reg[mac % 4]);
media = (val >> 10) & 0x3;
pr_info("Current media %x\n", media);
if (media & 0x2) {
pr_info("Powering off COPPER\n");
- write_phy(base, 0xfff, 29, 1);
+ phy_package_write_paged(phydev, 0xfff, 29, 1);
/* Ensure power is off */
- read_phy(base, 0xa40, 16, &power);
+ power = phy_package_read_paged(phydev, 0xa40, 16);
if (!(power & (1 << 11)))
- write_phy(base, 0xa40, 16, power | (1 << 11));
+ phy_package_write_paged(phydev, 0xa40, 16, power | (1 << 11));
} else {
pr_info("Powering off FIBRE");
- write_phy(base, 0xfff, 29, 3);
+ phy_package_write_paged(phydev, 0xfff, 29, 3);
/* Ensure power is off */
- read_phy(base, 0xa40, 16, &power);
+ power = phy_package_read_paged(phydev, 0xa40, 16);
if (!(power & (1 << 11)))
- write_phy(base, 0xa40, 16, power | (1 << 11));
+ phy_package_write_paged(phydev, 0xa40, 16, power | (1 << 11));
}
if (set_fibre) {
val |= 1 << 10;
val |= 1 << 11;
}
- write_phy(base, 0xfff, 29, 8);
- write_phy(base, 0x266, reg[mac % 4], val);
- write_phy(base, 0xfff, 29, 0);
+ phy_package_write_paged(phydev, 0xfff, 29, 8);
+ phy_package_write_paged(phydev, 0x266, reg[mac % 4], val);
+ phy_package_write_paged(phydev, 0xfff, 29, 0);
if (set_fibre) {
pr_info("Powering on FIBRE");
- write_phy(base, 0xfff, 29, 3);
+ phy_package_write_paged(phydev, 0xfff, 29, 3);
/* Ensure power is off */
- read_phy(base, 0xa40, 16, &power);
+ power = phy_package_read_paged(phydev, 0xa40, 16);
if (power & (1 << 11))
- write_phy(base, 0xa40, 16, power & ~(1 << 11));
+ phy_package_write_paged(phydev, 0xa40, 16, power & ~(1 << 11));
} else {
pr_info("Powering on COPPER\n");
- write_phy(base, 0xfff, 29, 1);
+ phy_package_write_paged(phydev, 0xfff, 29, 1);
/* Ensure power is off */
- read_phy(base, 0xa40, 16, &power);
+ power = phy_package_read_paged(phydev, 0xa40, 16);
if (power & (1 << 11))
- write_phy(base, 0xa40, 16, power & ~(1 << 11));
+ phy_package_write_paged(phydev, 0xa40, 16, power & ~(1 << 11));
}
- write_phy(base, 0xfff, 29, 0);
+ phy_package_write_paged(phydev, 0xfff, 29, 0);
}
-static bool rtl8380_rtl8214fc_media_is_fibre(int mac)
+static bool rtl8380_rtl8214fc_media_is_fibre(struct phy_device *phydev)
{
- int base = mac - (mac % 4);
+ int mac = phydev->mdio.addr;
+
static int reg[] = {16, 19, 20, 21};
u32 val;
- write_phy(base, 0xfff, 29, 8);
- read_phy(base, 0x266, reg[mac % 4], &val);
- write_phy(base, 0xfff, 29, 0);
+ phy_package_write_paged(phydev, 0xfff, 29, 8);
+ val = phy_package_read_paged(phydev, 0x266, reg[mac % 4]);
+ phy_package_write_paged(phydev, 0xfff, 29, 0);
if (val & (1 << 11))
return false;
return true;
pr_debug("%s port %d to %d\n", __func__, addr, port);
- rtl8380_rtl8214fc_media_set(addr, is_fibre);
+ rtl8380_rtl8214fc_media_set(phydev, is_fibre);
return 0;
}
int addr = phydev->mdio.addr;
pr_debug("%s: port %d\n", __func__, addr);
- if (rtl8380_rtl8214fc_media_is_fibre(addr))
+ if (rtl8380_rtl8214fc_media_is_fibre(phydev))
return PORT_FIBRE;
return PORT_MII;
}
* but the only way that works since the kernel first enables EEE in the MAC
* and then sets up the PHY. The MAC-based approach would require the oppsite.
*/
-void rtl8218d_eee_set(int port, bool enable)
+void rtl8218d_eee_set(struct phy_device *phydev, bool enable)
{
u32 val;
bool an_enabled;
- pr_debug("In %s %d, enable %d\n", __func__, port, enable);
+ pr_debug("In %s %d, enable %d\n", __func__, phydev->mdio.addr, enable);
/* Set GPHY page to copper */
- write_phy(port, 0xa42, 30, 0x0001);
+ phy_write_paged(phydev, 0xa42, 30, 0x0001);
- read_phy(port, 0, 0, &val);
+ val = phy_read(phydev, 0);
an_enabled = val & BIT(12);
/* Enable 100M (bit 1) / 1000M (bit 2) EEE */
- read_mmd_phy(port, 7, 60, &val);
+ val = phy_read_mmd(phydev, 7, 60);
val |= BIT(2) | BIT(1);
- write_mmd_phy(port, 7, 60, enable ? 0x6 : 0);
+ phy_write_mmd(phydev, 7, 60, enable ? 0x6 : 0);
/* 500M EEE ability */
- read_phy(port, 0xa42, 20, &val);
+ val = phy_read_paged(phydev, 0xa42, 20);
if (enable)
val |= BIT(7);
else
val &= ~BIT(7);
- write_phy(port, 0xa42, 20, val);
+ phy_write_paged(phydev, 0xa42, 20, val);
/* Restart AN if enabled */
if (an_enabled) {
- read_phy(port, 0, 0, &val);
+ val = phy_read(phydev, 0);
val |= BIT(9);
- write_phy(port, 0, 0, val);
+ phy_write(phydev, 0, val);
}
/* GPHY page back to auto*/
- write_phy(port, 0xa42, 30, 0);
+ phy_write_paged(phydev, 0xa42, 30, 0);
}
static int rtl8218b_get_eee(struct phy_device *phydev,
pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
/* Set GPHY page to copper */
- write_phy(addr, 0xa42, 29, 0x0001);
+ phy_write_paged(phydev, 0xa42, 29, 0x0001);
- read_phy(addr, 7, 60, &val);
+ val = phy_read_paged(phydev, 7, 60);
if (e->eee_enabled) {
// Verify vs MAC-based EEE
e->eee_enabled = !!(val & BIT(7));
if (!e->eee_enabled) {
- read_phy(addr, 0x0A43, 25, &val);
+ val = phy_read_paged(phydev, 0x0A43, 25);
e->eee_enabled = !!(val & BIT(4));
}
}
pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
/* GPHY page to auto */
- write_phy(addr, 0xa42, 29, 0x0000);
+ phy_write_paged(phydev, 0xa42, 29, 0x0000);
return 0;
}
pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
/* Set GPHY page to copper */
- write_phy(addr, 0xa42, 30, 0x0001);
+ phy_write_paged(phydev, 0xa42, 30, 0x0001);
- read_phy(addr, 7, 60, &val);
+ val = phy_read_paged(phydev, 7, 60);
if (e->eee_enabled)
e->eee_enabled = !!(val & BIT(7));
pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
/* GPHY page to auto */
- write_phy(addr, 0xa42, 30, 0x0000);
+ phy_write_paged(phydev, 0xa42, 30, 0x0000);
return 0;
}
pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled);
- if (rtl8380_rtl8214fc_media_is_fibre(port)) {
+ if (rtl8380_rtl8214fc_media_is_fibre(phydev)) {
netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port);
return -ENOTSUPP;
}
poll_state = disable_polling(port);
/* Set GPHY page to copper */
- write_phy(port, 0xa42, 29, 0x0001);
+ phy_write_paged(phydev, 0xa42, 29, 0x0001);
// Get auto-negotiation status
- read_phy(port, 0, 0, &val);
+ val = phy_read(phydev, 0);
an_enabled = val & BIT(12);
pr_info("%s: aneg: %d\n", __func__, an_enabled);
- read_phy(port, 0x0A43, 25, &val);
+ val = phy_read_paged(phydev, 0x0A43, 25);
val &= ~BIT(5); // Use MAC-based EEE
- write_phy(port, 0x0A43, 25, val);
+ phy_write_paged(phydev, 0x0A43, 25, val);
/* Enable 100M (bit 1) / 1000M (bit 2) EEE */
- write_phy(port, 7, 60, e->eee_enabled ? 0x6 : 0);
+ phy_write_paged(phydev, 7, 60, e->eee_enabled ? 0x6 : 0);
/* 500M EEE ability */
- read_phy(port, 0xa42, 20, &val);
+ val = phy_read_paged(phydev, 0xa42, 20);
if (e->eee_enabled)
val |= BIT(7);
else
val &= ~BIT(7);
- write_phy(port, 0xa42, 20, val);
+
+ phy_write_paged(phydev, 0xa42, 20, val);
/* Restart AN if enabled */
if (an_enabled) {
pr_info("%s: doing aneg\n", __func__);
- read_phy(port, 0, 0, &val);
+ val = phy_read(phydev, 0);
val |= BIT(9);
- write_phy(port, 0, 0, val);
+ phy_write(phydev, 0, val);
}
/* GPHY page back to auto*/
- write_phy(port, 0xa42, 29, 0);
+ phy_write_paged(phydev, 0xa42, 29, 0);
resume_polling(poll_state);
int addr = phydev->mdio.addr;
pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled);
- if (rtl8380_rtl8214fc_media_is_fibre(addr)) {
+ if (rtl8380_rtl8214fc_media_is_fibre(phydev)) {
netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr);
return -ENOTSUPP;
}
poll_state = disable_polling(port);
/* Set GPHY page to copper */
- write_phy(port, 0, 30, 0x0001);
- read_phy(port, 0, 0, &val);
+ phy_write(phydev, 30, 0x0001);
+ val = phy_read(phydev, 0);
an_enabled = val & BIT(12);
if (e->eee_enabled) {
/* 100/1000M EEE Capability */
- write_phy(port, 0, 13, 0x0007);
- write_phy(port, 0, 14, 0x003C);
- write_phy(port, 0, 13, 0x4007);
- write_phy(port, 0, 14, 0x0006);
+ phy_write(phydev, 13, 0x0007);
+ phy_write(phydev, 14, 0x003C);
+ phy_write(phydev, 13, 0x4007);
+ phy_write(phydev, 14, 0x0006);
- read_phy(port, 0x0A43, 25, &val);
+ val = phy_read_paged(phydev, 0x0A43, 25);
val |= BIT(4);
- write_phy(port, 0x0A43, 25, val);
+ phy_write_paged(phydev, 0x0A43, 25, val);
} else {
/* 100/1000M EEE Capability */
- write_phy(port, 0, 13, 0x0007);
- write_phy(port, 0, 14, 0x003C);
- write_phy(port, 0, 13, 0x0007);
- write_phy(port, 0, 14, 0x0000);
+ phy_write(phydev, 13, 0x0007);
+ phy_write(phydev, 14, 0x003C);
+ phy_write(phydev, 13, 0x0007);
+ phy_write(phydev, 14, 0x0000);
- read_phy(port, 0x0A43, 25, &val);
+ val = phy_read_paged(phydev, 0x0A43, 25);
val &= ~BIT(4);
- write_phy(port, 0x0A43, 25, val);
+ phy_write_paged(phydev, 0x0A43, 25, val);
}
/* Restart AN if enabled */
if (an_enabled) {
- read_phy(port, 0, 0, &val);
+ val = phy_read(phydev, 0);
val |= BIT(9);
- write_phy(port, 0, 0, val);
+ phy_write(phydev, 0, val);
}
/* GPHY page back to auto*/
- write_phy(port, 0xa42, 30, 0);
+ phy_write_paged(phydev, 0xa42, 30, 0);
pr_info("%s done\n", __func__);
resume_polling(poll_state);
poll_state = disable_polling(addr);
- rtl8218d_eee_set(addr, (bool) e->eee_enabled);
+ rtl8218d_eee_set(phydev, (bool) e->eee_enabled);
resume_polling(poll_state);
u32 phy_id, val;
int mac = phydev->mdio.addr;
- read_phy(mac, 0, 2, &val);
+ val = phy_read(phydev, 2);
phy_id = val << 16;
- read_phy(mac, 0, 3, &val);
+ val = phy_read(phydev, 3);
phy_id |= val;
pr_debug("Phy on MAC %d: %x\n", mac, phy_id);
phydev_info(phydev, "Detected external RTL8214C\n");
/* GPHY auto conf */
- write_phy(mac, 0xa42, 29, 0);
+ phy_write_paged(phydev, 0xa42, 29, 0);
return 0;
}
u32 *rtl8380_rtl8214fc_perchip;
u32 *rtl8380_rtl8214fc_perport;
- read_phy(mac, 0, 2, &val);
+ val = phy_read(phydev, 2);
phy_id = val << 16;
- read_phy(mac, 0, 3, &val);
+ val = phy_read(phydev, 3);
phy_id |= val;
pr_debug("Phy on MAC %d: %x\n", mac, phy_id);
/* Read internal PHY id */
- write_phy(mac, 0, 30, 0x0001);
- write_phy(mac, 0, 31, 0x0a42);
- write_phy(mac, 31, 27, 0x0002);
- read_phy(mac, 31, 28, &val);
+ phy_write_paged(phydev, 0, 30, 0x0001);
+ phy_write_paged(phydev, 0, 31, 0x0a42);
+ phy_write_paged(phydev, 31, 27, 0x0002);
+ val = phy_read_paged(phydev, 31, 28);
if (val != 0x6276) {
phydev_err(phydev, "Expected external RTL8214FC, found PHY-ID %x\n", val);
return -1;
+ h->parts[1].start;
/* detect phy version */
- write_phy(mac, 0xfff, 27, 0x0004);
- read_phy(mac, 0xfff, 28, &val);
+ phy_write_paged(phydev, 0xfff, 27, 0x0004);
+ val = phy_read_paged(phydev, 0xfff, 28);
- read_phy(mac, 0, 16, &val);
+ val = phy_read(phydev, 16);
if (val & (1 << 11))
- rtl8380_rtl8214fc_on_off(mac, true);
+ rtl8380_rtl8214fc_on_off(phydev, true);
else
- rtl8380_phy_reset(mac);
+ rtl8380_phy_reset(phydev);
msleep(100);
- write_phy(mac, 0, 30, 0x0001);
+ phy_write_paged(phydev, 0, 30, 0x0001);
i = 0;
while (rtl8380_rtl8214fc_perchip[i * 3]
if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x1f)
page = rtl8380_rtl8214fc_perchip[i * 3 + 2];
if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x13 && page == 0x260) {
- read_phy(mac + rtl8380_rtl8214fc_perchip[i * 3], 0x260, 13, &val);
+ val = phy_read_paged(phydev, 0x260, 13);
val = (val & 0x1f00) | (rtl8380_rtl8214fc_perchip[i * 3 + 2]
& 0xe0ff);
- write_phy(mac + rtl8380_rtl8214fc_perchip[i * 3],
- 0xfff, rtl8380_rtl8214fc_perchip[i * 3 + 1], val);
+ phy_write_paged(phydev, 0xfff,
+ rtl8380_rtl8214fc_perchip[i * 3 + 1], val);
} else {
- write_phy(mac + rtl8380_rtl8214fc_perchip[i * 3],
- 0xfff, rtl8380_rtl8214fc_perchip[i * 3 + 1],
- rtl8380_rtl8214fc_perchip[i * 3 + 2]);
+ phy_write_paged(phydev, 0xfff,
+ rtl8380_rtl8214fc_perchip[i * 3 + 1],
+ rtl8380_rtl8214fc_perchip[i * 3 + 2]);
}
i++;
}
/* Force copper medium */
for (i = 0; i < 4; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0000);
- write_phy(mac + i, 0xfff, 0x1e, 0x0001);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1f, 0x0000);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1e, 0x0001);
}
/* Enable PHY */
for (i = 0; i < 4; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0000);
- write_phy(mac + i, 0xfff, 0x00, 0x1140);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1f, 0x0000);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x00, 0x1140);
}
mdelay(100);
/* Disable Autosensing */
for (i = 0; i < 4; i++) {
for (l = 0; l < 100; l++) {
- read_phy(mac + i, 0x0a42, 0x10, &val);
+ val = phy_package_port_read_paged(phydev, i, 0x0a42, 0x10);
if ((val & 0x7) >= 3)
break;
}
/* Request patch */
for (i = 0; i < 4; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0b82);
- write_phy(mac + i, 0xfff, 0x10, 0x0010);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x1f, 0x0b82);
+ phy_package_port_write_paged(phydev, i, 0xfff, 0x10, 0x0010);
}
mdelay(300);
/* Verify patch readiness */
for (i = 0; i < 4; i++) {
for (l = 0; l < 100; l++) {
- read_phy(mac + i, 0xb80, 0x10, &val);
+ val = phy_package_port_read_paged(phydev, i, 0xb80, 0x10);
if (val & 0x40)
break;
}
return -1;
}
}
-
/* Use Broadcast ID method for patching */
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0008);
- write_phy(mac, 0xfff, 0x1f, 0x0266);
- write_phy(mac, 0xfff, 0x16, 0xff00 + mac);
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0008);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0266);
+ phy_write_paged(phydev, 0xfff, 0x16, 0xff00 + mac);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0000);
mdelay(1);
i = 0;
while (rtl8380_rtl8214fc_perport[i * 2]) {
- write_phy(mac, 0xfff, rtl8380_rtl8214fc_perport[i * 2],
+ phy_write_paged(phydev, 0xfff, rtl8380_rtl8214fc_perport[i * 2],
rtl8380_rtl8214fc_perport[i * 2 + 1]);
i++;
}
/*Disable broadcast ID*/
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0008);
- write_phy(mac, 0xfff, 0x1f, 0x0266);
- write_phy(mac, 0xfff, 0x16, 0x00 + mac);
- write_phy(mac, 0xfff, 0x1f, 0x0000);
- write_phy(mac, 0xfff, 0x1d, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0008);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0266);
+ phy_write_paged(phydev, 0xfff, 0x16, 0x00 + mac);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1d, 0x0000);
mdelay(1);
/* Auto medium selection */
for (i = 0; i < 4; i++) {
- write_phy(mac + i, 0xfff, 0x1f, 0x0000);
- write_phy(mac + i, 0xfff, 0x1e, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1f, 0x0000);
+ phy_write_paged(phydev, 0xfff, 0x1e, 0x0000);
}
return 0;
static int rtl8214fc_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
+ int ret = 0;
/* 839x has internal SerDes */
if (soc_info.id == 0x8393)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8214FC";
-
/* All base addresses of the PHYs start at multiples of 8 */
+ devm_phy_package_join(dev, phydev, addr & (~7),
+ sizeof(struct rtl83xx_shared_private));
+
if (!(addr % 8)) {
- /* Configuration must be done whil patching still possible */
- return rtl8380_configure_rtl8214fc(phydev);
+ struct rtl83xx_shared_private *shared = phydev->shared->priv;
+ shared->name = "RTL8214FC";
+ /* Configuration must be done while patching still possible */
+ ret = rtl8380_configure_rtl8214fc(phydev);
+ if (ret)
+ return ret;
}
+
return 0;
}
static int rtl8214c_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8214C";
-
/* All base addresses of the PHYs start at multiples of 8 */
+ devm_phy_package_join(dev, phydev, addr & (~7),
+ sizeof(struct rtl83xx_shared_private));
+
if (!(addr % 8)) {
+ struct rtl83xx_shared_private *shared = phydev->shared->priv;
+ shared->name = "RTL8214C";
/* Configuration must be done whil patching still possible */
return rtl8380_configure_rtl8214c(phydev);
}
static int rtl8218b_ext_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8218B (external)";
-
/* All base addresses of the PHYs start at multiples of 8 */
- if (!(addr % 8) && soc_info.family == RTL8380_FAMILY_ID) {
- /* Configuration must be done while patching still possible */
- return rtl8380_configure_ext_rtl8218b(phydev);
+ devm_phy_package_join(dev, phydev, addr & (~7),
+ sizeof(struct rtl83xx_shared_private));
+
+ if (!(addr % 8)) {
+ struct rtl83xx_shared_private *shared = phydev->shared->priv;
+ shared->name = "RTL8218B (external)";
+ if (soc_info.family == RTL8380_FAMILY_ID) {
+ /* Configuration must be done while patching still possible */
+ return rtl8380_configure_ext_rtl8218b(phydev);
+ }
}
+
return 0;
}
static int rtl8218b_int_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
if (soc_info.family != RTL8380_FAMILY_ID)
if (addr >= 24)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8218B (internal)";
-
+ pr_debug("%s: id: %d\n", __func__, addr);
/* All base addresses of the PHYs start at multiples of 8 */
+ devm_phy_package_join(dev, phydev, addr & (~7),
+ sizeof(struct rtl83xx_shared_private));
+
if (!(addr % 8)) {
+ struct rtl83xx_shared_private *shared = phydev->shared->priv;
+ shared->name = "RTL8218B (internal)";
/* Configuration must be done while patching still possible */
return rtl8380_configure_int_rtl8218b(phydev);
}
+
return 0;
}
static int rtl8218d_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
pr_debug("%s: id: %d\n", __func__, addr);
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8218D";
+ /* All base addresses of the PHYs start at multiples of 8 */
+ devm_phy_package_join(dev, phydev, addr & (~7),
+ sizeof(struct rtl83xx_shared_private));
/* All base addresses of the PHYs start at multiples of 8 */
if (!(addr % 8)) {
+ struct rtl83xx_shared_private *shared = phydev->shared->priv;
+ shared->name = "RTL8218D";
/* Configuration must be done while patching still possible */
// TODO: return configure_rtl8218d(phydev);
}
return 0;
}
-static int rtl8226_phy_probe(struct phy_device *phydev)
-{
- struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
- int addr = phydev->mdio.addr;
-
- pr_info("%s: id: %d\n", __func__, addr);
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8226";
-
- return 0;
-}
-
static int rtl838x_serdes_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
if (soc_info.family != RTL8380_FAMILY_ID)
if (addr < 24)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8380 Serdes";
-
/* On the RTL8380M, PHYs 24-27 connect to the internal SerDes */
if (soc_info.id == 0x8380) {
if (addr == 24)
static int rtl8393_serdes_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
pr_info("%s: id: %d\n", __func__, addr);
if (addr < 24)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8393 Serdes";
return rtl8390_configure_serdes(phydev);
}
static int rtl8390_serdes_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
int addr = phydev->mdio.addr;
if (soc_info.family != RTL8390_FAMILY_ID)
if (addr < 24)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL8390 Serdes";
return rtl8390_configure_generic(phydev);
}
static int rtl9300_serdes_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->mdio.dev;
- struct rtl838x_phy_priv *priv;
-
if (soc_info.family != RTL9300_FAMILY_ID)
return -ENODEV;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->name = "RTL9300 Serdes";
phydev_info(phydev, "Detected internal RTL9300 Serdes\n");
return rtl9300_configure_serdes(phydev);
PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C),
.name = "Realtek RTL8214C",
.features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.match_phy_device = rtl8214c_match_phy_device,
.probe = rtl8214c_phy_probe,
.suspend = genphy_suspend,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC),
.name = "Realtek RTL8214FC",
.features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.match_phy_device = rtl8214fc_match_phy_device,
.probe = rtl8214fc_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8218b_read_mmd,
- .write_mmd = rtl8218b_write_mmd,
.set_port = rtl8214fc_set_port,
.get_port = rtl8214fc_get_port,
.set_eee = rtl8214fc_set_eee,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E),
.name = "Realtek RTL8218B (external)",
.features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.match_phy_device = rtl8218b_ext_match_phy_device,
.probe = rtl8218b_ext_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8218b_read_mmd,
- .write_mmd = rtl8218b_write_mmd,
.set_eee = rtl8218b_set_eee,
.get_eee = rtl8218b_get_eee,
},
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218D),
.name = "REALTEK RTL8218D",
.features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl8218d_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8221B),
.name = "REALTEK RTL8221B",
.features = PHY_GBIT_FEATURES,
- .probe = rtl8226_phy_probe,
+ .flags = PHY_HAS_REALTEK_PAGES,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8226_read_mmd,
- .write_mmd = rtl8226_write_mmd,
.read_page = rtl8226_read_page,
.write_page = rtl8226_write_page,
.read_status = rtl8226_read_status,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8226),
.name = "REALTEK RTL8226",
.features = PHY_GBIT_FEATURES,
- .probe = rtl8226_phy_probe,
+ .flags = PHY_HAS_REALTEK_PAGES,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8226_read_mmd,
- .write_mmd = rtl8226_write_mmd,
.read_page = rtl8226_read_page,
.write_page = rtl8226_write_page,
.read_status = rtl8226_read_status,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
.name = "Realtek RTL8218B (internal)",
.features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl8218b_int_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8218b_read_mmd,
- .write_mmd = rtl8218b_write_mmd,
.set_eee = rtl8218b_set_eee,
.get_eee = rtl8218b_get_eee,
},
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
.name = "Realtek RTL8380 SERDES",
.features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl838x_serdes_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
- .read_mmd = rtl8218b_read_mmd,
- .write_mmd = rtl8218b_write_mmd,
.read_status = rtl8380_read_status,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_RTL8393_I),
.name = "Realtek RTL8393 SERDES",
.features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl8393_serdes_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
PHY_ID_MATCH_MODEL(PHY_ID_RTL8390_GENERIC),
.name = "Realtek RTL8390 Generic",
.features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl8390_serdes_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
PHY_ID_MATCH_MODEL(PHY_ID_RTL9300_I),
.name = "REALTEK RTL9300 SERDES",
.features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_HAS_REALTEK_PAGES,
.probe = rtl9300_serdes_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
--- /dev/null
+From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 3 Feb 2022 16:38:50 +0000
+Subject: [PATCH] net: mdio: support hardware-assisted indirect access
+
+MDIO controllers found in Switch-SoCs can offload some MDIO operations
+to the hardware:
+ * MMD register access via Clause-22
+ Instead of using multiple operations to access MMD registers via
+ MII register MII_MMD_CTRL and MII_MMD_DATA some controllers
+ allow transparent access to MMD PHY registers.
+
+ * paged MII register access
+ Some PHYs (namely RealTek and Vitesse) use vendor-defined MII
+ register 0x1f for paged access. Some MDIO host controllers support
+ transparent paged access when used with such PHYs.
+
+ * add convenience accessors to fully support paged access also on
+ multi-PHY packages (like the embedded PHYs in RTL83xx):
+ phy_package_read_paged and phy_package_write_paged
+ phy_package_port_read and phy_package_port_write
+ phy_package_port_read_paged and phy_package_port_write_paged
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/mdio_bus.c | 335 ++++++++++++++++++++++++++++++++++++-
+ drivers/net/phy/phy-core.c | 66 +++++++-
+ include/linux/mdio.h | 59 +++++++
+ include/linux/phy.h | 129 ++++++++++++++
+ include/uapi/linux/mii.h | 1 +
+ 5 files changed, 580 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -734,6 +734,32 @@ out:
+ }
+
+ /**
++ * __mdiobus_select_page - Unlocked version of the mdiobus_select_page function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: register page to select
++ *
++ * Selects a MDIO bus register page. Caller must hold the mdio bus lock.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
++{
++ lockdep_assert_held_once(&bus->mdio_lock);
++
++ if (bus->selected_page[addr] == page)
++ return 0;
++
++ bus->selected_page[addr] = page;
++ if (bus->read_paged)
++ return 0;
++
++ return bus->write(bus, addr, MII_MAINPAGE, page);
++
++}
++EXPORT_SYMBOL(__mdiobus_select_page);
++
++/**
+ * __mdiobus_read - Unlocked version of the mdiobus_read function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -749,7 +775,10 @@ int __mdiobus_read(struct mii_bus *bus,
+
+ WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+
+- retval = bus->read(bus, addr, regnum);
++ if (bus->read_paged)
++ retval = bus->read_paged(bus, addr, bus->selected_page[addr], regnum);
++ else
++ retval = bus->read(bus, addr, regnum);
+
+ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
+ mdiobus_stats_acct(&bus->stats[addr], true, retval);
+@@ -759,6 +788,40 @@ int __mdiobus_read(struct mii_bus *bus,
+ EXPORT_SYMBOL(__mdiobus_read);
+
+ /**
++ * __mdiobus_read_paged - Unlocked version of the mdiobus_read_paged function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to read
++ *
++ * Read a MDIO bus register. Caller must hold the mdio bus lock.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
++{
++ int retval;
++ int oldpage;
++
++ lockdep_assert_held_once(&bus->mdio_lock);
++
++ if (bus->read_paged) {
++ retval = bus->read_paged(bus, addr, page, regnum);
++ } else {
++ oldpage = bus->selected_page[addr];
++ __mdiobus_select_page(bus, addr, page);
++ retval = bus->read(bus, addr, regnum);
++ __mdiobus_select_page(bus, addr, oldpage);
++ }
++
++ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
++ mdiobus_stats_acct(&bus->stats[addr], true, retval);
++
++ return retval;
++}
++EXPORT_SYMBOL(__mdiobus_read_paged);
++
++/**
+ * __mdiobus_write - Unlocked version of the mdiobus_write function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -775,7 +838,10 @@ int __mdiobus_write(struct mii_bus *bus,
+
+ WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+
+- err = bus->write(bus, addr, regnum, val);
++ if (bus->write_paged)
++ err = bus->write_paged(bus, addr, bus->selected_page[addr], regnum, val);
++ else
++ err = bus->write(bus, addr, regnum, val);
+
+ trace_mdio_access(bus, 0, addr, regnum, val, err);
+ mdiobus_stats_acct(&bus->stats[addr], false, err);
+@@ -785,6 +851,39 @@ int __mdiobus_write(struct mii_bus *bus,
+ EXPORT_SYMBOL(__mdiobus_write);
+
+ /**
++ * __mdiobus_write_paged - Unlocked version of the mdiobus_write_paged function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * Write a MDIO bus register. Caller must hold the mdio bus lock.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
++{
++ int err, oldpage;
++
++ lockdep_assert_held_once(&bus->mdio_lock);
++
++ if (bus->write_paged) {
++ err = bus->write_paged(bus, addr, page, regnum, val);
++ } else {
++ oldpage = bus->selected_page[addr];
++ __mdiobus_select_page(bus, addr, page);
++ err = bus->write(bus, addr, regnum, val);
++ __mdiobus_select_page(bus, addr, oldpage);
++ }
++ trace_mdio_access(bus, 0, addr, regnum, val, err);
++ mdiobus_stats_acct(&bus->stats[addr], false, err);
++ return err;
++}
++EXPORT_SYMBOL(__mdiobus_write_paged);
++
++
++/**
+ * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -817,6 +916,43 @@ int __mdiobus_modify_changed(struct mii_
+ EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
+
+ /**
++ * __mdiobus_modify_changed_paged - Unlocked version of the mdiobus_modify_paged function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @regnum: register number to modify
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * Read, modify, and if any change, write the register value back to the
++ * device. Any error returns a negative number.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
++ u16 mask, u16 set)
++{
++ int new, ret, oldpage;
++
++ oldpage = bus->selected_page[addr];
++ __mdiobus_select_page(bus, addr, page);
++
++ ret = __mdiobus_read_paged(bus, addr, page, regnum);
++ if (ret < 0)
++ return ret;
++
++ new = (ret & ~mask) | set;
++ if (new == ret)
++ return 0;
++
++ ret = __mdiobus_write_paged(bus, addr, page, regnum, new);
++
++ __mdiobus_select_page(bus, addr, oldpage);
++
++ return ret < 0 ? ret : 1;
++}
++EXPORT_SYMBOL_GPL(__mdiobus_modify_changed_paged);
++
++/**
+ * mdiobus_read_nested - Nested version of the mdiobus_read function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -842,6 +978,79 @@ int mdiobus_read_nested(struct mii_bus *
+ EXPORT_SYMBOL(mdiobus_read_nested);
+
+ /**
++ * mdiobus_select_page_nested - Nested version of the mdiobus_select_page function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: register page to access
++ *
++ * In case of nested MDIO bus access avoid lockdep false positives by
++ * using mutex_lock_nested().
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page)
++{
++ int retval;
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++ retval = __mdiobus_select_page(bus, addr, page);
++ mutex_unlock(&bus->mdio_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(mdiobus_select_page_nested);
++
++/**
++ * mdiobus_read_paged_nested - Nested version of the mdiobus_read_paged function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: register page to access
++ * @regnum: register number to read
++ *
++ * In case of nested MDIO bus access avoid lockdep false positives by
++ * using mutex_lock_nested().
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_read_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum)
++{
++ int retval;
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++ retval = __mdiobus_read_paged(bus, addr, page, regnum);
++ mutex_unlock(&bus->mdio_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(mdiobus_read_paged_nested);
++
++/**
++ * mdiobus_select_page - Convenience function for setting the MII register page
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to set
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
++{
++ int retval;
++
++ mutex_lock(&bus->mdio_lock);
++ retval = __mdiobus_select_page(bus, addr, page);
++ mutex_unlock(&bus->mdio_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(mdiobus_select_page);
++
++/**
+ * mdiobus_read - Convenience function for reading a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -864,6 +1073,29 @@ int mdiobus_read(struct mii_bus *bus, in
+ EXPORT_SYMBOL(mdiobus_read);
+
+ /**
++ * mdiobus_read_paged - Convenience function for reading a given paged MII mgmt register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: register page to access
++ * @regnum: register number to read
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
++{
++ int retval;
++
++ mutex_lock(&bus->mdio_lock);
++ retval = __mdiobus_read_paged(bus, addr, page, regnum);
++ mutex_unlock(&bus->mdio_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(mdiobus_read_paged);
++
++/**
+ * mdiobus_write_nested - Nested version of the mdiobus_write function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -890,6 +1122,33 @@ int mdiobus_write_nested(struct mii_bus
+ EXPORT_SYMBOL(mdiobus_write_nested);
+
+ /**
++ * mdiobus_write_paged_nested - Nested version of the mdiobus_write_aged function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * In case of nested MDIO bus access avoid lockdep false positives by
++ * using mutex_lock_nested().
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_write_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
++{
++ int err;
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++ err = __mdiobus_write_paged(bus, addr, page, regnum, val);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err;
++}
++EXPORT_SYMBOL(mdiobus_write_paged_nested);
++
++/**
+ * mdiobus_write - Convenience function for writing a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -913,6 +1172,30 @@ int mdiobus_write(struct mii_bus *bus, i
+ EXPORT_SYMBOL(mdiobus_write);
+
+ /**
++ * mdiobus_write_paged - Convenience function for writing a given paged MII mgmt register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_write_paged(bus, addr, page, regnum, val);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err;
++}
++EXPORT_SYMBOL(mdiobus_write_paged);
++
++/**
+ * mdiobus_modify - Convenience function for modifying a given mdio device
+ * register
+ * @bus: the mii_bus struct
+@@ -934,6 +1217,51 @@ int mdiobus_modify(struct mii_bus *bus,
+ EXPORT_SYMBOL_GPL(mdiobus_modify);
+
+ /**
++ * mdiobus_modify_paged - Convenience function for modifying a given mdio device
++ * register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ */
++int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, u16 set)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err < 0 ? err : 0;
++}
++EXPORT_SYMBOL_GPL(mdiobus_modify_paged);
++
++/**
++ * mdiobus_modify_changed_paged - Convenience function for modifying a given paged
++ * mdio device register and returning if it changed
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @page: the register page to access
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ */
++int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
++ u16 mask, u16 set)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err;
++}
++EXPORT_SYMBOL_GPL(mdiobus_modify_changed_paged);
++
++/**
+ * mdio_bus_match - determine if given MDIO driver supports the given
+ * MDIO device
+ * @dev: target MDIO device
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -481,10 +481,16 @@ int __phy_read_mmd(struct phy_device *ph
+ struct mii_bus *bus = phydev->mdio.bus;
+ int phy_addr = phydev->mdio.addr;
+
+- mmd_phy_indirect(bus, phy_addr, devad, regnum);
+-
+- /* Read the content of the MMD's selected register */
+- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
++ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
++ val = __mdiobus_c22_mmd_read(phydev->mdio.bus,
++ phydev->mdio.addr,
++ devad, regnum);
++ } else {
++ mmd_phy_indirect(bus, phy_addr, devad, regnum);
++
++ /* Read the content of the MMD's selected register */
++ val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
++ }
+ }
+ return val;
+ }
+@@ -537,12 +543,18 @@ int __phy_write_mmd(struct phy_device *p
+ struct mii_bus *bus = phydev->mdio.bus;
+ int phy_addr = phydev->mdio.addr;
+
+- mmd_phy_indirect(bus, phy_addr, devad, regnum);
++ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
++ ret = __mdiobus_c22_mmd_write(phydev->mdio.bus,
++ phydev->mdio.addr,
++ devad, regnum, val);
++ } else {
++ mmd_phy_indirect(bus, phy_addr, devad, regnum);
+
+- /* Write the data into MMD's selected register */
+- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
++ /* Write the data into MMD's selected register */
++ __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
+
+- ret = 0;
++ ret = 0;
++ }
+ }
+ return ret;
+ }
+@@ -748,6 +760,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd);
+
+ static int __phy_read_page(struct phy_device *phydev)
+ {
++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
++ struct mii_bus *bus = phydev->mdio.bus;
++ int phy_addr = phydev->mdio.addr;
++
++ return bus->selected_page[phy_addr];
++ }
++
+ if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
+ return -EOPNOTSUPP;
+
+@@ -756,6 +775,13 @@ static int __phy_read_page(struct phy_de
+
+ static int __phy_write_page(struct phy_device *phydev, int page)
+ {
++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
++ struct mii_bus *bus = phydev->mdio.bus;
++ int phy_addr = phydev->mdio.addr;
++
++ return __mdiobus_select_page(bus, phy_addr, page);
++ }
++
+ if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
+ return -EOPNOTSUPP;
+
+@@ -857,6 +883,18 @@ int phy_read_paged(struct phy_device *ph
+ {
+ int ret = 0, oldpage;
+
++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
++ struct mii_bus *bus = phydev->mdio.bus;
++ int phy_addr = phydev->mdio.addr;
++
++ if (bus->read_paged) {
++ phy_lock_mdio_bus(phydev);
++ ret = bus->read_paged(bus, phy_addr, page, regnum);
++ phy_unlock_mdio_bus(phydev);
++ return ret;
++ }
++ }
++
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0)
+ ret = __phy_read(phydev, regnum);
+@@ -878,6 +916,18 @@ int phy_write_paged(struct phy_device *p
+ {
+ int ret = 0, oldpage;
+
++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
++ struct mii_bus *bus = phydev->mdio.bus;
++ int phy_addr = phydev->mdio.addr;
++
++ if (bus->write_paged) {
++ phy_lock_mdio_bus(phydev);
++ ret = bus->write_paged(bus, phy_addr, page, regnum, val);
++ phy_unlock_mdio_bus(phydev);
++ return ret;
++ }
++ }
++
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0)
+ ret = __phy_write(phydev, regnum, val);
+--- a/include/linux/mdio.h
++++ b/include/linux/mdio.h
+@@ -14,6 +14,7 @@
+ * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips.
+ */
+ #define MII_ADDR_C45 (1<<30)
++#define MII_ADDR_C22_MMD (1<<29)
+ #define MII_DEVADDR_C45_SHIFT 16
+ #define MII_DEVADDR_C45_MASK GENMASK(20, 16)
+ #define MII_REGADDR_C45_MASK GENMASK(15, 0)
+@@ -327,11 +328,19 @@ static inline void mii_10gbt_stat_mod_li
+ advertising, lpa & MDIO_AN_10GBT_STAT_LP10G);
+ }
+
++int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
+ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+ int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
+ u16 mask, u16 set);
+
++int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
++int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
++int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
++ u16 mask, u16 set);
++
++int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
++int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page);
+ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+ int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
+ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+@@ -339,11 +348,51 @@ int mdiobus_write_nested(struct mii_bus
+ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
+ u16 set);
+
++int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
++int mdiobus_read_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
++int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
++int mdiobus_write_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
++int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask,
++ u16 set);
++int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
++ u16 mask, u16 set);
++
++static inline int mdiodev_read_paged(struct mdio_device *mdiodev, u16 page,
++ u32 regnum)
++{
++ return mdiobus_read_paged(mdiodev->bus, mdiodev->addr, page, regnum);
++}
++
++static inline int mdiodev_write_paged(struct mdio_device *mdiodev, u16 page,
++ u32 regnum, u16 val)
++{
++ return mdiobus_write_paged(mdiodev->bus, mdiodev->addr, page, regnum, val);
++}
++
++static inline int mdiodev_modify_paged(struct mdio_device *mdiodev, u16 page,
++ u32 regnum, u16 mask, u16 set)
++{
++ return mdiobus_modify_paged(mdiodev->bus, mdiodev->addr, page, regnum,
++ mask, set);
++}
++
++static inline int mdiodev_modify_changed_paged(struct mdio_device *mdiodev, u16 page,
++ u32 regnum, u16 mask, u16 set)
++{
++ return mdiobus_modify_changed_paged(mdiodev->bus, mdiodev->addr, page, regnum,
++ mask, set);
++}
++
+ static inline u32 mdiobus_c45_addr(int devad, u16 regnum)
+ {
+ return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
+ }
+
++static inline u32 mdiobus_c22_mmd_addr(int devad, u16 regnum)
++{
++ return MII_ADDR_C22_MMD | devad << MII_DEVADDR_C45_SHIFT | regnum;
++}
++
+ static inline u16 mdiobus_c45_regad(u32 regnum)
+ {
+ return FIELD_GET(MII_REGADDR_C45_MASK, regnum);
+@@ -367,6 +416,19 @@ static inline int __mdiobus_c45_write(st
+ val);
+ }
+
++static inline int __mdiobus_c22_mmd_read(struct mii_bus *bus, int prtad,
++ int devad, u16 regnum)
++{
++ return __mdiobus_read(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum));
++}
++
++static inline int __mdiobus_c22_mmd_write(struct mii_bus *bus, int prtad,
++ int devad, u16 regnum, u16 val)
++{
++ return __mdiobus_write(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum),
++ val);
++}
++
+ static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
+ u16 regnum)
+ {
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -80,6 +80,7 @@ extern const int phy_10gbit_features_arr
+ #define PHY_IS_INTERNAL 0x00000001
+ #define PHY_RST_AFTER_CLK_EN 0x00000002
+ #define PHY_POLL_CABLE_TEST 0x00000004
++#define PHY_HAS_REALTEK_PAGES 0x00000010
+ #define MDIO_DEVICE_IS_PHY 0x80000000
+
+ /**
+@@ -374,6 +375,22 @@ struct mii_bus {
+
+ /** @shared: shared state across different PHYs */
+ struct phy_package_shared *shared[PHY_MAX_ADDR];
++
++ /** @access_capabilities: hardware-assisted access capabilties */
++ enum {
++ MDIOBUS_ACCESS_SOFTWARE_ONLY = 0,
++ MDIOBUS_ACCESS_C22_MMD = 0x1,
++ } access_capabilities;
++
++ /** @read: Perform a read transfer on the bus, offloading page access */
++ int (*read_paged)(struct mii_bus *bus, int addr, u16 page, int regnum);
++ /** @write: Perform a write transfer on the bus, offloading page access */
++ int (*write_paged)(struct mii_bus *bus, int addr, u16 page, int regnum, u16 val);
++ /** currently selected page when page access is offloaded
++ * array should be PHY_MAX_ADDR+1size, but current design of the MDIO driver
++ * uses port addresses as phy addresses and they are up to 6 bit.
++ */
++ u16 selected_page[64];
+ };
+ #define to_mii_bus(d) container_of(d, struct mii_bus, dev)
+
+@@ -1651,6 +1668,66 @@ static inline int __phy_package_read(str
+ return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+ }
+
++static inline int phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
++}
++
++static inline int __phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
++}
++
++static inline int phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
++}
++
++static inline int __phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
++}
++
++static inline int phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
++}
++
++static inline int __phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
++}
++
+ static inline int phy_package_write(struct phy_device *phydev,
+ u32 regnum, u16 val)
+ {
+@@ -1673,6 +1750,72 @@ static inline int __phy_package_write(st
+ return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+ }
+
++static inline int phy_package_port_write(struct phy_device *phydev,
++ u16 port, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
++}
++
++static inline int __phy_package_port_write(struct phy_device *phydev,
++ u16 port, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
++}
++
++static inline int phy_package_port_write_paged(struct phy_device *phydev,
++ u16 port, u16 page, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
++}
++
++static inline int __phy_package_port_write_paged(struct phy_device *phydev,
++ u16 port, u16 page, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
++}
++
++static inline int phy_package_write_paged(struct phy_device *phydev,
++ u16 page, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
++}
++
++static inline int __phy_package_write_paged(struct phy_device *phydev,
++ u16 page, u32 regnum, u16 val)
++{
++ struct phy_package_shared *shared = phydev->shared;
++
++ if (!shared)
++ return -EIO;
++
++ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
++}
++
+ static inline bool __phy_package_set_once(struct phy_device *phydev,
+ unsigned int b)
+ {
+--- a/include/uapi/linux/mii.h
++++ b/include/uapi/linux/mii.h
+@@ -36,6 +36,7 @@
+ #define MII_RESV2 0x1a /* Reserved... */
+ #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+ #define MII_NCONFIG 0x1c /* Network interface config */
++#define MII_MAINPAGE 0x1f /* Page register */
+
+ /* Basic mode control register. */
+ #define BMCR_RESV 0x003f /* Unused... */