* [RFC PATCH v2 0/2] net: mdio: setup RealTek MDIO polling registers
@ 2026-01-22 4:00 Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 1/2] net: phy: add (*register_phy)() hook to struct mii_bus Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers Daniel Golle
0 siblings, 2 replies; 7+ messages in thread
From: Daniel Golle @ 2026-01-22 4:00 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
Cc: Jonas Jelonek, Markus Stockhausen
Some switch SoCs supported as DSA devices such as RTL930x require to
setup hardware polling of the PHY status in order to configure the
switch MACs, there just isn't another way to setup MAC speed, duplex,
flow-control, ... settings.
In order to reduce code duplication it would be great if the MDIO driver
could expect to get a pointer to the fully populated 'struct phy_device'
and use that to then setup the polling registers based on the PHY type.
Obviously there will still need to be register definitions for each
supported type of PHY, but generally there aren't that many different
kind of PHYs used on those switches, and already being able to rely on
phydev->supported being intialized helps a lot.
Please take a look at the downstream MDIO bus driver for RealTek switch
SoCs here:
https://github.com/jonasjelonek/openwrt/blob/5f4c692319c66dcff9602441d5512546c81d7a1e/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c#L864
Note that the driver currently re-implements MDIO bus scanning in order
to figure out which PHYs are on the bus, see
https://github.com/jonasjelonek/openwrt/blob/5f4c692319c66dcff9602441d5512546c81d7a1e/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c#L798
As you can see the polling registers of the MDIO bus need to be set to
MMD device, register and bit-inside-register values in case of Clause-45
PHYs to deal with half-duplex and 1000M advertisement and LPA the
controller needs to be told that it is dealing with a 1G standard PHY
(plus there are some proprietary extensions, like 1G over 2 pairs, but
never mind those at this point).
This is due to the Clause-45 standard lacking clear definitions for
these features, while being able to rely on standard registers for
pretty much everything else.
In vanilla Linux, drivers/net/mdio/mdio-realtek-rtl9300.c currently
relies on the bootloader to setup these registers, however, on quite a
lot of switches the bootloader only does this in case the user
interrupts the boot process and loads firmware via TFTP, otherwise
Ethernet remains uninitialized. On some devices the U-Boot environment
can be customized so they always intialize the switch and MDIO
controller, and rarely vendors decide to just always do it.
In order to efficiently implement setting up the PHY polling registers
of the MDIO controller, add a new hook (*register_phy)() to
struct mii_bus which is called once the PHY is fully probed and it's
ID(s) and supported features have been populated in struct phy_device.
Use this new hook to setup the MDIO controller's polling registers
without having to maintain an excessive list of PHY IDs, or re-implement
scanning the bus.
Daniel Golle (2):
net: phy: add (*register_phy)() hook to struct mii_bus
net: mdio: rtl9300: setup PHY polling registers
drivers/net/mdio/mdio-realtek-rtl9300.c | 82 +++++++++++++++++++++++++
drivers/net/phy/phy_device.c | 8 ++-
include/linux/phy.h | 3 +
3 files changed, 92 insertions(+), 1 deletion(-)
--
2.52.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH v2 1/2] net: phy: add (*register_phy)() hook to struct mii_bus
2026-01-22 4:00 [RFC PATCH v2 0/2] net: mdio: setup RealTek MDIO polling registers Daniel Golle
@ 2026-01-22 4:00 ` Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers Daniel Golle
1 sibling, 0 replies; 7+ messages in thread
From: Daniel Golle @ 2026-01-22 4:00 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
Cc: Jonas Jelonek, Markus Stockhausen
Some MDIO busses require to program PHY polling registers depending on
the PHY type. RealTek switch SoCs are the most prominent example of a
DSA switch which doesn't allow to program MAC speed, duplex and
flow-control settings without using PHY polling to do so.
Hence there is a need to inform the MDIO bus driver that a PHY has been
registered on the bus, as otherwise the bus driver will have to reinvent
and duplicate all the bus scanning logic for Clause-22 and Clause-45
PHYs, and discover their features (or maintain a long list of PHY IDs).
Provide a simple hook in struct mii_bus which is called right after a
PHY has been fully probed.
Alternative ways which would allow to do the same things without
having to change any kernel code would of course be very welcome.
Link: https://github.com/openwrt/openwrt/pull/21515#discussion_r2714069716
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/phy_device.c | 8 +++++++-
include/linux/phy.h | 3 +++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index f624218bf3664..7807f3db50507 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -3778,8 +3778,14 @@ static int phy_probe(struct device *dev)
/* Get the LEDs from the device tree, and instantiate standard
* LEDs for them.
*/
- if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev))
+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev)) {
err = of_phy_leds(phydev);
+ if (err)
+ goto out;
+ }
+
+ if (phydev->mdio.bus->register_phy)
+ err = phydev->mdio.bus->register_phy(phydev);
out:
/* Re-assert the reset signal on error */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5972f19af16da..1d38e27d20389 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -377,6 +377,9 @@ struct mii_bus {
/** @reset: Perform a reset of the bus */
int (*reset)(struct mii_bus *bus);
+ /** @register_phy: Called for each PHY detected on the bus */
+ int (*register_phy)(struct phy_device *phydev);
+
/** @stats: Statistic counters per device on the bus */
struct mdio_bus_stats stats[PHY_MAX_ADDR];
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers
2026-01-22 4:00 [RFC PATCH v2 0/2] net: mdio: setup RealTek MDIO polling registers Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 1/2] net: phy: add (*register_phy)() hook to struct mii_bus Daniel Golle
@ 2026-01-22 4:00 ` Daniel Golle
2026-01-22 12:10 ` Russell King (Oracle)
1 sibling, 1 reply; 7+ messages in thread
From: Daniel Golle @ 2026-01-22 4:00 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
Cc: Jonas Jelonek, Markus Stockhausen
The mdio-realtek-rtl9300 driver currently relies on the bootloader to
correctly setup polling registers depending on the PHY type. On quite a
lot of switches the bootloader only does this in case the user
interrupts the boot process and loads firmware via TFTP, otherwise
Ethernet remains uninitialized. On some devices the U-Boot environment
can be customized so they always initialize the switch and MDIO
controller before launching Linux, and rarely vendors decide to just
always do it.
Setup the polling registers based on the PHYs detected on the bus, and
setup registers for non-standard Clause-45 registers used in polling in
case the PHY vendor is known and commonly used in those switches.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/mdio/mdio-realtek-rtl9300.c | 82 +++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 405a07075dd11..9e030be497ab3 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -24,6 +24,9 @@
#define SMI_GLB_CTRL 0xca00
#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf))
+#define SMI_MAC_TYPE_CTRL 0xca04
+#define SMI_MAC_TYPE_CTRL_1G 3
+#define SMI_MAC_TYPE_CTRL_2G_PLUS 1
#define SMI_PORT0_15_POLLING_SEL 0xca08
#define SMI_ACCESS_PHY_CTRL_0 0xcb70
#define SMI_ACCESS_PHY_CTRL_1 0xcb74
@@ -43,6 +46,13 @@
#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16)
#define PHY_CTRL_MMD_REG GENMASK(15, 0)
#define SMI_PORT0_5_ADDR_CTRL 0xcb80
+#define SMI_PRVTE_POLLING_CTRL 0xca10
+#define SMI_10G_POLLING_REG0_CFG 0xcbb4
+#define SMI_10G_POLLING_REG9_CFG 0xcbb8
+#define SMI_10G_POLLING_REG10_CFG 0xcbbc
+
+#define RTMDIO_PHY_POLL_MMD(dev, reg, bit) \
+ (((bit) << 21) | ((dev) << 16) | (reg))
#define MAX_PORTS 28
#define MAX_SMI_BUSSES 4
@@ -56,6 +66,7 @@ struct rtl9300_mdio_priv {
u8 smi_addr[MAX_PORTS];
bool smi_bus_is_c45[MAX_SMI_BUSSES];
struct mii_bus *bus[MAX_SMI_BUSSES];
+ bool c45_polling_is_setup;
};
struct rtl9300_mdio_chan {
@@ -350,6 +361,74 @@ static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
return 0;
}
+static int rtl9300_register_phy(struct phy_device *phydev)
+{
+ u32 mask, val, poll_duplex, poll_adv_1000, poll_lpa_1000;
+ struct mii_bus *bus = phydev->mdio.bus;
+ struct rtl9300_mdio_chan *chan;
+ struct rtl9300_mdio_priv *priv;
+ struct regmap *regmap;
+ int port, ret;
+ u8 mac_type;
+
+ chan = bus->priv;
+ priv = chan->priv;
+ regmap = priv->regmap;
+ port = rtl9300_mdio_phy_to_port(bus, phydev->mdio.addr);
+
+ /* Detect if PHY has 2.5G/5G/10G capabilities */
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported) ||
+ linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, phydev->supported) ||
+ linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, phydev->supported))
+ mac_type = SMI_MAC_TYPE_CTRL_2G_PLUS;
+ else
+ mac_type = SMI_MAC_TYPE_CTRL_1G;
+
+ mask = port > 23 ? 0x7 << ((port - 24) * 3 + 12) : 0x3 << ((port / 4) * 2);
+ val = mac_type << (ffs(mask) - 1);
+ ret = regmap_update_bits(regmap, SMI_MAC_TYPE_CTRL, mask, val);
+ if (ret < 0)
+ return ret;
+
+ /* Setup Clause-45 registers to be polled for the first >1G PHY.
+ * All >1G PHYs connected to the switch SoC have to be of the same type.
+ */
+ if (mac_type == SMI_MAC_TYPE_CTRL_1G || priv->c45_polling_is_setup)
+ return 0;
+
+ priv->c45_polling_is_setup = true;
+
+ switch (phydev->phy_id & PHY_ID_MATCH_VENDOR_MASK) {
+ case 0x001cc800: /* RealTek */
+ poll_duplex = RTMDIO_PHY_POLL_MMD(MDIO_MMD_VEND2, 0xa400, 8);
+ poll_adv_1000 = RTMDIO_PHY_POLL_MMD(MDIO_MMD_VEND2, 0xa412, 9);
+ poll_lpa_1000 = RTMDIO_PHY_POLL_MMD(MDIO_MMD_VEND2, 0xa414, 11);
+ break;
+ case 0x03a1b400: /* Aquantia */
+ case 0x31c31c00:
+ poll_duplex = RTMDIO_PHY_POLL_MMD(MDIO_MMD_PMAPMD, MDIO_CTRL1, 8);
+ poll_adv_1000 = RTMDIO_PHY_POLL_MMD(MDIO_MMD_AN, 0xc400, 15);
+ poll_lpa_1000 = RTMDIO_PHY_POLL_MMD(MDIO_MMD_AN, 0xe820, 15);
+ break;
+ default:
+ dev_warn(&phydev->mdio.dev,
+ "Unknown register layout, relying on bootloader network setup.\n");
+ return 0;
+ };
+
+ ret = regmap_write(regmap, SMI_10G_POLLING_REG0_CFG, poll_duplex);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(regmap, SMI_10G_POLLING_REG9_CFG, poll_adv_1000);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(regmap, SMI_10G_POLLING_REG10_CFG, poll_lpa_1000);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_priv *priv,
struct fwnode_handle *node)
{
@@ -387,6 +466,7 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri
bus->write = rtl9300_mdio_write_c22;
}
bus->parent = dev;
+ bus->register_phy = rtl9300_register_phy;
chan = bus->priv;
chan->mdio_bus = mdio_bus;
chan->priv = priv;
@@ -481,6 +561,8 @@ static int rtl9300_mdiobus_probe(struct platform_device *pdev)
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
+ priv->c45_polling_is_setup = false;
+
platform_set_drvdata(pdev, priv);
err = rtl9300_mdiobus_map_ports(dev);
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers
2026-01-22 4:00 ` [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers Daniel Golle
@ 2026-01-22 12:10 ` Russell King (Oracle)
2026-01-22 13:22 ` Daniel Golle
0 siblings, 1 reply; 7+ messages in thread
From: Russell King (Oracle) @ 2026-01-22 12:10 UTC (permalink / raw)
To: Daniel Golle
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, Jonas Jelonek,
Markus Stockhausen
On Thu, Jan 22, 2026 at 04:00:56AM +0000, Daniel Golle wrote:
> + /* Detect if PHY has 2.5G/5G/10G capabilities */
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported) ||
> + linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, phydev->supported) ||
> + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, phydev->supported))
> + mac_type = SMI_MAC_TYPE_CTRL_2G_PLUS;
> + else
> + mac_type = SMI_MAC_TYPE_CTRL_1G;
There are PHYs which change their support depending on how they're
configured, and there are PHYs that aren't configured correctly at
probe time (and thus their ->supported mask is not correct.)
The next thing I'm concerned about is the need to encode register
information into this driver for each PHY that could be connected.
Will there only be a small subset of PHYs that get used with this
switch, or can any PHY be connected? If the latter, then encoding
register information doesn't scale.
So, I don't think this is a good solution.
I'm wondering whether it would be better to have a callback through
the bus when the PHY is connected with a netdev (in other words,
after phy_init_hw() in phy_attach_direct()) which should be when
the ->supported mask is fully up to date. This also means that the
driver is bound, and we could then consider some kind of interface
that would allow data about the PHY to be requested, such as register
information. That would likely eliminate your need to test the
phydev->supported bitmask (which I think is there to determine whether
you need to tell your MDIO polling controller where certain registers
are.)
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers
2026-01-22 12:10 ` Russell King (Oracle)
@ 2026-01-22 13:22 ` Daniel Golle
2026-01-22 13:46 ` Andrew Lunn
0 siblings, 1 reply; 7+ messages in thread
From: Daniel Golle @ 2026-01-22 13:22 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, Jonas Jelonek,
Markus Stockhausen
On Thu, Jan 22, 2026 at 12:10:04PM +0000, Russell King (Oracle) wrote:
> On Thu, Jan 22, 2026 at 04:00:56AM +0000, Daniel Golle wrote:
> > + /* Detect if PHY has 2.5G/5G/10G capabilities */
> > + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported) ||
> > + linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, phydev->supported) ||
> > + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, phydev->supported))
> > + mac_type = SMI_MAC_TYPE_CTRL_2G_PLUS;
> > + else
> > + mac_type = SMI_MAC_TYPE_CTRL_1G;
>
> There are PHYs which change their support depending on how they're
> configured, and there are PHYs that aren't configured correctly at
> probe time (and thus their ->supported mask is not correct.)
Oh, right...
>
> The next thing I'm concerned about is the need to encode register
> information into this driver for each PHY that could be connected.
> Will there only be a small subset of PHYs that get used with this
> switch, or can any PHY be connected? If the latter, then encoding
> register information doesn't scale.
Only a small subset has been seen in the wild inside RealTek switches
for now. Most, of course, come with RealTek switches. However, it is
only very recently that RealTek offers 10G PHYs, so before vendors
were using 10G PHYs of other vendors, in practise I've only seen
Aquantia.
>
> So, I don't think this is a good solution.
>
> I'm wondering whether it would be better to have a callback through
> the bus when the PHY is connected with a netdev (in other words,
> after phy_init_hw() in phy_attach_direct()) which should be when
> the ->supported mask is fully up to date. This also means that the
> driver is bound, and we could then consider some kind of interface
> that would allow data about the PHY to be requested, such as register
> information. That would likely eliminate your need to test the
> phydev->supported bitmask (which I think is there to determine whether
> you need to tell your MDIO polling controller where certain registers
> are.)
Yes, that does sound better, especially with your concern about
->supported mask not necessarily being correct at probe time in mind.
It also makes more sense because it's only then that the interface
is in admin-up state and actually needs to be polled for a link.
Thank you for your thoughts and input on that. I'll discuss this
with the other OpenWrt folks involved with those switches and we'll
come up with a follow-up version.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers
2026-01-22 13:22 ` Daniel Golle
@ 2026-01-22 13:46 ` Andrew Lunn
2026-01-22 16:06 ` Daniel Golle
0 siblings, 1 reply; 7+ messages in thread
From: Andrew Lunn @ 2026-01-22 13:46 UTC (permalink / raw)
To: Daniel Golle
Cc: Russell King (Oracle), Heiner Kallweit, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
Jonas Jelonek, Markus Stockhausen
> Only a small subset has been seen in the wild inside RealTek switches
> for now. Most, of course, come with RealTek switches. However, it is
> only very recently that RealTek offers 10G PHYs, so before vendors
> were using 10G PHYs of other vendors, in practise I've only seen
> Aquantia.
...
> Yes, that does sound better, especially with your concern about
> ->supported mask not necessarily being correct at probe time in mind.
FYI: The Aquantia PHYs are particularly bad with ->supported.
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers
2026-01-22 13:46 ` Andrew Lunn
@ 2026-01-22 16:06 ` Daniel Golle
0 siblings, 0 replies; 7+ messages in thread
From: Daniel Golle @ 2026-01-22 16:06 UTC (permalink / raw)
To: Andrew Lunn
Cc: Russell King (Oracle), Heiner Kallweit, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
Jonas Jelonek, Markus Stockhausen
On Thu, Jan 22, 2026 at 02:46:58PM +0100, Andrew Lunn wrote:
> > Only a small subset has been seen in the wild inside RealTek switches
> > for now. Most, of course, come with RealTek switches. However, it is
> > only very recently that RealTek offers 10G PHYs, so before vendors
> > were using 10G PHYs of other vendors, in practise I've only seen
> > Aquantia.
>
> ...
>
> > Yes, that does sound better, especially with your concern about
> > ->supported mask not necessarily being correct at probe time in mind.
>
> FYI: The Aquantia PHYs are particularly bad with ->supported.
Yes, but the PHY driver takes care of that and implements fixes via a
custom .get_features op. That's precisely why I'd like the MDIO polling
controller to rely on what the PHY driver has "discovered" and set in
->supported, instead of doing all that in duplicate open-coded MDIO bus
scanning and PHY probing in the MDIO driver, which is exactly why I
believe we should have API for that.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-01-22 16:07 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-22 4:00 [RFC PATCH v2 0/2] net: mdio: setup RealTek MDIO polling registers Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 1/2] net: phy: add (*register_phy)() hook to struct mii_bus Daniel Golle
2026-01-22 4:00 ` [RFC PATCH v2 2/2] net: mdio: rtl9300: setup PHY polling registers Daniel Golle
2026-01-22 12:10 ` Russell King (Oracle)
2026-01-22 13:22 ` Daniel Golle
2026-01-22 13:46 ` Andrew Lunn
2026-01-22 16:06 ` Daniel Golle
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox