From: Markus Stockhausen <markus.stockhausen@gmx.de>
To: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, netdev@vger.kernel.org,
chris.packham@alliedtelesis.co.nz, daniel@makrotopia.org,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org
Cc: Markus Stockhausen <markus.stockhausen@gmx.de>
Subject: [PATCH net-next v11 07/10] net: mdio: realtek-rtl9300: Add page tracking
Date: Sun, 2 Aug 2026 10:35:25 +0200 [thread overview]
Message-ID: <20260802083528.490216-8-markus.stockhausen@gmx.de> (raw)
In-Reply-To: <20260802083528.490216-1-markus.stockhausen@gmx.de>
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. Given this, 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. If a
non-Realtek PHY is identified on a c22 bus the attachment aborts. It
should be noted that bus scan runs with the page handling already in
place before the check in notify_phy_attach(). This is accepted for
now.
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 a c22 bus) this is no limitation.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/net/mdio/mdio-realtek-rtl9300.c | 46 +++++++++++++++++++++----
1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 161df1818d02..95641052bc9b 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -178,6 +178,9 @@
#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16)
#define PHY_CTRL_MMD_REG GENMASK(15, 0)
+#define RTL_VENDOR_ID 0x001cc800
+#define RTL_PAGE_SELECT 31
+
#define MAP_ADDRS_PER_REG 6
#define MAP_BITS_PER_ADDR 5
#define MAP_BITS_PER_BUS 2
@@ -203,6 +206,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 +358,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 +372,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 +412,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 +424,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 +470,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 == RTL_PAGE_SELECT)
+ return priv->page[port];
+
ret = priv->info->read_c22(bus, port, regnum, &value);
+ }
return ret ? ret : value;
}
@@ -481,8 +489,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 == RTL_PAGE_SELECT) {
+ if (value >= RAW_PAGE(priv))
+ return -EINVAL;
+
+ priv->page[port] = value;
+ return 0;
+ }
+
ret = priv->info->write_c22(bus, port, regnum, value);
+ }
return ret;
}
@@ -593,12 +610,23 @@ static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)
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;
+ struct otto_emdio_priv *priv = chan->priv;
if (port < 0)
return port;
+ /* "sync" page in case of previously failed attachment */
+ scoped_guard(mutex, &priv->lock)
+ priv->page[port] = 0;
+
+ if (!priv->smi_bus_is_c45[chan->mdio_bus] &&
+ !phy_id_compare_vendor(phydev->phy_id, RTL_VENDOR_ID)) {
+ phydev_err(phydev, "Only Realtek PHYs allowed on C22 bus\n");
+ return -EOPNOTSUPP;
+ }
+
return otto_emdio_set_port_polling(priv, port, true);
}
@@ -618,6 +646,10 @@ static void otto_emdio_notify_phy_detach(struct phy_device *phydev)
if (port < 0)
return;
+ /* "sync" page for next attachment */
+ scoped_guard(mutex, &priv->lock)
+ priv->page[port] = 0;
+
if (otto_emdio_set_port_polling(priv, port, false))
dev_err(bus->parent, "failed to disable polling for port %d\n", port);
}
--
2.55.0
next prev parent reply other threads:[~2026-08-02 8:35 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-02 8:35 [PATCH net-next v11 00/10] net: mdio: realtek-rtl9300: Add RTL83xx support Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 01/10] net: mctp: usb: Allow multiple urbs in flight Markus Stockhausen
2026-08-03 1:45 ` Jeremy Kerr
2026-08-03 5:33 ` AW: " Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 02/10] dt-bindings: net: realtek,rtl9301-mdio: Add RTL83xx series Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 03/10] net: mdio: realtek-rtl9300: Add polling documentation Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 04/10] net: phy: add (*notify_phy_attach/detach)() hooks to struct mii_bus Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 05/10] net: mdio: realtek-rtl9300: suppress sysfs bind/unbind attributes Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 06/10] net: mdio: realtek-rtl9300: Configure hardware polling during probing Markus Stockhausen
2026-08-02 8:35 ` Markus Stockhausen [this message]
2026-08-02 8:35 ` [PATCH net-next v11 08/10] net: mdio: realtek-rtl9300: Increase MDIO timeout Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 09/10] net: mdio: realtek-rtl9300: Add support for RTL838x Markus Stockhausen
2026-08-02 8:35 ` [PATCH net-next v11 10/10] net: mdio: realtek-rtl9300: Add support for RTL839x Markus Stockhausen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260802083528.490216-8-markus.stockhausen@gmx.de \
--to=markus.stockhausen@gmx.de \
--cc=andrew@lunn.ch \
--cc=chris.packham@alliedtelesis.co.nz \
--cc=conor+dt@kernel.org \
--cc=daniel@makrotopia.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox