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
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!
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.
> 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
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.
© 2016 - 2026 Red Hat, Inc.