]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: mdio: backport upstream patches 23678/head
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Sat, 6 Jun 2026 06:33:27 +0000 (08:33 +0200)
committerMarkus Stockhausen <markus.stockhausen@gmx.de>
Sat, 6 Jun 2026 13:03:16 +0000 (15:03 +0200)
This backports the upstream efforts for the mdio driver. No downstream
functionality is affected as OpenWrt still uses the mdio-realtek-otto
driver.

Link: https://github.com/openwrt/openwrt/pull/23678
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
20 files changed:
target/linux/realtek/patches-6.18/031-01-v7.2-net-mdio-realtek-rtl9300-enhance-documentation-namin.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-02-v7.2-net-mdio-realtek-rtl9300-Add-device-specific-info-st.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-03-v7.2-net-mdio-realtek-rtl9300-Add-ports-to-info-structure.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-04-v7.2-net-mdio-realtek-rtl9300-Add-pages-to-info-structure.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-05-v7.2-net-mdio-realtek-rtl9300-Add-register-structure.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-06-v7.2-net-mdio-realtek-rtl9300-Add-command-C22-register.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-07-v7.2-net-mdio-realtek-rtl9300-Add-I-O-register.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-08-v7.2-net-mdio-realtek-rtl9300-Add-port-mask-register.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-09-v7.2-net-mdio-realtek-rtl9300-Link-I-O-functions-in-info-.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-10-v7.2-net-mdio-realtek-rtl9300-provide-generic-command-run.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-11-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-writ.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-12-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-13-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-14-v7.2-net-mdio-realtek-rtl9300-Refactor-otto_emdio_map_por.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-15-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_map_ports.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-16-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_probe_one.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-17-v7.2-net-mdio-realtek-rtl9300-relocate-topology-setup.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-18-v7.2-net-mdio-realtek-rtl9300-relocate-c22-c45-device-tre.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-19-v7.2-net-mdio-realtek-rtl9300-reorder-controller-setup.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/031-20-v7.2-net-mdio-realtek-rtl9300-Correctly-handle-ethernet-p.patch [new file with mode: 0644]

diff --git a/target/linux/realtek/patches-6.18/031-01-v7.2-net-mdio-realtek-rtl9300-enhance-documentation-namin.patch b/target/linux/realtek/patches-6.18/031-01-v7.2-net-mdio-realtek-rtl9300-enhance-documentation-namin.patch
new file mode 100644 (file)
index 0000000..58eff1e
--- /dev/null
@@ -0,0 +1,381 @@
+From d15c0d1df49480b8f7d7c6182880e353574c871d Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:10 +0200
+Subject: [PATCH 01/20] net: mdio: realtek-rtl9300: enhance documentation &
+ naming
+
+The Realtek ethernet MDIO driver currently only serves SOCs from the
+Realtek RTL930x series. This is only one lineup of the Realtek Otto
+switch series that also knows RTL838x, RTL839x, RTL931x devices.
+All of these share similar hardware with comparable MMIO access logic
+but have individual variations. Important to note
+
+- Controller works on switch ports instead of buses and addresses.
+- Devices incorporate additional MDIO hardware. E.g.
+  - an auxiliary MDIO controller for GPIO expanders [1]
+  - a MDIO style SerDes controller [2]
+
+To avoid future confusion enhance the driver documentation and
+function naming. Make clear what this driver is about and what
+parts are generic and what parts are device specific. For this
+rename the function and structure prefix as follows:
+
+- for generic functions use otto_emdio_
+- for device specific helpers use e.g. otto_emdio_9300_
+
+This prefix naming tries to align with the watchdog timer [3].
+It paves the way so that drivers for the other Realtek Otto MDIO
+controllers can be added in future commits using the same naming
+convention.
+
+Remark 1: The read/write functions are kept device specific for now
+because they will only fit the RTL930x SOCs. Renaming will take place
+as soon as the I/O handling will be generalized.
+
+Remark 2: The driver name "mdio-rtl9300" is kept for now.
+
+[1] https://git.openwrt.org/openwrt/openwrt/tree/target/linux/realtek/patches-6.18/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch
+[2] https://git.openwrt.org/openwrt/openwrt/tree/target/linux/realtek/files-6.18/drivers/net/mdio/mdio-realtek-otto-serdes.c
+[3] https://elixir.bootlin.com/linux/v7.0/source/drivers/watchdog/realtek_otto_wdt.c
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-2-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 139 ++++++++++++++----------
+ 1 file changed, 84 insertions(+), 55 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -1,11 +1,40 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ /*
+- * MDIO controller for RTL9300 switches with integrated SoC.
++ * Realtek switches of the Otto series (RTL838x, RTL839x, RTL930x and RTL931x SoCs) have multiple
++ * integrated MDIO controllers. This driver targets the ethernet MDIO controller. It serves only
++ * 1G/2.5G/10G ethernet PHYs attached to up to 4 individual buses.
+  *
+- * The MDIO communication is abstracted by the switch. At the software level
+- * communication uses the switch port to address the PHY. We work out the
+- * mapping based on the MDIO bus described in device tree and phandles on the
+- * ethernet-ports property.
++ * The controller is programmed through MMIO. The MDIO communication is abstracted by the hardware
++ * and uses the switch port number for its addressing. For this to work, mapping registers need to
++ * be setup in advance. With that the controller translates each port based I/O operation into the
++ * physical bus and address. This gives the following end-to-end communication
++ *
++ *     +----------+       +----------+           +----------+       +----------+
++ *     |  phydev  |  ...  |  phydev  |           |  phydev  |  ...  |  phydev  |
++ *     +----------+       +----------+           +----------+       +----------+
++ *              |                  |               |                  |
++ *   mii_bus 0  +------------------+               +------------------+  mii_bus 1
++ *                                 |               |
++ *           +-----------------------------------------------------+
++ *           |  MDIO driver                                        |
++ *           |                      translate bus/address -> port  |
++ *           +-----------------------------------------------------+
++ *                                        |                             Software
++ *                             - - - - - - - - - - - - - - - - - - - - - - - - -
++ *                                        |                             Hardware
++ *           +-----------------------------------------------------+
++ *           | MDIO controller                                     |
++ *           |                      translate port -> bus/address  |
++ *           +-----------------------------------------------------+
++ *                                 |               |
++ *       bus 0  +------------------+               +------------------+  bus 1
++ *              |                  |               |                  |
++ *     +----------+       +----------+           +----------+       +----------+
++ *     | PHY 0/1  |  ...  | PHY 0/31 |           | PHY 1/1  |  ...  | PHY 1/31 |
++ *     +----------+       +----------+           +----------+       +----------+
++ *
++ * The driver works out the mapping based on the MDIO bus described in device tree and phandles on
++ * the ethernet-ports property.
+  */
+ #include <linux/bitfield.h>
+@@ -48,7 +77,7 @@
+ #define MAX_SMI_BUSSES  4
+ #define MAX_SMI_ADDR  0x1f
+-struct rtl9300_mdio_priv {
++struct otto_emdio_priv {
+       struct regmap *regmap;
+       struct mutex lock; /* protect HW access */
+       DECLARE_BITMAP(valid_ports, MAX_PORTS);
+@@ -58,15 +87,15 @@ struct rtl9300_mdio_priv {
+       struct mii_bus *bus[MAX_SMI_BUSSES];
+ };
+-struct rtl9300_mdio_chan {
+-      struct rtl9300_mdio_priv *priv;
++struct otto_emdio_chan {
++      struct otto_emdio_priv *priv;
+       u8 mdio_bus;
+ };
+-static int rtl9300_mdio_phy_to_port(struct mii_bus *bus, int phy_id)
++static int otto_emdio_phy_to_port(struct mii_bus *bus, int phy_id)
+ {
+-      struct rtl9300_mdio_chan *chan = bus->priv;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_chan *chan = bus->priv;
++      struct otto_emdio_priv *priv;
+       int i;
+       priv = chan->priv;
+@@ -79,7 +108,7 @@ static int rtl9300_mdio_phy_to_port(stru
+       return -ENOENT;
+ }
+-static int rtl9300_mdio_wait_ready(struct rtl9300_mdio_priv *priv)
++static int otto_emdio_wait_ready(struct otto_emdio_priv *priv)
+ {
+       struct regmap *regmap = priv->regmap;
+       u32 val;
+@@ -90,10 +119,10 @@ static int rtl9300_mdio_wait_ready(struc
+                                       val, !(val & PHY_CTRL_CMD), 10, 1000);
+ }
+-static int rtl9300_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
++static int otto_emdio_9300_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+ {
+-      struct rtl9300_mdio_chan *chan = bus->priv;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_chan *chan = bus->priv;
++      struct otto_emdio_priv *priv;
+       struct regmap *regmap;
+       int port;
+       u32 val;
+@@ -102,12 +131,12 @@ static int rtl9300_mdio_read_c22(struct
+       priv = chan->priv;
+       regmap = priv->regmap;
+-      port = rtl9300_mdio_phy_to_port(bus, phy_id);
++      port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+               return port;
+       mutex_lock(&priv->lock);
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -123,7 +152,7 @@ static int rtl9300_mdio_read_c22(struct
+       if (err)
+               goto out_err;
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -139,10 +168,10 @@ out_err:
+       return err;
+ }
+-static int rtl9300_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
++static int otto_emdio_9300_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
+ {
+-      struct rtl9300_mdio_chan *chan = bus->priv;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_chan *chan = bus->priv;
++      struct otto_emdio_priv *priv;
+       struct regmap *regmap;
+       int port;
+       u32 val;
+@@ -151,12 +180,12 @@ static int rtl9300_mdio_write_c22(struct
+       priv = chan->priv;
+       regmap = priv->regmap;
+-      port = rtl9300_mdio_phy_to_port(bus, phy_id);
++      port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+               return port;
+       mutex_lock(&priv->lock);
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -194,10 +223,10 @@ out_err:
+       return err;
+ }
+-static int rtl9300_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
++static int otto_emdio_9300_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
+ {
+-      struct rtl9300_mdio_chan *chan = bus->priv;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_chan *chan = bus->priv;
++      struct otto_emdio_priv *priv;
+       struct regmap *regmap;
+       int port;
+       u32 val;
+@@ -206,12 +235,12 @@ static int rtl9300_mdio_read_c45(struct
+       priv = chan->priv;
+       regmap = priv->regmap;
+-      port = rtl9300_mdio_phy_to_port(bus, phy_id);
++      port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+               return port;
+       mutex_lock(&priv->lock);
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -231,7 +260,7 @@ static int rtl9300_mdio_read_c45(struct
+       if (err)
+               goto out_err;
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -247,11 +276,11 @@ out_err:
+       return err;
+ }
+-static int rtl9300_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
++static int otto_emdio_9300_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
+                                 int regnum, u16 value)
+ {
+-      struct rtl9300_mdio_chan *chan = bus->priv;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_chan *chan = bus->priv;
++      struct otto_emdio_priv *priv;
+       struct regmap *regmap;
+       int port;
+       u32 val;
+@@ -260,12 +289,12 @@ static int rtl9300_mdio_write_c45(struct
+       priv = chan->priv;
+       regmap = priv->regmap;
+-      port = rtl9300_mdio_phy_to_port(bus, phy_id);
++      port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+               return port;
+       mutex_lock(&priv->lock);
+-      err = rtl9300_mdio_wait_ready(priv);
++      err = otto_emdio_wait_ready(priv);
+       if (err)
+               goto out_err;
+@@ -307,7 +336,7 @@ out_err:
+       return err;
+ }
+-static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv)
++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;
+@@ -350,10 +379,10 @@ static int rtl9300_mdiobus_init(struct r
+       return 0;
+ }
+-static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_priv *priv,
+-                                   struct fwnode_handle *node)
++static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
++                               struct fwnode_handle *node)
+ {
+-      struct rtl9300_mdio_chan *chan;
++      struct otto_emdio_chan *chan;
+       struct mii_bus *bus;
+       u32 mdio_bus;
+       int err;
+@@ -380,11 +409,11 @@ static int rtl9300_mdiobus_probe_one(str
+       bus->name = "Realtek Switch MDIO Bus";
+       if (priv->smi_bus_is_c45[mdio_bus]) {
+-              bus->read_c45 = rtl9300_mdio_read_c45;
+-              bus->write_c45 =  rtl9300_mdio_write_c45;
++              bus->read_c45 = otto_emdio_9300_read_c45;
++              bus->write_c45 = otto_emdio_9300_write_c45;
+       } else {
+-              bus->read = rtl9300_mdio_read_c22;
+-              bus->write = rtl9300_mdio_write_c22;
++              bus->read = otto_emdio_9300_read_c22;
++              bus->write = otto_emdio_9300_write_c22;
+       }
+       bus->parent = dev;
+       chan = bus->priv;
+@@ -404,9 +433,9 @@ static int rtl9300_mdiobus_probe_one(str
+  * ethernet-ports node and build a mapping of the switch port to MDIO bus/addr
+  * based on the phy-handle.
+  */
+-static int rtl9300_mdiobus_map_ports(struct device *dev)
++static int otto_emdio_map_ports(struct device *dev)
+ {
+-      struct rtl9300_mdio_priv *priv = dev_get_drvdata(dev);
++      struct otto_emdio_priv *priv = dev_get_drvdata(dev);
+       struct device *parent = dev->parent;
+       int err;
+@@ -462,10 +491,10 @@ static int rtl9300_mdiobus_map_ports(str
+       return 0;
+ }
+-static int rtl9300_mdiobus_probe(struct platform_device *pdev)
++static int otto_emdio_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+-      struct rtl9300_mdio_priv *priv;
++      struct otto_emdio_priv *priv;
+       int err;
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+@@ -482,38 +511,38 @@ static int rtl9300_mdiobus_probe(struct
+       platform_set_drvdata(pdev, priv);
+-      err = rtl9300_mdiobus_map_ports(dev);
++      err = otto_emdio_map_ports(dev);
+       if (err)
+               return err;
+       device_for_each_child_node_scoped(dev, child) {
+-              err = rtl9300_mdiobus_probe_one(dev, priv, child);
++              err = otto_emdio_probe_one(dev, priv, child);
+               if (err)
+                       return err;
+       }
+-      err = rtl9300_mdiobus_init(priv);
++      err = otto_emdio_9300_mdiobus_init(priv);
+       if (err)
+               return dev_err_probe(dev, err, "failed to initialise MDIO bus controller\n");
+       return 0;
+ }
+-static const struct of_device_id rtl9300_mdio_ids[] = {
++static const struct of_device_id otto_emdio_ids[] = {
+       { .compatible = "realtek,rtl9301-mdio" },
+       {}
+ };
+-MODULE_DEVICE_TABLE(of, rtl9300_mdio_ids);
++MODULE_DEVICE_TABLE(of, otto_emdio_ids);
+-static struct platform_driver rtl9300_mdio_driver = {
+-      .probe = rtl9300_mdiobus_probe,
++static struct platform_driver otto_emdio_driver = {
++      .probe = otto_emdio_probe,
+       .driver = {
+               .name = "mdio-rtl9300",
+-              .of_match_table = rtl9300_mdio_ids,
++              .of_match_table = otto_emdio_ids,
+       },
+ };
+-module_platform_driver(rtl9300_mdio_driver);
++module_platform_driver(otto_emdio_driver);
+ MODULE_DESCRIPTION("RTL9300 MDIO driver");
+ MODULE_LICENSE("GPL");
diff --git a/target/linux/realtek/patches-6.18/031-02-v7.2-net-mdio-realtek-rtl9300-Add-device-specific-info-st.patch b/target/linux/realtek/patches-6.18/031-02-v7.2-net-mdio-realtek-rtl9300-Add-device-specific-info-st.patch
new file mode 100644 (file)
index 0000000..50a681a
--- /dev/null
@@ -0,0 +1,84 @@
+From 315a02b39e8c4d1e8aebc8c3cc1e14e193f116ae Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:11 +0200
+Subject: [PATCH 02/20] net: mdio: realtek-rtl9300: Add device specific info
+ structure
+
+Device properties of the RTL930x SOCs are hardcoded into the MDIO driver.
+This must be relaxed to support additional devices like the RTL838x or
+RTL839x. These do not have 4 SMI buses but 1 or 2 instead.
+
+To support multiple devices establish an info structure that contains
+individual variations of each series. As a first use case add the number
+of buses into this structure and use it where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-3-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -51,6 +51,7 @@
+ #include <linux/property.h>
+ #include <linux/regmap.h>
++#define RTL9300_NUM_BUSES             4
+ #define SMI_GLB_CTRL                  0xca00
+ #define   GLB_CTRL_INTF_SEL(intf)     BIT(16 + (intf))
+ #define SMI_PORT0_15_POLLING_SEL      0xca08
+@@ -77,7 +78,12 @@
+ #define MAX_SMI_BUSSES  4
+ #define MAX_SMI_ADDR  0x1f
++struct otto_emdio_info {
++      u8 num_buses;
++};
++
+ 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);
+@@ -357,7 +363,7 @@ static int otto_emdio_9300_mdiobus_init(
+       /* Put the interfaces into C45 mode if required */
+       glb_ctrl_mask = GENMASK(19, 16);
+-      for (i = 0; i < MAX_SMI_BUSSES; i++)
++      for (i = 0; i < priv->info->num_buses; i++)
+               if (priv->smi_bus_is_c45[i])
+                       glb_ctrl_val |= GLB_CTRL_INTF_SEL(i);
+@@ -476,7 +482,7 @@ static int otto_emdio_map_ports(struct d
+               if (err)
+                       return err;
+-              if (bus >= MAX_SMI_BUSSES)
++              if (bus >= priv->info->num_buses)
+                       return dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus);
+               err = of_property_read_u32(phy_dn, "reg", &addr);
+@@ -505,6 +511,7 @@ static int otto_emdio_probe(struct platf
+       if (err)
+               return err;
++      priv->info = device_get_match_data(dev);
+       priv->regmap = syscon_node_to_regmap(dev->parent->of_node);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+@@ -528,8 +535,12 @@ static int otto_emdio_probe(struct platf
+       return 0;
+ }
++static const struct otto_emdio_info otto_emdio_9300_info = {
++      .num_buses = RTL9300_NUM_BUSES,
++};
++
+ static const struct of_device_id otto_emdio_ids[] = {
+-      { .compatible = "realtek,rtl9301-mdio" },
++      { .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info },
+       {}
+ };
+ MODULE_DEVICE_TABLE(of, otto_emdio_ids);
diff --git a/target/linux/realtek/patches-6.18/031-03-v7.2-net-mdio-realtek-rtl9300-Add-ports-to-info-structure.patch b/target/linux/realtek/patches-6.18/031-03-v7.2-net-mdio-realtek-rtl9300-Add-ports-to-info-structure.patch
new file mode 100644 (file)
index 0000000..c38fb5d
--- /dev/null
@@ -0,0 +1,79 @@
+From 215701873a7ed1214bba82c8fadcd2583d0246c3 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:12 +0200
+Subject: [PATCH 03/20] net: mdio: realtek-rtl9300: Add ports to info structure
+
+The ethernet MDIO controller in the Realtek Otto series has a very special
+command register style. Instead of working with bus/address it works on
+ethernet port numbers. For this the controller is initialized via mapping
+registers that tell which port is mapped to which bus/address. Every
+request to the driver is then converted as follows
+
+1. Kernel calls driver with bus/address
+2. Driver converts bus/address to port and issues command
+3. Hardware maps port back to bus/address
+
+The number of ports is different for each device. Make this configurable
+by adding a property to the info structure. Switch the existing usage of
+MAX_PORTS to this new property where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-4-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -52,6 +52,7 @@
+ #include <linux/regmap.h>
+ #define RTL9300_NUM_BUSES             4
++#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
+@@ -80,6 +81,7 @@
+ struct otto_emdio_info {
+       u8 num_buses;
++      u8 num_ports;
+ };
+ struct otto_emdio_priv {
+@@ -106,7 +108,7 @@ static int otto_emdio_phy_to_port(struct
+       priv = chan->priv;
+-      for_each_set_bit(i, priv->valid_ports, MAX_PORTS)
++      for_each_set_bit(i, priv->valid_ports, priv->info->num_ports)
+               if (priv->smi_bus[i] == chan->mdio_bus &&
+                   priv->smi_addr[i] == phy_id)
+                       return i;
+@@ -351,7 +353,7 @@ static int otto_emdio_9300_mdiobus_init(
+       int i, err;
+       /* Associate the port with the SMI interface and PHY */
+-      for_each_set_bit(i, priv->valid_ports, MAX_PORTS) {
++      for_each_set_bit(i, priv->valid_ports, priv->info->num_ports) {
+               int pos;
+               pos = (i % 6) * 5;
+@@ -472,7 +474,7 @@ static int otto_emdio_map_ports(struct d
+               if (err)
+                       return err;
+-              if (pn >= MAX_PORTS)
++              if (pn >= priv->info->num_ports)
+                       return dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn);
+               if (test_bit(pn, priv->valid_ports))
+@@ -537,6 +539,7 @@ static int otto_emdio_probe(struct platf
+ static const struct otto_emdio_info otto_emdio_9300_info = {
+       .num_buses = RTL9300_NUM_BUSES,
++      .num_ports = RTL9300_NUM_PORTS,
+ };
+ static const struct of_device_id otto_emdio_ids[] = {
diff --git a/target/linux/realtek/patches-6.18/031-04-v7.2-net-mdio-realtek-rtl9300-Add-pages-to-info-structure.patch b/target/linux/realtek/patches-6.18/031-04-v7.2-net-mdio-realtek-rtl9300-Add-pages-to-info-structure.patch
new file mode 100644 (file)
index 0000000..2c39877
--- /dev/null
@@ -0,0 +1,90 @@
+From 38c9d5b644049e9e78fafa385d420b2ae8485b81 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:13 +0200
+Subject: [PATCH 04/20] net: mdio: realtek-rtl9300: Add pages to info structure
+
+The Realtek ethernet MDIO controller has a proprietary paging feature
+that is closely aligned with Realtek based PHYs. These PHY know "pages"
+for C22 access. Those can be switched via reads/writes to register 31.
+Usually the paged access must be programmed in four steps.
+
+1. read/save page register
+2. change "page" register 31
+3. read/write data register (on the given page)
+4. restore page register
+
+The controller can run all this in hardware with one single request
+from the driver. It is given the page, the register and the data
+and takes care of all the rest. This reduces CPU load. The number
+of supported pages depend on the model. This is either 4096 for low
+port count SOCs (up to 28 ports) or 8192 for high port count SOCs
+(up to 56 ports).
+
+There is however one special page that allows to pass through all C22
+commands directly to the PHY - without any caching. This so called raw
+page is dependent of the hardware. It is the highest supported page
+number minus 1.
+
+Provide the number of supported pages as a device specific property.
+This new "num_pages" aligns with the existing properties and gives
+an better insight into the hardware layout than just defining the
+number of the raw page. The later directly derives from that and
+can be accessed with the new RAW_PAGE() macro. Make use of it where
+needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-5-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -52,6 +52,7 @@
+ #include <linux/regmap.h>
+ #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))
+@@ -78,10 +79,12 @@
+ #define MAX_PORTS       28
+ #define MAX_SMI_BUSSES  4
+ #define MAX_SMI_ADDR  0x1f
++#define RAW_PAGE(priv)        ((priv)->info->num_pages - 1)
+ struct otto_emdio_info {
+       u8 num_buses;
+       u8 num_ports;
++      u16 num_pages;
+ };
+ struct otto_emdio_priv {
+@@ -154,7 +157,7 @@ static int otto_emdio_9300_read_c22(stru
+       val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+             FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-            FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) |
++            FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+             PHY_CTRL_READ | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+       err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
+       if (err)
+@@ -207,7 +210,7 @@ static int otto_emdio_9300_write_c22(str
+       val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+             FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-            FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) |
++            FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+             PHY_CTRL_WRITE | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+       err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
+       if (err)
+@@ -540,6 +543,7 @@ static int otto_emdio_probe(struct platf
+ 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,
+ };
+ static const struct of_device_id otto_emdio_ids[] = {
diff --git a/target/linux/realtek/patches-6.18/031-05-v7.2-net-mdio-realtek-rtl9300-Add-register-structure.patch b/target/linux/realtek/patches-6.18/031-05-v7.2-net-mdio-realtek-rtl9300-Add-register-structure.patch
new file mode 100644 (file)
index 0000000..9199bd4
--- /dev/null
@@ -0,0 +1,109 @@
+From 62baf5f1d80fe4f83099d52ac12e1337dd9fa9fc Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:14 +0200
+Subject: [PATCH 05/20] net: mdio: realtek-rtl9300: Add register structure
+
+The MDIO controller of the Realtek Otto switches has either 4 or 7 command
+registers. This depends on the number of supported ports. These registers
+are "scattered" around the MMIO block and their addresses depend on the
+specific model.
+
+Nevertheless all command registers share a common pattern:
+
+- A mask register with one bit per addressed port
+  (remark: the driver internally works on ports instead of bus/address)
+- A I/O data register that transfers the to be read/written data
+- A C45 registers that takes devnum and regnum
+- A C22 register that also includes run and status bits
+  (remark: this also takes the Realtek proprietary C22 PHY page)
+
+Provide an additional structure for these command registers so it can be
+reused in two places.
+
+1. For defining the register addresses in the regmap.
+2. For defining the to be read/written register data
+
+This will finally result in access patterns like
+
+static int otto_emdio_run_cmd(u32 cmd,
+                              struct rtl_mdio_cmd_regs *cmd_regs, ...)
+{
+  regmap_write(regmap, priv->info->reg->cmd_regs.c45_data,
+                       cmd_regs->c45_data);
+  ...
+}
+
+static int otto_emdio_9300_write_c45(...)
+{
+  struct otto_emdio_cmd_regs cmd_regs = {
+    .c45_data  = ...
+    .io_data   = ...,
+    .port_mask = ...,
+  };
+
+  return otto_emdio_run_cmd(RTL9300_CMD_WRITE_C45, &cmd_regs, ...);
+}
+
+As a first step start with the C45 register. This one takes the
+devnum/regnum data that is stored in the high/low 16 bits.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-6-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -71,7 +71,7 @@
+ #define SMI_ACCESS_PHY_CTRL_2         0xcb78
+ #define   PHY_CTRL_INDATA             GENMASK(31, 16)
+ #define   PHY_CTRL_DATA                       GENMASK(15, 0)
+-#define SMI_ACCESS_PHY_CTRL_3         0xcb7c
++#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
+@@ -81,7 +81,13 @@
+ #define MAX_SMI_ADDR  0x1f
+ #define RAW_PAGE(priv)        ((priv)->info->num_pages - 1)
++
++struct otto_emdio_cmd_regs {
++      u32 c45_data;
++};
++
+ struct otto_emdio_info {
++      struct otto_emdio_cmd_regs cmd_regs;
+       u8 num_buses;
+       u8 num_ports;
+       u16 num_pages;
+@@ -262,7 +268,7 @@ static int otto_emdio_9300_read_c45(stru
+       val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+             FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val);
++      err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
+       if (err)
+               goto out_err;
+@@ -320,7 +326,7 @@ static int otto_emdio_9300_write_c45(str
+       val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+             FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val);
++      err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
+       if (err)
+               goto out_err;
+@@ -541,6 +547,9 @@ static int otto_emdio_probe(struct platf
+ }
+ static const struct otto_emdio_info otto_emdio_9300_info = {
++      .cmd_regs = {
++              .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
++      },
+       .num_buses = RTL9300_NUM_BUSES,
+       .num_ports = RTL9300_NUM_PORTS,
+       .num_pages = RTL9300_NUM_PAGES,
diff --git a/target/linux/realtek/patches-6.18/031-06-v7.2-net-mdio-realtek-rtl9300-Add-command-C22-register.patch b/target/linux/realtek/patches-6.18/031-06-v7.2-net-mdio-realtek-rtl9300-Add-command-C22-register.patch
new file mode 100644 (file)
index 0000000..1bc7d4e
--- /dev/null
@@ -0,0 +1,178 @@
+From 8bf7e2b133db0ec690268a0afb0ee9471f4fbb52 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:15 +0200
+Subject: [PATCH 06/20] net: mdio: realtek-rtl9300: Add command/C22 register
+
+Command issuing/status bits and C22 data share the same register. In the
+future the number of places where this register is used will be:
+
+- One generic command helper/runner for all devices that will access the
+  command bits of the register
+- 8 device specific C22 read/write functions that will access the C22
+  data fields.
+
+Thus name the register c22_data to align with the existing c45_data
+register. This way all device specific helpers will have a common
+view on the to-be-fed data. Add the register to the existing structure
+and make use of it where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-7-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 38 +++++++++++++------------
+ 1 file changed, 20 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -58,7 +58,7 @@
+ #define   GLB_CTRL_INTF_SEL(intf)     BIT(16 + (intf))
+ #define SMI_PORT0_15_POLLING_SEL      0xca08
+ #define SMI_ACCESS_PHY_CTRL_0         0xcb70
+-#define SMI_ACCESS_PHY_CTRL_1         0xcb74
++#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)
+@@ -83,6 +83,7 @@
+ struct otto_emdio_cmd_regs {
++      u32 c22_data;
+       u32 c45_data;
+ };
+@@ -128,12 +129,12 @@ static int otto_emdio_phy_to_port(struct
+ static int otto_emdio_wait_ready(struct otto_emdio_priv *priv)
+ {
+       struct regmap *regmap = priv->regmap;
+-      u32 val;
++      u32 cmd_reg, val;
+       lockdep_assert_held(&priv->lock);
++      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+-      return regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
+-                                      val, !(val & PHY_CTRL_CMD), 10, 1000);
++      return regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 1000);
+ }
+ static int otto_emdio_9300_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+@@ -141,12 +142,13 @@ static int otto_emdio_9300_read_c22(stru
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
+       struct regmap *regmap;
++      u32 cmd_reg, val;
+       int port;
+-      u32 val;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+@@ -165,7 +167,7 @@ static int otto_emdio_9300_read_c22(stru
+             FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+             FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+             PHY_CTRL_READ | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
++      err = regmap_write(regmap, cmd_reg, val);
+       if (err)
+               goto out_err;
+@@ -190,12 +192,13 @@ static int otto_emdio_9300_write_c22(str
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
+       struct regmap *regmap;
++      u32 cmd_reg, val;
+       int port;
+-      u32 val;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+@@ -218,12 +221,11 @@ static int otto_emdio_9300_write_c22(str
+             FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+             FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+             PHY_CTRL_WRITE | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val);
++      err = regmap_write(regmap, cmd_reg, val);
+       if (err)
+               goto out_err;
+-      err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
+-                                     val, !(val & PHY_CTRL_CMD), 10, 100);
++      err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
+       if (err)
+               goto out_err;
+@@ -245,12 +247,13 @@ static int otto_emdio_9300_read_c45(stru
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
+       struct regmap *regmap;
++      u32 cmd_reg, val;
+       int port;
+-      u32 val;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+@@ -272,8 +275,7 @@ static int otto_emdio_9300_read_c45(stru
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1,
+-                         PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD);
++      err = regmap_write(regmap, cmd_reg, PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD);
+       if (err)
+               goto out_err;
+@@ -299,12 +301,13 @@ static int otto_emdio_9300_write_c45(str
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
+       struct regmap *regmap;
++      u32 cmd_reg, val;
+       int port;
+-      u32 val;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+@@ -330,13 +333,11 @@ static int otto_emdio_9300_write_c45(str
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1,
+-                         PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD);
++      err = regmap_write(regmap, cmd_reg, PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD);
+       if (err)
+               goto out_err;
+-      err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1,
+-                                     val, !(val & PHY_CTRL_CMD), 10, 100);
++      err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
+       if (err)
+               goto out_err;
+@@ -548,6 +549,7 @@ static int otto_emdio_probe(struct platf
+ static const struct otto_emdio_info otto_emdio_9300_info = {
+       .cmd_regs = {
++              .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+               .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
+       },
+       .num_buses = RTL9300_NUM_BUSES,
diff --git a/target/linux/realtek/patches-6.18/031-07-v7.2-net-mdio-realtek-rtl9300-Add-I-O-register.patch b/target/linux/realtek/patches-6.18/031-07-v7.2-net-mdio-realtek-rtl9300-Add-I-O-register.patch
new file mode 100644 (file)
index 0000000..6408a8f
--- /dev/null
@@ -0,0 +1,161 @@
+From ffc92cd009145eb5f85d913fbbc67fd24b601080 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:16 +0200
+Subject: [PATCH 07/20] net: mdio: realtek-rtl9300: Add I/O register
+
+The MDIO data that needs to be written or read to registers of the
+controller is handled by an I/O register. Add that to the register
+structure and make use of it where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-8-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 28 +++++++++++++++----------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -68,7 +68,7 @@
+ #define   PHY_CTRL_TYPE_C22           0
+ #define   PHY_CTRL_CMD                        BIT(0)
+ #define   PHY_CTRL_FAIL                       BIT(25)
+-#define SMI_ACCESS_PHY_CTRL_2         0xcb78
++#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
+@@ -85,6 +85,7 @@
+ struct otto_emdio_cmd_regs {
+       u32 c22_data;
+       u32 c45_data;
++      u32 io_data;
+ };
+ struct otto_emdio_info {
+@@ -141,13 +142,14 @@ static int otto_emdio_9300_read_c22(stru
+ {
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
++      u32 io_reg, cmd_reg, val;
+       struct regmap *regmap;
+-      u32 cmd_reg, val;
+       int port;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      io_reg = priv->info->cmd_regs.io_data;
+       cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+@@ -159,7 +161,7 @@ static int otto_emdio_9300_read_c22(stru
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, port));
++      err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, port));
+       if (err)
+               goto out_err;
+@@ -175,7 +177,7 @@ static int otto_emdio_9300_read_c22(stru
+       if (err)
+               goto out_err;
+-      err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val);
++      err = regmap_read(regmap, io_reg, &val);
+       if (err)
+               goto out_err;
+@@ -191,13 +193,14 @@ static int otto_emdio_9300_write_c22(str
+ {
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
++      u32 io_reg, cmd_reg, val;
+       struct regmap *regmap;
+-      u32 cmd_reg, val;
+       int port;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      io_reg = priv->info->cmd_regs.io_data;
+       cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+@@ -213,7 +216,7 @@ static int otto_emdio_9300_write_c22(str
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, value));
++      err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, value));
+       if (err)
+               goto out_err;
+@@ -246,13 +249,14 @@ static int otto_emdio_9300_read_c45(stru
+ {
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
++      u32 io_reg, cmd_reg, val;
+       struct regmap *regmap;
+-      u32 cmd_reg, val;
+       int port;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      io_reg = priv->info->cmd_regs.io_data;
+       cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+@@ -265,7 +269,7 @@ static int otto_emdio_9300_read_c45(stru
+               goto out_err;
+       val = FIELD_PREP(PHY_CTRL_INDATA, port);
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val);
++      err = regmap_write(regmap, io_reg, val);
+       if (err)
+               goto out_err;
+@@ -283,7 +287,7 @@ static int otto_emdio_9300_read_c45(stru
+       if (err)
+               goto out_err;
+-      err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val);
++      err = regmap_read(regmap, io_reg, &val);
+       if (err)
+               goto out_err;
+@@ -300,13 +304,14 @@ static int otto_emdio_9300_write_c45(str
+ {
+       struct otto_emdio_chan *chan = bus->priv;
+       struct otto_emdio_priv *priv;
++      u32 io_reg, cmd_reg, val;
+       struct regmap *regmap;
+-      u32 cmd_reg, val;
+       int port;
+       int err;
+       priv = chan->priv;
+       regmap = priv->regmap;
++      io_reg = priv->info->cmd_regs.io_data;
+       cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+       port = otto_emdio_phy_to_port(bus, phy_id);
+@@ -323,7 +328,7 @@ static int otto_emdio_9300_write_c45(str
+               goto out_err;
+       val = FIELD_PREP(PHY_CTRL_INDATA, value);
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val);
++      err = regmap_write(regmap, io_reg, val);
+       if (err)
+               goto out_err;
+@@ -551,6 +556,7 @@ static const struct otto_emdio_info otto
+       .cmd_regs = {
+               .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+               .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
++              .io_data = RTL9300_SMI_ACCESS_PHY_CTRL_2,
+       },
+       .num_buses = RTL9300_NUM_BUSES,
+       .num_ports = RTL9300_NUM_PORTS,
diff --git a/target/linux/realtek/patches-6.18/031-08-v7.2-net-mdio-realtek-rtl9300-Add-port-mask-register.patch b/target/linux/realtek/patches-6.18/031-08-v7.2-net-mdio-realtek-rtl9300-Add-port-mask-register.patch
new file mode 100644 (file)
index 0000000..6137842
--- /dev/null
@@ -0,0 +1,71 @@
+From cad6a373f7f69254cb57e76e06f7734777575555 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:17 +0200
+Subject: [PATCH 08/20] net: mdio: realtek-rtl9300: Add port mask register
+
+MDIO controller commands work on ports. These are converted by
+the driver and hardware forth and back to bus/address. For write
+commands a port mask register needs to be filled. Each bit tells the
+controller to which PHY the write will be issued. Setting multiple
+bits allows to program multiple PHYs in one step. The driver will
+not make use of this parallel write feature. But it must at least
+fill the bit of the target port that it wants to write to.
+
+Depending on the SOC type and the number of supported PHYs this is
+either one or two 32 bit port mask registers. The driver currently only
+supports the 28 port RTL930x SOCs. So provide only the mask register
+for the lower 32 ports. Add it to the register structure and make use
+of it where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-9-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -57,7 +57,7 @@
+ #define SMI_GLB_CTRL                  0xca00
+ #define   GLB_CTRL_INTF_SEL(intf)     BIT(16 + (intf))
+ #define SMI_PORT0_15_POLLING_SEL      0xca08
+-#define SMI_ACCESS_PHY_CTRL_0         0xcb70
++#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)
+@@ -86,6 +86,7 @@ struct otto_emdio_cmd_regs {
+       u32 c22_data;
+       u32 c45_data;
+       u32 io_data;
++      u32 port_mask_low;
+ };
+ struct otto_emdio_info {
+@@ -212,7 +213,7 @@ static int otto_emdio_9300_write_c22(str
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port));
++      err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
+       if (err)
+               goto out_err;
+@@ -323,7 +324,7 @@ static int otto_emdio_9300_write_c45(str
+       if (err)
+               goto out_err;
+-      err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port));
++      err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
+       if (err)
+               goto out_err;
+@@ -557,6 +558,7 @@ static const struct otto_emdio_info otto
+               .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+               .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
+               .io_data = RTL9300_SMI_ACCESS_PHY_CTRL_2,
++              .port_mask_low = RTL9300_SMI_ACCESS_PHY_CTRL_0,
+       },
+       .num_buses = RTL9300_NUM_BUSES,
+       .num_ports = RTL9300_NUM_PORTS,
diff --git a/target/linux/realtek/patches-6.18/031-09-v7.2-net-mdio-realtek-rtl9300-Link-I-O-functions-in-info-.patch b/target/linux/realtek/patches-6.18/031-09-v7.2-net-mdio-realtek-rtl9300-Link-I-O-functions-in-info-.patch
new file mode 100644 (file)
index 0000000..a5d397a
--- /dev/null
@@ -0,0 +1,66 @@
+From 826a1926f084037b91415a53a29ce93264f08ada Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Thu, 21 May 2026 19:59:18 +0200
+Subject: [PATCH 09/20] net: mdio: realtek-rtl9300: Link I/O functions in info
+ structure
+
+The MDIO controller registers of the different devices of the
+Realtek Otto switch series are very similar. Nevertheless each
+device will need to feed the whole command data distributed over
+the controller registers slightly different.
+
+E.g. the combined C22/command register has different field layouts.
+On RTL930x bits 24-20 define the to-be-accessed C22 register number
+while on RTL839x this is stored in bits 9-5.
+
+Thus there need to be device specific read/write functions that
+are called dynamically. Add them into the info structure and make
+use of them where needed.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260521175918.1494797-10-markus.stockhausen@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -94,6 +94,10 @@ struct otto_emdio_info {
+       u8 num_buses;
+       u8 num_ports;
+       u16 num_pages;
++      int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
++      int (*read_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum);
++      int (*write_c22)(struct mii_bus *bus, int phy_id, int regnum, u16 value);
++      int (*write_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum, u16 value);
+ };
+ struct otto_emdio_priv {
+@@ -433,11 +437,11 @@ static int otto_emdio_probe_one(struct d
+       bus->name = "Realtek Switch MDIO Bus";
+       if (priv->smi_bus_is_c45[mdio_bus]) {
+-              bus->read_c45 = otto_emdio_9300_read_c45;
+-              bus->write_c45 = otto_emdio_9300_write_c45;
++              bus->read_c45 = priv->info->read_c45;
++              bus->write_c45 = priv->info->write_c45;
+       } else {
+-              bus->read = otto_emdio_9300_read_c22;
+-              bus->write = otto_emdio_9300_write_c22;
++              bus->read = priv->info->read_c22;
++              bus->write = priv->info->write_c22;
+       }
+       bus->parent = dev;
+       chan = bus->priv;
+@@ -563,6 +567,10 @@ static const struct otto_emdio_info otto
+       .num_buses = RTL9300_NUM_BUSES,
+       .num_ports = RTL9300_NUM_PORTS,
+       .num_pages = RTL9300_NUM_PAGES,
++      .read_c22 = otto_emdio_9300_read_c22,
++      .read_c45 = otto_emdio_9300_read_c45,
++      .write_c22 = otto_emdio_9300_write_c22,
++      .write_c45 = otto_emdio_9300_write_c45,
+ };
+ static const struct of_device_id otto_emdio_ids[] = {
diff --git a/target/linux/realtek/patches-6.18/031-10-v7.2-net-mdio-realtek-rtl9300-provide-generic-command-run.patch b/target/linux/realtek/patches-6.18/031-10-v7.2-net-mdio-realtek-rtl9300-provide-generic-command-run.patch
new file mode 100644 (file)
index 0000000..29c82b6
--- /dev/null
@@ -0,0 +1,241 @@
+From 9ccb438210c4838962dfa1a10d4c198391dcc284 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 27 May 2026 18:34:46 +0200
+Subject: [PATCH 10/20] net: mdio: realtek-rtl9300: provide generic command
+ runner
+
+The current bus read/write commands for C22/C45 are RTL930x specific.
+Avoid to duplicate those 200 lines of code for the RTL838x, RTL839x and
+RTL931x targets. Instead provide a generic command runner that is SoC
+independent. The implementation works as follows:
+
+The runner will take a prepared list of the four MDIO registers. It will
+feed the data into the registers. This generic write to all registers
+(or to say "a little bit too much") is no issue. The hardware looks at
+the to be executed command and will only take the pieces of data that
+are really required. No side effects have been observed on any of the
+four SoCs during the time this mechanism exists in downstream OpenWrt.
+
+The last fed register is the C22/command register. This will be enriched
+with the proper command flags from the caller. The hardware issues the
+command and the runner will wait for its finalization.
+
+Besides from feeding all registers the runner emulates the behaviour of
+the old code as best as possible
+
+- check defensively for a running command in advance
+- Before this commit the driver had different MMIO timeout values.
+  1000s for command preparation, 100us after writes and 1000us after
+  reads. The new version uses a consistent 1000us timeout for all
+  of these.
+- return -ENXIO in case of hardware failure (fail bit)
+
+As a first consumer of this runner convert the write_c45() function.
+This is realized in a multi stage approach
+
+- a generic otto_emdio_write_c45() will be called by the bus
+- this will forward the request to the device specific writer. In this
+  case otto_emdio_9300_write_c45().
+- There the command data is filled in and the additional helper
+  otto_emdio_write_cmd() will be called
+- That adds the write flag and issues the generic command runner.
+
+With all the above mentioned in place, there is not much left to do in
+otto_emdio_9300_write_c45(). It just fills the register fields and
+calls the write helper with the right command bits.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260527163449.1294961-2-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 135 +++++++++++++++---------
+ 1 file changed, 83 insertions(+), 52 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -90,6 +90,8 @@ struct otto_emdio_cmd_regs {
+ };
+ struct otto_emdio_info {
++      u32 cmd_fail;
++      u32 cmd_write;
+       struct otto_emdio_cmd_regs cmd_regs;
+       u8 num_buses;
+       u8 num_ports;
+@@ -97,7 +99,7 @@ struct otto_emdio_info {
+       int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
+       int (*read_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum);
+       int (*write_c22)(struct mii_bus *bus, int phy_id, int regnum, u16 value);
+-      int (*write_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum, u16 value);
++      int (*write_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u16 value);
+ };
+ struct otto_emdio_priv {
+@@ -132,6 +134,64 @@ static int otto_emdio_phy_to_port(struct
+       return -ENOENT;
+ }
++static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus)
++{
++      struct otto_emdio_chan *chan = bus->priv;
++
++      return chan->priv;
++}
++
++static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,
++                            struct otto_emdio_cmd_regs *cmd_data)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      const struct otto_emdio_info *info = priv->info;
++      u32 cmdstate;
++      int ret;
++
++      /* Defensive pre check just in case something goes horrible wrong */
++      ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data,
++                                     cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000);
++      if (ret)
++              return ret;
++
++      /* Fill all registers. Hardware will read only the needed bits depending on command */
++      ret = regmap_write(priv->regmap, info->cmd_regs.port_mask_low, cmd_data->port_mask_low);
++      if (ret)
++              return ret;
++
++      ret = regmap_write(priv->regmap, info->cmd_regs.io_data, cmd_data->io_data);
++      if (ret)
++              return ret;
++
++      ret = regmap_write(priv->regmap, info->cmd_regs.c45_data, cmd_data->c45_data);
++      if (ret)
++              return ret;
++
++      /* C22 data and command bits share the same register. */
++      ret = regmap_write(priv->regmap, info->cmd_regs.c22_data,
++                         cmd_data->c22_data | cmd | PHY_CTRL_CMD);
++      if (ret)
++              return ret;
++
++      ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data,
++                                     cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000);
++      if (ret)
++              return ret;
++
++      return cmdstate & info->cmd_fail ? -ENXIO : 0;
++}
++
++static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd,
++                              struct otto_emdio_cmd_regs *cmd_data)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++
++      lockdep_assert_held(&priv->lock);
++
++      return otto_emdio_run_cmd(bus, cmd | priv->info->cmd_write, cmd_data);
++}
++
+ static int otto_emdio_wait_ready(struct otto_emdio_priv *priv)
+ {
+       struct regmap *regmap = priv->regmap;
+@@ -304,64 +364,33 @@ out_err:
+       return err;
+ }
+-static int otto_emdio_9300_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
+-                                int regnum, u16 value)
++static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port,
++                                   int dev_addr, int regnum, u16 value)
+ {
+-      struct otto_emdio_chan *chan = bus->priv;
+-      struct otto_emdio_priv *priv;
+-      u32 io_reg, cmd_reg, val;
+-      struct regmap *regmap;
+-      int port;
+-      int err;
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
++                                FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
++              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, value),
++              .port_mask_low  = BIT(port),
++      };
+-      priv = chan->priv;
+-      regmap = priv->regmap;
+-      io_reg = priv->info->cmd_regs.io_data;
+-      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
++      return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data);
++}
++
++static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id,
++                              int dev_addr, int regnum, u16 value)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      int ret, port;
+       port = otto_emdio_phy_to_port(bus, phy_id);
+       if (port < 0)
+               return port;
+-      mutex_lock(&priv->lock);
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_INDATA, value);
+-      err = regmap_write(regmap, io_reg, val);
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+-            FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
+-      err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, cmd_reg, PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
+-      if (err)
+-              goto out_err;
+-
+-      if (val & PHY_CTRL_FAIL) {
+-              err = -ENXIO;
+-              goto out_err;
+-      }
+-
+-      mutex_unlock(&priv->lock);
+-      return 0;
++      scoped_guard(mutex, &priv->lock)
++              ret = priv->info->write_c45(bus, port, dev_addr, regnum, value);
+-out_err:
+-      mutex_unlock(&priv->lock);
+-      return err;
++      return ret;
+ }
+ static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv)
+@@ -438,7 +467,7 @@ static int otto_emdio_probe_one(struct d
+       bus->name = "Realtek Switch MDIO Bus";
+       if (priv->smi_bus_is_c45[mdio_bus]) {
+               bus->read_c45 = priv->info->read_c45;
+-              bus->write_c45 = priv->info->write_c45;
++              bus->write_c45 = otto_emdio_write_c45;
+       } else {
+               bus->read = priv->info->read_c22;
+               bus->write = priv->info->write_c22;
+@@ -558,6 +587,8 @@ static int otto_emdio_probe(struct platf
+ }
+ static const struct otto_emdio_info otto_emdio_9300_info = {
++      .cmd_fail = PHY_CTRL_FAIL,
++      .cmd_write = PHY_CTRL_WRITE,
+       .cmd_regs = {
+               .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
+               .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3,
diff --git a/target/linux/realtek/patches-6.18/031-11-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-writ.patch b/target/linux/realtek/patches-6.18/031-11-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-writ.patch
new file mode 100644 (file)
index 0000000..d3bfb3e
--- /dev/null
@@ -0,0 +1,133 @@
+From 9cd59932f2d2a09ef239228671020dfd1ae7f4af Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 27 May 2026 18:34:47 +0200
+Subject: [PATCH 11/20] net: mdio: realtek-rtl9300: use command runner for
+ write_c22()
+
+Now that the driver has a generic command runner make use of it in the
+write_c22() path. For this.
+
+- add generic otto_emdio_write_c22() helper that will be called by bus
+- convert otto_emdio_9300_write_c22() to new command runner logic
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260527163449.1294961-3-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 81 +++++++++----------------
+ 1 file changed, 27 insertions(+), 54 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -98,7 +98,7 @@ struct otto_emdio_info {
+       u16 num_pages;
+       int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
+       int (*read_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum);
+-      int (*write_c22)(struct mii_bus *bus, int phy_id, int regnum, u16 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);
+ };
+@@ -254,60 +254,18 @@ out_err:
+       return err;
+ }
+-static int otto_emdio_9300_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
++static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+ {
+-      struct otto_emdio_chan *chan = bus->priv;
+-      struct otto_emdio_priv *priv;
+-      u32 io_reg, cmd_reg, val;
+-      struct regmap *regmap;
+-      int port;
+-      int err;
+-
+-      priv = chan->priv;
+-      regmap = priv->regmap;
+-      io_reg = priv->info->cmd_regs.io_data;
+-      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+-
+-      port = otto_emdio_phy_to_port(bus, phy_id);
+-      if (port < 0)
+-              return port;
+-
+-      mutex_lock(&priv->lock);
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, priv->info->cmd_regs.port_mask_low, BIT(port));
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, value));
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+-            FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-            FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+-            PHY_CTRL_WRITE | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+-      err = regmap_write(regmap, cmd_reg, val);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 100);
+-      if (err)
+-              goto out_err;
+-
+-      if (val & PHY_CTRL_FAIL) {
+-              err = -ENXIO;
+-              goto out_err;
+-      }
+-
+-      mutex_unlock(&priv->lock);
+-      return 0;
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c22_data       = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
++                                FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, value),
++              .port_mask_low  = BIT(port),
++      };
+-out_err:
+-      mutex_unlock(&priv->lock);
+-      return err;
++      return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C22, &cmd_data);
+ }
+ static int otto_emdio_9300_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
+@@ -377,6 +335,21 @@ static int otto_emdio_9300_write_c45(str
+       return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data);
+ }
++static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      int ret, port;
++
++      port = otto_emdio_phy_to_port(bus, phy_id);
++      if (port < 0)
++              return port;
++
++      scoped_guard(mutex, &priv->lock)
++              ret = priv->info->write_c22(bus, port, regnum, value);
++
++      return ret;
++}
++
+ static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id,
+                               int dev_addr, int regnum, u16 value)
+ {
+@@ -470,7 +443,7 @@ static int otto_emdio_probe_one(struct d
+               bus->write_c45 = otto_emdio_write_c45;
+       } else {
+               bus->read = priv->info->read_c22;
+-              bus->write = priv->info->write_c22;
++              bus->write = otto_emdio_write_c22;
+       }
+       bus->parent = dev;
+       chan = bus->priv;
diff --git a/target/linux/realtek/patches-6.18/031-12-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch b/target/linux/realtek/patches-6.18/031-12-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch
new file mode 100644 (file)
index 0000000..70cbef8
--- /dev/null
@@ -0,0 +1,182 @@
+From fcce51bfd4edc5cd4059f835315d227657bcf62e Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 27 May 2026 18:34:48 +0200
+Subject: [PATCH 12/20] net: mdio: realtek-rtl9300: use command runner for
+ read_c45()
+
+Convert the read_c45() path to the new command runner. This needs the
+additional helper otto_emdio_read_cmd() that can issue the command runner
+and process a read operation. It is basically nothing more than
+
+- run the command
+- read the command result thorugh the I/O register
+
+With this in place convert the read_c45() like the alread existing write
+C22/C45 implementation.
+
+- bus calls otto_emdio_read_c45()
+- this handed over to SoC specific otto_emdio_9300_read_c45()
+- the registers are filled
+- the otto_emdio_read_cmd() is issued
+- that calls the command runner
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260527163449.1294961-4-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 100 ++++++++++++------------
+ 1 file changed, 48 insertions(+), 52 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -91,13 +91,14 @@ struct otto_emdio_cmd_regs {
+ struct otto_emdio_info {
+       u32 cmd_fail;
++      u32 cmd_read;
+       u32 cmd_write;
+       struct otto_emdio_cmd_regs cmd_regs;
+       u8 num_buses;
+       u8 num_ports;
+       u16 num_pages;
+       int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
+-      int (*read_c45)(struct mii_bus *bus, int phy_id, int dev_addr, int regnum);
++      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);
+ };
+@@ -182,6 +183,26 @@ static int otto_emdio_run_cmd(struct mii
+       return cmdstate & info->cmd_fail ? -ENXIO : 0;
+ }
++static int otto_emdio_read_cmd(struct mii_bus *bus, u32 cmd,
++                             struct otto_emdio_cmd_regs *cmd_data, u32 *value)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      int ret;
++
++      lockdep_assert_held(&priv->lock);
++      ret = otto_emdio_run_cmd(bus, cmd | priv->info->cmd_read, cmd_data);
++      if (ret)
++              return ret;
++
++      ret = regmap_read(priv->regmap, priv->info->cmd_regs.io_data, value);
++      if (ret)
++              return ret;
++
++      *value = FIELD_GET(PHY_CTRL_DATA, *value);
++
++      return 0;
++}
++
+ static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd,
+                               struct otto_emdio_cmd_regs *cmd_data)
+ {
+@@ -268,58 +289,16 @@ static int otto_emdio_9300_write_c22(str
+       return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C22, &cmd_data);
+ }
+-static int otto_emdio_9300_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
++static int otto_emdio_9300_read_c45(struct mii_bus *bus, int port,
++                                  int dev_addr, int regnum, u32 *value)
+ {
+-      struct otto_emdio_chan *chan = bus->priv;
+-      struct otto_emdio_priv *priv;
+-      u32 io_reg, cmd_reg, val;
+-      struct regmap *regmap;
+-      int port;
+-      int err;
+-
+-      priv = chan->priv;
+-      regmap = priv->regmap;
+-      io_reg = priv->info->cmd_regs.io_data;
+-      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+-
+-      port = otto_emdio_phy_to_port(bus, phy_id);
+-      if (port < 0)
+-              return port;
+-
+-      mutex_lock(&priv->lock);
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_INDATA, port);
+-      err = regmap_write(regmap, io_reg, val);
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+-            FIELD_PREP(PHY_CTRL_MMD_REG, regnum);
+-      err = regmap_write(regmap, priv->info->cmd_regs.c45_data, val);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, cmd_reg, PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD);
+-      if (err)
+-              goto out_err;
+-
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_read(regmap, io_reg, &val);
+-      if (err)
+-              goto out_err;
+-
+-      mutex_unlock(&priv->lock);
+-      return FIELD_GET(PHY_CTRL_DATA, val);
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c45_data       = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
++                                FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
++              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, port),
++      };
+-out_err:
+-      mutex_unlock(&priv->lock);
+-      return err;
++      return otto_emdio_read_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data, value);
+ }
+ static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port,
+@@ -350,6 +329,22 @@ static int otto_emdio_write_c22(struct m
+       return ret;
+ }
++static int otto_emdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      int ret, port;
++      u32 value;
++
++      port = otto_emdio_phy_to_port(bus, phy_id);
++      if (port < 0)
++              return port;
++
++      scoped_guard(mutex, &priv->lock)
++              ret = priv->info->read_c45(bus, port, dev_addr, regnum, &value);
++
++      return ret ? ret : value;
++}
++
+ static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id,
+                               int dev_addr, int regnum, u16 value)
+ {
+@@ -439,7 +434,7 @@ static int otto_emdio_probe_one(struct d
+       bus->name = "Realtek Switch MDIO Bus";
+       if (priv->smi_bus_is_c45[mdio_bus]) {
+-              bus->read_c45 = priv->info->read_c45;
++              bus->read_c45 = otto_emdio_read_c45;
+               bus->write_c45 = otto_emdio_write_c45;
+       } else {
+               bus->read = priv->info->read_c22;
+@@ -561,6 +556,7 @@ static int otto_emdio_probe(struct platf
+ static const struct otto_emdio_info otto_emdio_9300_info = {
+       .cmd_fail = PHY_CTRL_FAIL,
++      .cmd_read = PHY_CTRL_READ,
+       .cmd_write = PHY_CTRL_WRITE,
+       .cmd_regs = {
+               .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1,
diff --git a/target/linux/realtek/patches-6.18/031-13-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch b/target/linux/realtek/patches-6.18/031-13-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch
new file mode 100644 (file)
index 0000000..6a6d952
--- /dev/null
@@ -0,0 +1,142 @@
+From 2e3c28c715e913decf228dcd656bbb0dfc18133b Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 27 May 2026 18:34:49 +0200
+Subject: [PATCH 13/20] net: mdio: realtek-rtl9300: use command runner for
+ read_c22()
+
+Convert the final missing read_c22() path to the new read enabled command
+runner. Do it the same way as other implementations.
+
+- bus calls otto_emdio_read_c22()
+- this hands over to SoC specific otto_emdio_9300_read_c22()
+- finally the registers are filled and the runner issued
+
+With this cleanup remove the obsolete helper otto_emdio_wait_ready()
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260527163449.1294961-5-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 87 ++++++++-----------------
+ 1 file changed, 27 insertions(+), 60 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -97,7 +97,7 @@ struct otto_emdio_info {
+       u8 num_buses;
+       u8 num_ports;
+       u16 num_pages;
+-      int (*read_c22)(struct mii_bus *bus, int phy_id, int regnum);
++      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);
+@@ -213,66 +213,17 @@ static int otto_emdio_write_cmd(struct m
+       return otto_emdio_run_cmd(bus, cmd | priv->info->cmd_write, cmd_data);
+ }
+-static int otto_emdio_wait_ready(struct otto_emdio_priv *priv)
++static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)
+ {
+-      struct regmap *regmap = priv->regmap;
+-      u32 cmd_reg, val;
+-
+-      lockdep_assert_held(&priv->lock);
+-      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+-
+-      return regmap_read_poll_timeout(regmap, cmd_reg, val, !(val & PHY_CTRL_CMD), 10, 1000);
+-}
+-
+-static int otto_emdio_9300_read_c22(struct mii_bus *bus, int phy_id, int regnum)
+-{
+-      struct otto_emdio_chan *chan = bus->priv;
+-      struct otto_emdio_priv *priv;
+-      u32 io_reg, cmd_reg, val;
+-      struct regmap *regmap;
+-      int port;
+-      int err;
+-
+-      priv = chan->priv;
+-      regmap = priv->regmap;
+-      io_reg = priv->info->cmd_regs.io_data;
+-      cmd_reg = priv->info->cmd_regs.c22_data; /* shared command/C22 register */
+-
+-      port = otto_emdio_phy_to_port(bus, phy_id);
+-      if (port < 0)
+-              return port;
+-
+-      mutex_lock(&priv->lock);
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_write(regmap, io_reg, FIELD_PREP(PHY_CTRL_INDATA, port));
+-      if (err)
+-              goto out_err;
+-
+-      val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
+-            FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
+-            FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)) |
+-            PHY_CTRL_READ | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD;
+-      err = regmap_write(regmap, cmd_reg, val);
+-      if (err)
+-              goto out_err;
+-
+-      err = otto_emdio_wait_ready(priv);
+-      if (err)
+-              goto out_err;
+-
+-      err = regmap_read(regmap, io_reg, &val);
+-      if (err)
+-              goto out_err;
+-
+-      mutex_unlock(&priv->lock);
+-      return FIELD_GET(PHY_CTRL_DATA, val);
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      struct otto_emdio_cmd_regs cmd_data = {
++              .c22_data       = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) |
++                                FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) |
++                                FIELD_PREP(PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
++              .io_data        = FIELD_PREP(PHY_CTRL_INDATA, port),
++      };
+-out_err:
+-      mutex_unlock(&priv->lock);
+-      return err;
++      return otto_emdio_read_cmd(bus, PHY_CTRL_TYPE_C22, &cmd_data, value);
+ }
+ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+@@ -314,6 +265,22 @@ static int otto_emdio_9300_write_c45(str
+       return otto_emdio_write_cmd(bus, PHY_CTRL_TYPE_C45, &cmd_data);
+ }
++static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
++{
++      struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
++      int ret, port;
++      u32 value;
++
++      port = otto_emdio_phy_to_port(bus, phy_id);
++      if (port < 0)
++              return port;
++
++      scoped_guard(mutex, &priv->lock)
++              ret = priv->info->read_c22(bus, port, regnum, &value);
++
++      return ret ? ret : value;
++}
++
+ static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
+ {
+       struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+@@ -437,7 +404,7 @@ static int otto_emdio_probe_one(struct d
+               bus->read_c45 = otto_emdio_read_c45;
+               bus->write_c45 = otto_emdio_write_c45;
+       } else {
+-              bus->read = priv->info->read_c22;
++              bus->read = otto_emdio_read_c22;
+               bus->write = otto_emdio_write_c22;
+       }
+       bus->parent = dev;
diff --git a/target/linux/realtek/patches-6.18/031-14-v7.2-net-mdio-realtek-rtl9300-Refactor-otto_emdio_map_por.patch b/target/linux/realtek/patches-6.18/031-14-v7.2-net-mdio-realtek-rtl9300-Refactor-otto_emdio_map_por.patch
new file mode 100644 (file)
index 0000000..d44b2ce
--- /dev/null
@@ -0,0 +1,136 @@
+From f369dd8ccf2dbce9166544e266e2b6ca974f98d9 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:18 +0200
+Subject: [PATCH 14/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-2-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 69 +++++++++++++++----------
+ 1 file changed, 42 insertions(+), 27 deletions(-)
+
+--- 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 d
+  */
+ 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)) {
++                      err = dev_err_probe(dev, -EINVAL, "duplicated 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);
+-
+-              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)
diff --git a/target/linux/realtek/patches-6.18/031-15-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_map_ports.patch b/target/linux/realtek/patches-6.18/031-15-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_map_ports.patch
new file mode 100644 (file)
index 0000000..e384124
--- /dev/null
@@ -0,0 +1,56 @@
+From 829ee0f8bc3920092b06d32d9e05328395ed5b77 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:19 +0200
+Subject: [PATCH 15/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-3-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- 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 d
+       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 d
+                       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;
diff --git a/target/linux/realtek/patches-6.18/031-16-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_probe_one.patch b/target/linux/realtek/patches-6.18/031-16-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_probe_one.patch
new file mode 100644 (file)
index 0000000..4bf313d
--- /dev/null
@@ -0,0 +1,43 @@
+From 67885c0e3919be0f39d8f69e4eb446c9df8fa5fc Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:20 +0200
+Subject: [PATCH 16/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-4-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- 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 d
+       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.
diff --git a/target/linux/realtek/patches-6.18/031-17-v7.2-net-mdio-realtek-rtl9300-relocate-topology-setup.patch b/target/linux/realtek/patches-6.18/031-17-v7.2-net-mdio-realtek-rtl9300-relocate-topology-setup.patch
new file mode 100644 (file)
index 0000000..e3c67cf
--- /dev/null
@@ -0,0 +1,219 @@
+From 94f05740ad16c5b2bf738af69b99cd0c48afd19e Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:21 +0200
+Subject: [PATCH 17/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-5-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 127 ++++++++++++++----------
+ 1 file changed, 76 insertions(+), 51 deletions(-)
+
+--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
++++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
+@@ -51,34 +51,38 @@
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+-#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 MAX_PORTS       28
+-#define MAX_SMI_BUSSES  4
+-#define RAW_PAGE(priv)        ((priv)->info->num_pages - 1)
++#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 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 m
+       return ret;
+ }
+-static int otto_emdio_9300_mdiobus_init(struct otto_emdio_priv *priv)
++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 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;
++      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);
+-      /* Associate the port with the SMI interface and PHY */
+-      for_each_set_bit(i, priv->valid_ports, priv->info->num_ports) {
+-              int pos;
++      return regmap_update_bits(priv->regmap, reg, mask << shift, value << shift);
++}
+-              pos = (i % 6) * 5;
+-              port_addr[i / 6] |= (priv->smi_addr[i] & 0x1f) << pos;
++static int otto_emdio_setup_topology(struct otto_emdio_priv *priv)
++{
++      const struct otto_emdio_info *info = priv->info;
++      u32 port;
++      int ret;
+-              pos = (i % 16) * 2;
+-              poll_sel[i / 16] |= (priv->smi_bus[i] & 0x3) << pos;
++      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;
++      int i, err;
++
+       /* 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 platf
+       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 platf
+ }
+ 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,
diff --git a/target/linux/realtek/patches-6.18/031-18-v7.2-net-mdio-realtek-rtl9300-relocate-c22-c45-device-tre.patch b/target/linux/realtek/patches-6.18/031-18-v7.2-net-mdio-realtek-rtl9300-relocate-c22-c45-device-tre.patch
new file mode 100644 (file)
index 0000000..2338944
--- /dev/null
@@ -0,0 +1,64 @@
+From 439aba443a8012b5a61bf327f75bec69be56ddd3 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:22 +0200
+Subject: [PATCH 18/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-6-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- 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 d
+               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 d
+                       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;
diff --git a/target/linux/realtek/patches-6.18/031-19-v7.2-net-mdio-realtek-rtl9300-reorder-controller-setup.patch b/target/linux/realtek/patches-6.18/031-19-v7.2-net-mdio-realtek-rtl9300-reorder-controller-setup.patch
new file mode 100644 (file)
index 0000000..129f61f
--- /dev/null
@@ -0,0 +1,136 @@
+From 56b4864150970ba834367fe74b37ce250419347e Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Wed, 3 Jun 2026 19:59:23 +0200
+Subject: [PATCH 19/20] 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 <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-7-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 44 ++++++++++++++-----------
+ 1 file changed, 24 insertions(+), 20 deletions(-)
+
+--- 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(str
+       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(
+       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 platf
+       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
+       .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,
diff --git a/target/linux/realtek/patches-6.18/031-20-v7.2-net-mdio-realtek-rtl9300-Correctly-handle-ethernet-p.patch b/target/linux/realtek/patches-6.18/031-20-v7.2-net-mdio-realtek-rtl9300-Correctly-handle-ethernet-p.patch
new file mode 100644 (file)
index 0000000..3c1534b
--- /dev/null
@@ -0,0 +1,57 @@
+From 50b682a5aed70847e0746db7f723d22bef1801d1 Mon Sep 17 00:00:00 2001
+From: Manuel Stocker <mensi@mensi.ch>
+Date: Wed, 3 Jun 2026 19:59:24 +0200
+Subject: [PATCH 20/20] 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 <mensi@mensi.ch>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://patch.msgid.link/20260603175924.123019-8-markus.stockhausen@gmx.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/mdio/mdio-realtek-rtl9300.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- 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 d
+       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 d
+               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;