From: Markus Stockhausen Date: Sat, 6 Jun 2026 06:33:27 +0000 (+0200) Subject: realtek: mdio: backport upstream patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=32bcc2986c76595a623a0f56f46b3d4995fa024c;p=thirdparty%2Fopenwrt.git realtek: mdio: backport upstream patches 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 --- 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 index 00000000000..58eff1e6ecf --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-01-v7.2-net-mdio-realtek-rtl9300-enhance-documentation-namin.patch @@ -0,0 +1,381 @@ +From d15c0d1df49480b8f7d7c6182880e353574c871d Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-2-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 +@@ -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 index 00000000000..50a681a8a2f --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-02-v7.2-net-mdio-realtek-rtl9300-Add-device-specific-info-st.patch @@ -0,0 +1,84 @@ +From 315a02b39e8c4d1e8aebc8c3cc1e14e193f116ae Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-3-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 + #include + ++#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 index 00000000000..c38fb5d3ca6 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-03-v7.2-net-mdio-realtek-rtl9300-Add-ports-to-info-structure.patch @@ -0,0 +1,79 @@ +From 215701873a7ed1214bba82c8fadcd2583d0246c3 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-4-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 + + #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 index 00000000000..2c39877e8fc --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-04-v7.2-net-mdio-realtek-rtl9300-Add-pages-to-info-structure.patch @@ -0,0 +1,90 @@ +From 38c9d5b644049e9e78fafa385d420b2ae8485b81 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-5-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 + + #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 index 00000000000..9199bd411e2 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-05-v7.2-net-mdio-realtek-rtl9300-Add-register-structure.patch @@ -0,0 +1,109 @@ +From 62baf5f1d80fe4f83099d52ac12e1337dd9fa9fc Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-6-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..1bc7d4ee014 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-06-v7.2-net-mdio-realtek-rtl9300-Add-command-C22-register.patch @@ -0,0 +1,178 @@ +From 8bf7e2b133db0ec690268a0afb0ee9471f4fbb52 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-7-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..6408a8f1731 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-07-v7.2-net-mdio-realtek-rtl9300-Add-I-O-register.patch @@ -0,0 +1,161 @@ +From ffc92cd009145eb5f85d913fbbc67fd24b601080 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-8-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..61378427091 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-08-v7.2-net-mdio-realtek-rtl9300-Add-port-mask-register.patch @@ -0,0 +1,71 @@ +From cad6a373f7f69254cb57e76e06f7734777575555 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-9-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..a5d397aa08b --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-09-v7.2-net-mdio-realtek-rtl9300-Link-I-O-functions-in-info-.patch @@ -0,0 +1,66 @@ +From 826a1926f084037b91415a53a29ce93264f08ada Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260521175918.1494797-10-markus.stockhausen@gmx.de +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..29c82b608ba --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-10-v7.2-net-mdio-realtek-rtl9300-provide-generic-command-run.patch @@ -0,0 +1,241 @@ +From 9ccb438210c4838962dfa1a10d4c198391dcc284 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260527163449.1294961-2-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + 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 index 00000000000..d3bfb3ef7bb --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-11-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-writ.patch @@ -0,0 +1,133 @@ +From 9cd59932f2d2a09ef239228671020dfd1ae7f4af Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260527163449.1294961-3-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + 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 index 00000000000..70cbef81a79 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-12-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch @@ -0,0 +1,182 @@ +From fcce51bfd4edc5cd4059f835315d227657bcf62e Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260527163449.1294961-4-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + 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 index 00000000000..6a6d95245ab --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-13-v7.2-net-mdio-realtek-rtl9300-use-command-runner-for-read.patch @@ -0,0 +1,142 @@ +From 2e3c28c715e913decf228dcd656bbb0dfc18133b Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260527163449.1294961-5-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + 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 index 00000000000..d44b2ce3c21 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-14-v7.2-net-mdio-realtek-rtl9300-Refactor-otto_emdio_map_por.patch @@ -0,0 +1,136 @@ +From f369dd8ccf2dbce9166544e266e2b6ca974f98d9 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-2-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 69 +++++++++++++++---------- + 1 file changed, 42 insertions(+), 27 deletions(-) + +--- 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 index 00000000000..e3841244257 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-15-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_map_ports.patch @@ -0,0 +1,56 @@ +From 829ee0f8bc3920092b06d32d9e05328395ed5b77 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-3-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +--- 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 index 00000000000..4bf313d46d1 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-16-v7.2-net-mdio-realtek-rtl9300-harden-otto_emdio_probe_one.patch @@ -0,0 +1,43 @@ +From 67885c0e3919be0f39d8f69e4eb446c9df8fa5fc Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-4-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- 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 index 00000000000..e3c67cf17d7 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-17-v7.2-net-mdio-realtek-rtl9300-relocate-topology-setup.patch @@ -0,0 +1,219 @@ +From 94f05740ad16c5b2bf738af69b99cd0c48afd19e Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-5-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + 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 + #include + +-#define RTL9300_NUM_BUSES 4 +-#define RTL9300_NUM_PAGES 4096 +-#define RTL9300_NUM_PORTS 28 +-#define SMI_GLB_CTRL 0xca00 +-#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) +-#define SMI_PORT0_15_POLLING_SEL 0xca08 +-#define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 +-#define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 +-#define PHY_CTRL_REG_ADDR GENMASK(24, 20) +-#define PHY_CTRL_PARK_PAGE GENMASK(19, 15) +-#define PHY_CTRL_MAIN_PAGE GENMASK(14, 3) +-#define PHY_CTRL_WRITE BIT(2) +-#define PHY_CTRL_READ 0 +-#define PHY_CTRL_TYPE_C45 BIT(1) +-#define PHY_CTRL_TYPE_C22 0 +-#define PHY_CTRL_CMD BIT(0) +-#define PHY_CTRL_FAIL BIT(25) +-#define RTL9300_SMI_ACCESS_PHY_CTRL_2 0xcb78 +-#define PHY_CTRL_INDATA GENMASK(31, 16) +-#define PHY_CTRL_DATA GENMASK(15, 0) +-#define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c +-#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) +-#define PHY_CTRL_MMD_REG GENMASK(15, 0) +-#define SMI_PORT0_5_ADDR_CTRL 0xcb80 +- +-#define 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 index 00000000000..233894416ed --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-18-v7.2-net-mdio-realtek-rtl9300-relocate-c22-c45-device-tre.patch @@ -0,0 +1,64 @@ +From 439aba443a8012b5a61bf327f75bec69be56ddd3 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-6-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 21 +++++++++------------ + 1 file changed, 9 insertions(+), 12 deletions(-) + +--- 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 index 00000000000..129f61f4a98 --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-19-v7.2-net-mdio-realtek-rtl9300-reorder-controller-setup.patch @@ -0,0 +1,136 @@ +From 56b4864150970ba834367fe74b37ce250419347e Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +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 +Link: https://patch.msgid.link/20260603175924.123019-7-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 44 ++++++++++++++----------- + 1 file changed, 24 insertions(+), 20 deletions(-) + +--- 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 index 00000000000..3c1534b0d9d --- /dev/null +++ b/target/linux/realtek/patches-6.18/031-20-v7.2-net-mdio-realtek-rtl9300-Correctly-handle-ethernet-p.patch @@ -0,0 +1,57 @@ +From 50b682a5aed70847e0746db7f723d22bef1801d1 Mon Sep 17 00:00:00 2001 +From: Manuel Stocker +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 +Signed-off-by: Markus Stockhausen +Link: https://patch.msgid.link/20260603175924.123019-8-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/mdio/mdio-realtek-rtl9300.c | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +--- 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; +