* [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