From f369dd8ccf2dbce9166544e266e2b6ca974f98d9 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:18 +0200 Subject: [PATCH 1/7] net: mdio: realtek-rtl9300: Refactor otto_emdio_map_ports() This function has multiple issues: - It uses __free low level cleanups - It mixes "fwnode" and "of" functions Convert this to a uniform "of" usage and manual reference counting cleanup. With that also fix two subtle lookup bugs in the original code. mdio_dn = phy_dn->parent; if (mdio_dn->parent != dev->of_node) continue; This skips an API access and therefore misses reference counting. Additionally in the case of a very buggy device tree, phy_dn might be a root node. Looking up its grandparent leads to a NULL pointer access. Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-2-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 69 +++++++++++++++---------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index bafcd6afe619..a280363593d2 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -427,60 +427,75 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv */ static int otto_emdio_map_ports(struct device *dev) { + struct device_node *ports_dn, *phy_dn, *bus_dn, *ctrl_dn; struct otto_emdio_priv *priv = dev_get_drvdata(dev); struct device *parent = dev->parent; - int err; + u32 addr, bus, pn; + int err = 0; - struct fwnode_handle *ports __free(fwnode_handle) = - device_get_named_child_node(parent, "ethernet-ports"); - if (!ports) + ports_dn = of_get_child_by_name(parent->of_node, "ethernet-ports"); + if (!ports_dn) return dev_err_probe(dev, -EINVAL, "%pfwP missing ethernet-ports\n", dev_fwnode(parent)); - fwnode_for_each_child_node_scoped(ports, port) { - struct device_node *mdio_dn; - u32 addr; - u32 bus; - u32 pn; - - struct device_node *phy_dn __free(device_node) = - of_parse_phandle(to_of_node(port), "phy-handle", 0); + for_each_available_child_of_node_scoped(ports_dn, port_dn) { + ctrl_dn = NULL; + bus_dn = NULL; + phy_dn = of_parse_phandle(port_dn, "phy-handle", 0); /* skip ports without phys */ if (!phy_dn) continue; - mdio_dn = phy_dn->parent; + bus_dn = of_get_parent(phy_dn); + if (!bus_dn) + goto put_nodes; + + ctrl_dn = of_get_parent(bus_dn); /* only map ports that are connected to this mdio-controller */ - if (mdio_dn->parent != dev->of_node) - continue; + if (ctrl_dn != dev->of_node) + goto put_nodes; - err = fwnode_property_read_u32(port, "reg", &pn); + err = of_property_read_u32(port_dn, "reg", &pn); if (err) - return err; + goto put_nodes; - if (pn >= priv->info->num_ports) - return dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn); + if (pn >= priv->info->num_ports) { + err = dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn); + goto put_nodes; + } - if (test_bit(pn, priv->valid_ports)) - return dev_err_probe(dev, -EINVAL, "duplicated port number %d\n", pn); + if (test_bit(pn, priv->valid_ports)) { + err = dev_err_probe(dev, -EINVAL, "duplicated port number %d\n", pn); + goto put_nodes; + } - err = of_property_read_u32(mdio_dn, "reg", &bus); + err = of_property_read_u32(bus_dn, "reg", &bus); if (err) - return err; + goto put_nodes; - if (bus >= priv->info->num_buses) - return dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus); + if (bus >= priv->info->num_buses) { + err = dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus); + goto put_nodes; + } err = of_property_read_u32(phy_dn, "reg", &addr); if (err) - return err; + goto put_nodes; __set_bit(pn, priv->valid_ports); priv->smi_bus[pn] = bus; priv->smi_addr[pn] = addr; +put_nodes: + of_node_put(bus_dn); + of_node_put(phy_dn); + of_node_put(ctrl_dn); + if (err) + break; } - return 0; + of_node_put(ports_dn); + + return err; } static int otto_emdio_probe(struct platform_device *pdev) From 829ee0f8bc3920092b06d32d9e05328395ed5b77 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:19 +0200 Subject: [PATCH 2/7] net: mdio: realtek-rtl9300: harden otto_emdio_map_ports() Due to its design the MDIO driver needs to set up a port to bus/address mapping during probing. The "ethernet-ports" subnodes are scanned and from the "phy-handle" property the MDIO nodes are looked up. In case of a malformed device tree the driver might produce out-of-bounds accesses. The PHY address is not checked against the maximum supported address. Add a sanity check and drop the unneeded MAX_SMI_ADDR define. Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-3-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index a280363593d2..ba4151fbae0d 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -78,7 +78,6 @@ #define MAX_PORTS 28 #define MAX_SMI_BUSSES 4 -#define MAX_SMI_ADDR 0x1f #define RAW_PAGE(priv) ((priv)->info->num_pages - 1) @@ -430,8 +429,8 @@ static int otto_emdio_map_ports(struct device *dev) struct device_node *ports_dn, *phy_dn, *bus_dn, *ctrl_dn; struct otto_emdio_priv *priv = dev_get_drvdata(dev); struct device *parent = dev->parent; - u32 addr, bus, pn; - int err = 0; + int addr, err = 0; + u32 bus, pn; ports_dn = of_get_child_by_name(parent->of_node, "ethernet-ports"); if (!ports_dn) @@ -478,9 +477,11 @@ static int otto_emdio_map_ports(struct device *dev) goto put_nodes; } - err = of_property_read_u32(phy_dn, "reg", &addr); - if (err) + addr = of_mdio_parse_addr(dev, phy_dn); + if (addr < 0) { + err = addr; goto put_nodes; + } __set_bit(pn, priv->valid_ports); priv->smi_bus[pn] = bus; From 67885c0e3919be0f39d8f69e4eb446c9df8fa5fc Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:20 +0200 Subject: [PATCH 3/7] net: mdio: realtek-rtl9300: harden otto_emdio_probe_one() The bus probing of the MDIO driver uses a two stage approach. 1. The device tree "ethernet-ports" node is scanned to build a mapping between ports and PHYs. 2. The children of the device tree "controller" are scanned to create the individual MDIO buses. The first step already checks the consistency of the PHY and bus nodes that are linked via the ports. But it might miss a dangling bus child node that is not linked. Step two simply iterates over all bus child nodes and might read malformed data from nodes not checked in step one. Harden this and return a meaningful error message. Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-4-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index ba4151fbae0d..aa45b0e92216 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -380,7 +380,11 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv err = fwnode_property_read_u32(node, "reg", &mdio_bus); if (err) - return err; + return dev_err_probe(dev, err, "undefined smi bus number\n"); + + if (mdio_bus >= priv->info->num_buses) + return dev_err_probe(dev, -EINVAL, + "illegal (dangling) smi bus number %d\n", mdio_bus); /* The MDIO accesses from the kernel work with the PHY polling unit in * the switch. We need to tell the PPU to operate either in GPHY (i.e. From 94f05740ad16c5b2bf738af69b99cd0c48afd19e Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:21 +0200 Subject: [PATCH 4/7] net: mdio: realtek-rtl9300: relocate topology setup Until now the driver sets up the port to bus/address topology of the controller after all buses are set up via otto_emdio_probe_one(). This does not work for devices where U-Boot skips this setup. It is not only needed for the hardware internal background PHY polling engine but it is essential for access to the PHYs during probing. Depending on the SoC type there exist two different register arrays - Bus mapping registers (RTL930x, RTL931x) define to which bus the port is attached. E.g. [1] - Address mapping registers (RTL838x, RTL930x, RTL931x) define to which address of the bus the port is attached. E.g. [2] Relocate the topology setup and make it generic. For this - Define device-specific bus_base/addr_base attributes that give the register base address where the mapping lives. In case one or both are not given the SoC does not support this specific type of mapping. - Create a helper otto_emdio_setup_topology() that writes the detected topology to the registers. - Call this helper prior to otto_emdio_probe_one(). - Remove unneeded code from otto_emdio_9300_mdiobus_init(). - Due to the added prefixes, increase define indentation Subtle change: The old coding used regmap_bulk_write and silently wrote bus=0/address=0 to mapping registers for ports that are out of scope. The new coding leaves those untouched. [1] https://svanheule.net/realtek/longan/register/smi_port0_15_polling_sel [2] https://svanheule.net/realtek/longan/register/smi_port0_5_addr_ctrl Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-5-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 125 ++++++++++++++---------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index aa45b0e92216..58854c54ad99 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -51,34 +51,38 @@ #include #include -#define RTL9300_NUM_BUSES 4 -#define RTL9300_NUM_PAGES 4096 -#define RTL9300_NUM_PORTS 28 -#define SMI_GLB_CTRL 0xca00 -#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) -#define SMI_PORT0_15_POLLING_SEL 0xca08 -#define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 -#define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 -#define PHY_CTRL_REG_ADDR GENMASK(24, 20) -#define PHY_CTRL_PARK_PAGE GENMASK(19, 15) -#define PHY_CTRL_MAIN_PAGE GENMASK(14, 3) -#define PHY_CTRL_WRITE BIT(2) -#define PHY_CTRL_READ 0 -#define PHY_CTRL_TYPE_C45 BIT(1) -#define PHY_CTRL_TYPE_C22 0 -#define PHY_CTRL_CMD BIT(0) -#define PHY_CTRL_FAIL BIT(25) -#define RTL9300_SMI_ACCESS_PHY_CTRL_2 0xcb78 -#define PHY_CTRL_INDATA GENMASK(31, 16) -#define PHY_CTRL_DATA GENMASK(15, 0) -#define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c -#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) -#define PHY_CTRL_MMD_REG GENMASK(15, 0) -#define SMI_PORT0_5_ADDR_CTRL 0xcb80 +#define RTL9300_NUM_BUSES 4 +#define RTL9300_NUM_PAGES 4096 +#define RTL9300_NUM_PORTS 28 +#define SMI_GLB_CTRL 0xca00 +#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) +#define RTL9300_SMI_PORT0_15_POLLING_SEL 0xca08 +#define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 +#define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 +#define PHY_CTRL_REG_ADDR GENMASK(24, 20) +#define PHY_CTRL_PARK_PAGE GENMASK(19, 15) +#define PHY_CTRL_MAIN_PAGE GENMASK(14, 3) +#define PHY_CTRL_WRITE BIT(2) +#define PHY_CTRL_READ 0 +#define PHY_CTRL_TYPE_C45 BIT(1) +#define PHY_CTRL_TYPE_C22 0 +#define PHY_CTRL_CMD BIT(0) +#define PHY_CTRL_FAIL BIT(25) +#define RTL9300_SMI_ACCESS_PHY_CTRL_2 0xcb78 +#define PHY_CTRL_INDATA GENMASK(31, 16) +#define PHY_CTRL_DATA GENMASK(15, 0) +#define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c +#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) +#define PHY_CTRL_MMD_REG GENMASK(15, 0) +#define RTL9300_SMI_PORT0_5_ADDR_CTRL 0xcb80 -#define MAX_PORTS 28 -#define MAX_SMI_BUSSES 4 -#define RAW_PAGE(priv) ((priv)->info->num_pages - 1) +#define MAP_ADDRS_PER_REG 6 +#define MAP_BITS_PER_ADDR 5 +#define MAP_BITS_PER_BUS 2 +#define MAP_BUSES_PER_REG 16 +#define MAX_PORTS 28 +#define MAX_SMI_BUSSES 4 +#define RAW_PAGE(priv) ((priv)->info->num_pages - 1) struct otto_emdio_cmd_regs { @@ -89,6 +93,8 @@ struct otto_emdio_cmd_regs { }; struct otto_emdio_info { + u32 addr_map_base; + u32 bus_map_base; u32 cmd_fail; u32 cmd_read; u32 cmd_write; @@ -327,41 +333,54 @@ static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id, return ret; } +static int otto_emdio_write_mapping(struct otto_emdio_priv *priv, u32 base, u32 port, + u32 vals_per_reg, u32 bits_per_val, u32 value) +{ + u32 shift = (port % vals_per_reg) * bits_per_val; + u32 reg = base + (port / vals_per_reg) * 4; + u32 mask = GENMASK(bits_per_val - 1, 0); + + return regmap_update_bits(priv->regmap, reg, mask << shift, value << shift); +} + +static int otto_emdio_setup_topology(struct otto_emdio_priv *priv) +{ + const struct otto_emdio_info *info = priv->info; + u32 port; + int ret; + + for_each_set_bit(port, priv->valid_ports, info->num_ports) { + if (info->bus_map_base) { + ret = otto_emdio_write_mapping(priv, info->bus_map_base, port, + MAP_BUSES_PER_REG, MAP_BITS_PER_BUS, + priv->smi_bus[port]); + if (ret) + return ret; + } + if (info->addr_map_base) { + ret = otto_emdio_write_mapping(priv, info->addr_map_base, port, + MAP_ADDRS_PER_REG, MAP_BITS_PER_ADDR, + priv->smi_addr[port]); + if (ret) + return ret; + } + } + + return 0; +} + static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv) { u32 glb_ctrl_mask = 0, glb_ctrl_val = 0; struct regmap *regmap = priv->regmap; - u32 port_addr[5] = { 0 }; - u32 poll_sel[2] = { 0 }; int i, err; - /* Associate the port with the SMI interface and PHY */ - for_each_set_bit(i, priv->valid_ports, priv->info->num_ports) { - int pos; - - pos = (i % 6) * 5; - port_addr[i / 6] |= (priv->smi_addr[i] & 0x1f) << pos; - - pos = (i % 16) * 2; - poll_sel[i / 16] |= (priv->smi_bus[i] & 0x3) << pos; - } - /* Put the interfaces into C45 mode if required */ glb_ctrl_mask = GENMASK(19, 16); for (i = 0; i < priv->info->num_buses; i++) if (priv->smi_bus_is_c45[i]) glb_ctrl_val |= GLB_CTRL_INTF_SEL(i); - err = regmap_bulk_write(regmap, SMI_PORT0_5_ADDR_CTRL, - port_addr, 5); - if (err) - return err; - - err = regmap_bulk_write(regmap, SMI_PORT0_15_POLLING_SEL, - poll_sel, 2); - if (err) - return err; - err = regmap_update_bits(regmap, SMI_GLB_CTRL, glb_ctrl_mask, glb_ctrl_val); if (err) @@ -528,6 +547,10 @@ static int otto_emdio_probe(struct platform_device *pdev) if (err) return err; + err = otto_emdio_setup_topology(priv); + if (err) + return err; + device_for_each_child_node_scoped(dev, child) { err = otto_emdio_probe_one(dev, priv, child); if (err) @@ -542,6 +565,8 @@ static int otto_emdio_probe(struct platform_device *pdev) } static const struct otto_emdio_info otto_emdio_9300_info = { + .addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL, + .bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL, .cmd_fail = PHY_CTRL_FAIL, .cmd_read = PHY_CTRL_READ, .cmd_write = PHY_CTRL_WRITE, From 439aba443a8012b5a61bf327f75bec69be56ddd3 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:22 +0200 Subject: [PATCH 5/7] net: mdio: realtek-rtl9300: relocate c22/c45 device tree readout otto_emdio_map_ports() is the central place to lookup the topology and the properties of the Realtek ethernet MDIO controller from the device tree. Deviating from this the c22/c45 detection via "ethernet-phy-ieee802.3-c45" is running separately in otto_emdio_probe_one(). It loops over the same nodes, just at a later point in time. There is no benefit to divide this setup and to have a time window where the data structure is only filled partially. Additionally it uses the "fwnode" API. Consolidate the setup and convert it to the "of" API. Remark. This is a subtle change for dangling PHY nodes (not referenced by ethernet-ports). Before this commit all PHY nodes were evaluated for c45 setup, now only the referenced ones. Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-6-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index 58854c54ad99..e626dd7fb4b8 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -405,18 +405,6 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv return dev_err_probe(dev, -EINVAL, "illegal (dangling) smi bus number %d\n", mdio_bus); - /* The MDIO accesses from the kernel work with the PHY polling unit in - * the switch. We need to tell the PPU to operate either in GPHY (i.e. - * clause 22) or 10GPHY mode (i.e. clause 45). - * - * We select 10GPHY mode if there is at least one PHY that declares - * compatible = "ethernet-phy-ieee802.3-c45". This does mean we can't - * support both c45 and c22 on the same MDIO bus. - */ - fwnode_for_each_child_node_scoped(node, child) - if (fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45")) - priv->smi_bus_is_c45[mdio_bus] = true; - bus = devm_mdiobus_alloc_size(dev, sizeof(*chan)); if (!bus) return -ENOMEM; @@ -506,6 +494,15 @@ static int otto_emdio_map_ports(struct device *dev) goto put_nodes; } + /* + * The MDIO accesses from the kernel work with the PHY polling unit in the + * switch. The PPU either operates in GPHY (i.e. clause 22) or 10GPHY mode + * (i.e. clause 45). Select 10GPHY mode if there is at least one PHY that + * declares compatible = "ethernet-phy-ieee802.3-c45". + */ + if (of_device_is_compatible(phy_dn, "ethernet-phy-ieee802.3-c45")) + priv->smi_bus_is_c45[bus] = true; + __set_bit(pn, priv->valid_ports); priv->smi_bus[pn] = bus; priv->smi_addr[pn] = addr; From 56b4864150970ba834367fe74b37ce250419347e Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 3 Jun 2026 19:59:23 +0200 Subject: [PATCH 6/7] net: mdio: realtek-rtl9300: reorder controller setup After the former refactoring the existing otto_emdio_9300_mdiobus_init() contains only the c22/c45 bus mode setup. Like the topology setup this must run before bus registration. Otherwise the bus does not "speak" the right protocol for PHY setup. This setup is device-specific and other SoCs will need to set up other register bits in the controller in the future. Therefore - Relocate c22/c45 device tree readout to the very beginning of the probing - Add a new device-specific setup_controller() into the info structure. - Relocate otto_emdio_priv to satisfy the new info structure dependency. - Rename otto_emdio_9300_mdiobus_init accordingly and add it to the RTL9300 info structure. At the same time, adapt register naming for the function to make it clear that it only applies to this SoC. - Call setup_controller() prior to bus registration. Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-7-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 44 ++++++++++++++----------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index e626dd7fb4b8..bd361aa19739 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -54,8 +54,8 @@ #define RTL9300_NUM_BUSES 4 #define RTL9300_NUM_PAGES 4096 #define RTL9300_NUM_PORTS 28 -#define SMI_GLB_CTRL 0xca00 -#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) +#define RTL9300_SMI_GLB_CTRL 0xca00 +#define RTL9300_GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) #define RTL9300_SMI_PORT0_15_POLLING_SEL 0xca08 #define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 #define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 @@ -92,6 +92,17 @@ struct otto_emdio_cmd_regs { u32 port_mask_low; }; +struct otto_emdio_priv { + const struct otto_emdio_info *info; + struct regmap *regmap; + struct mutex lock; /* protect HW access */ + DECLARE_BITMAP(valid_ports, MAX_PORTS); + u8 smi_bus[MAX_PORTS]; + u8 smi_addr[MAX_PORTS]; + bool smi_bus_is_c45[MAX_SMI_BUSSES]; + struct mii_bus *bus[MAX_SMI_BUSSES]; +}; + struct otto_emdio_info { u32 addr_map_base; u32 bus_map_base; @@ -102,23 +113,13 @@ struct otto_emdio_info { u8 num_buses; u8 num_ports; u16 num_pages; + int (*setup_controller)(struct otto_emdio_priv *priv); int (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value); int (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value); int (*write_c22)(struct mii_bus *bus, int port, int regnum, u16 value); int (*write_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u16 value); }; -struct otto_emdio_priv { - const struct otto_emdio_info *info; - struct regmap *regmap; - struct mutex lock; /* protect HW access */ - DECLARE_BITMAP(valid_ports, MAX_PORTS); - u8 smi_bus[MAX_PORTS]; - u8 smi_addr[MAX_PORTS]; - bool smi_bus_is_c45[MAX_SMI_BUSSES]; - struct mii_bus *bus[MAX_SMI_BUSSES]; -}; - struct otto_emdio_chan { struct otto_emdio_priv *priv; u8 mdio_bus; @@ -369,7 +370,7 @@ static int otto_emdio_setup_topology(struct otto_emdio_priv *priv) return 0; } -static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv) +static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv) { u32 glb_ctrl_mask = 0, glb_ctrl_val = 0; struct regmap *regmap = priv->regmap; @@ -379,9 +380,9 @@ static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv) glb_ctrl_mask = GENMASK(19, 16); for (i = 0; i < priv->info->num_buses; i++) if (priv->smi_bus_is_c45[i]) - glb_ctrl_val |= GLB_CTRL_INTF_SEL(i); + glb_ctrl_val |= RTL9300_GLB_CTRL_INTF_SEL(i); - err = regmap_update_bits(regmap, SMI_GLB_CTRL, + err = regmap_update_bits(regmap, RTL9300_SMI_GLB_CTRL, glb_ctrl_mask, glb_ctrl_val); if (err) return err; @@ -548,16 +549,18 @@ static int otto_emdio_probe(struct platform_device *pdev) if (err) return err; + if (priv->info->setup_controller) { + err = priv->info->setup_controller(priv); + if (err) + return dev_err_probe(dev, err, "failed to setup MDIO bus controller\n"); + } + device_for_each_child_node_scoped(dev, child) { err = otto_emdio_probe_one(dev, priv, child); if (err) return err; } - err = otto_emdio_9300_mdiobus_init(priv); - if (err) - return dev_err_probe(dev, err, "failed to initialise MDIO bus controller\n"); - return 0; } @@ -576,6 +579,7 @@ static const struct otto_emdio_info otto_emdio_9300_info = { .num_buses = RTL9300_NUM_BUSES, .num_ports = RTL9300_NUM_PORTS, .num_pages = RTL9300_NUM_PAGES, + .setup_controller = otto_emdio_9300_setup_controller, .read_c22 = otto_emdio_9300_read_c22, .read_c45 = otto_emdio_9300_read_c45, .write_c22 = otto_emdio_9300_write_c22, From 50b682a5aed70847e0746db7f723d22bef1801d1 Mon Sep 17 00:00:00 2001 From: Manuel Stocker Date: Wed, 3 Jun 2026 19:59:24 +0200 Subject: [PATCH 7/7] net: mdio: realtek-rtl9300: Correctly handle ethernet-phy-package Realtek Otto switches usually make use of multiport PHYs (e.g. 8 port 1G RTL8218D or 4 port 2.5G RTL8224). The device tree can describe this fact via an "ethernet-phy-package" node that resides between the bus and the PHY node. When looking up the device tree bus node via the chain port->phy->parent the driver totally ignores the existence of a PHY package. Enhance the lookup to take care of this feature. Link: https://github.com/openwrt/openwrt/pull/23591 Signed-off-by: Manuel Stocker Signed-off-by: Markus Stockhausen Link: https://patch.msgid.link/20260603175924.123019-8-markus.stockhausen@gmx.de Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index bd361aa19739..92c8f2512476 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -432,6 +432,21 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv return 0; } +static struct device_node *otto_emdio_get_bus_node(struct device_node *dn) +{ + struct device_node *parent = of_get_parent(dn); + struct device_node *grandparent; + + if (parent && of_node_name_eq(parent, "ethernet-phy-package")) { + grandparent = of_get_parent(parent); + of_node_put(parent); + + return grandparent; + } + + return parent; +} + /* The mdio-controller is part of a switch block so we parse the sibling * ethernet-ports node and build a mapping of the switch port to MDIO bus/addr * based on the phy-handle. @@ -457,7 +472,7 @@ static int otto_emdio_map_ports(struct device *dev) if (!phy_dn) continue; - bus_dn = of_get_parent(phy_dn); + bus_dn = otto_emdio_get_bus_node(phy_dn); if (!bus_dn) goto put_nodes;