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 v3 5/8] net: mdio: realtek-rtl9300: Add page tracking
Date: Sun, 5 Jul 2026 18:35:29 +0200 [thread overview]
Message-ID: <20260705163532.2853959-6-markus.stockhausen@gmx.de> (raw)
In-Reply-To: <20260705163532.2853959-1-markus.stockhausen@gmx.de>
The hardware polling unit of the Realtek switches has a very special
handling for 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.
As of now all known existing hardware designs have Realtek PHYs for 1G
connectivity. Otherwise the polling engine and the MAC status update
will not work at all and the vendor SDK would fail totally.
This driver differentiates clearly between C22 and C45 buses. During
probing it enables only one of the protocols for a bus. So it is safe
to assume that any C22 access will only target a Realtek based 1G PHY.
For safety reasons block any non-Realtek PHY on a C22 bus.
Intercept access to 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 consecutive c22 commands that are
not interrupted by polling.
... hardware poll ...
phy_write(phy, 31, page)
phy_write(phy, reg, value)
... hardware poll ...
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 <markus.stockhausen@gmx.de>
---
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 c36244cd9a66..a5cac0d04114 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -171,6 +171,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
@@ -198,6 +199,7 @@ struct otto_emdio_priv {
struct mutex lock; /* protect HW access */
DECLARE_BITMAP(phy_poll, MAX_PORTS);
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];
@@ -351,7 +353,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),
};
@@ -365,7 +367,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),
};
@@ -405,7 +407,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,
@@ -417,7 +419,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)),
@@ -463,8 +465,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;
}
@@ -478,8 +484,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;
}
@@ -592,6 +607,7 @@ 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;
int ret;
if (port < 0)
@@ -600,6 +616,12 @@ static int otto_emdio_notify_phy_attach(struct phy_device *phydev)
if (test_bit(port, priv->phy_poll))
return 0;
+ 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;
+ }
+
scoped_guard(mutex, &priv->lock) {
ret = otto_emdio_set_port_polling(priv, port, true);
if (!ret)
--
2.54.0
next prev parent reply other threads:[~2026-07-05 16:36 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-05 16:35 [PATCH net-next v3 0/8] net: mdio: realtek-rtl9300: Add RTL83xx support Markus Stockhausen
2026-07-05 16:35 ` [PATCH net-next v3 1/8] dt-bindings: net: realtek,rtl9301-mdio: Add RTL83xx series Markus Stockhausen
2026-07-06 17:08 ` Conor Dooley
2026-07-05 16:35 ` [PATCH net-next v3 2/8] net: mdio: realtek-rtl9300: Add polling documentation Markus Stockhausen
2026-07-05 16:35 ` [PATCH net-next v3 3/8] net: phy: add (*notify_phy_attach/detach)() hooks to struct mii_bus Markus Stockhausen
[not found] ` <20260706163627.A22DC1F000E9@smtp.kernel.org>
2026-07-06 17:36 ` AW: " Markus Stockhausen
2026-07-05 16:35 ` [PATCH net-next v3 4/8] net: mdio: realtek-rtl9300: Configure hardware polling during probing Markus Stockhausen
2026-07-05 16:35 ` Markus Stockhausen [this message]
2026-07-05 16:35 ` [PATCH net-next v3 6/8] net: mdio: realtek-rtl9300: Increase MDIO timeout Markus Stockhausen
2026-07-05 16:35 ` [PATCH net-next v3 7/8] net: mdio: realtek-rtl9300: Add support for RTL838x Markus Stockhausen
2026-07-05 16:35 ` [PATCH net-next v3 8/8] 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=20260705163532.2853959-6-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