The hardware polling unit of the Realtek switches has a very special handling for c22 PHY register 31 (aka Realtek page register) in place. - On the RTL838x it is permanently reset to zero. - On other devices there is some magic saving/restoring (aka parking) in the background in place. This makes access to PHYs a gamble. It is vital to keep the polling alive so the MAC layer can rely on consistent data. Intercept access to c22 register 31 and handle it internally. Store the desired value for each port in the driver. When issuing hardware access to other registers add the page to the command towards the controller. This given, the hardware will run two c22 commands that are not interrupted by polling. ... hardware poll ... phy_write(phy, 31, page) phy_write(phy, reg, value) ... hardware poll ... Looking at this implementation one might argue that disabling/enabling polling might be a cleaner solution. But one must remember that - This driver differentiates clearly between C22 and C45 buses. During probing it enables only one of the protocols for a bus. - All known devices run RTL8218 (B/D/E) or RTL8214FC on 1G - RTL839x gives link flapping when deactivating polling for a port So a solution for a Realtek-only ecosystem is required. This commit copies the downstream-proven driver-only page handling patch without any new MDIO callbacks and is the lowest common denominator. Remark! To keep this simple, writes to register 31 are only accepted if they are lower than the device specific raw page - 0..4094/8190. Otherwise -EINVAL is returned. Under the above assumption (Only 1G Realtek PHYs on c22 bus) this is no limitation. Signed-off-by: Markus Stockhausen --- drivers/net/mdio/mdio-realtek-rtl9300.c | 34 ++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index c607dad9f0a3..07dcc2231582 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -177,6 +177,7 @@ #define PHY_CTRL_CMD BIT(0) #define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) #define PHY_CTRL_MMD_REG GENMASK(15, 0) +#define PHY_VENDOR_REALTEK 0x001cc800 #define MAP_ADDRS_PER_REG 6 #define MAP_BITS_PER_ADDR 5 @@ -203,6 +204,7 @@ struct otto_emdio_priv { struct regmap *regmap; struct mutex lock; /* protect HW access */ DECLARE_BITMAP(valid_ports, MAX_PORTS); + u16 page[MAX_PORTS]; u8 smi_bus[MAX_PORTS]; u8 smi_addr[MAX_PORTS]; bool smi_bus_is_c45[MAX_SMI_BUSSES]; @@ -354,7 +356,7 @@ static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]), .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port), }; @@ -368,7 +370,7 @@ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]), .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value), .port_mask_low = BIT(port), }; @@ -408,7 +410,7 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u struct otto_emdio_cmd_regs cmd_data = { .broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port), .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]), }; return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data, @@ -420,7 +422,7 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum, struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]), .io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value), .port_mask_high = (u32)(BIT_ULL(port) >> 32), .port_mask_low = (u32)(BIT_ULL(port)), @@ -466,8 +468,12 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) if (port < 0) return port; - scoped_guard(mutex, &priv->lock) + scoped_guard(mutex, &priv->lock) { + if (regnum == 31) + return priv->page[port]; + ret = priv->info->read_c22(bus, port, regnum, &value); + } return ret ? ret : value; } @@ -481,8 +487,17 @@ static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 if (port < 0) return port; - scoped_guard(mutex, &priv->lock) + scoped_guard(mutex, &priv->lock) { + if (regnum == 31) { + if (value >= RAW_PAGE(priv)) + return -EINVAL; + + priv->page[port] = value; + return 0; + } + ret = priv->info->write_c22(bus, port, regnum, value); + } return ret; } @@ -595,10 +610,17 @@ static int otto_emdio_notify_phy_attach(struct phy_device *phydev) { struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(phydev->mdio.bus); int port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr); + struct otto_emdio_chan *chan = phydev->mdio.bus->priv; if (port < 0) return port; + if (!priv->smi_bus_is_c45[chan->mdio_bus] && + (!phy_id_compare_vendor(phydev->phy_id, PHY_VENDOR_REALTEK))) { + phydev_err(phydev, "Only Realtek PHYs allowed on C22 bus\n"); + return -EOPNOTSUPP; + } + return otto_emdio_set_port_polling(priv, port, true); } -- 2.55.0