]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: add SerDes PCS driver 20075/head
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Wed, 17 Sep 2025 18:22:25 +0000 (14:22 -0400)
committerRobert Marko <robimarko@gmail.com>
Sat, 20 Sep 2025 10:51:23 +0000 (12:51 +0200)
Until now the the SerDes configuration is realized with helper functions
scattered around the DSA and PHY driver. Give them a new home as a PCS
driver.

The target design is as follows:

- dsa driver manages switch
- pcs driver manages SerDes on high level (this commit)
- mdio driver manages SerDes on low level

This driver adds the high level SerDes access via PCS. It makes use of
the low level mdio SerDes driver to access the registers.

Remark: This initial version provides exactly all phylink_pcs_ops that
are currently part of the DSA driver. So this can be swapped in one of
the next commits as a drop in replacement. To make use of it something
like this is needed:

...
ports = of_get_child_by_name(node, "ethernet-ports");
if (!ports)
return -EINVAL;

for_each_available_child_of_node(ports, port) {
pcs_node = of_parse_phandle(port, "pcs-handle", 0);
of_property_read_u32(port, "reg", &port_nr)) {

priv->pcs[port_nr] = rtpcs_create(dev, pcs_node, port_nr);
}
...

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/20075
Signed-off-by: Robert Marko <robimarko@gmail.com>
12 files changed:
target/linux/realtek/dts/rtl838x.dtsi
target/linux/realtek/dts/rtl839x.dtsi
target/linux/realtek/dts/rtl930x.dtsi
target/linux/realtek/dts/rtl931x.dtsi
target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c [new file with mode: 0644]
target/linux/realtek/patches-6.12/730-add-pcs-rtl-otto.patch [new file with mode: 0644]
target/linux/realtek/rtl838x/config-6.12
target/linux/realtek/rtl839x/config-6.12
target/linux/realtek/rtl930x/config-6.12
target/linux/realtek/rtl930x_nand/config-6.12
target/linux/realtek/rtl931x/config-6.12
target/linux/realtek/rtl931x_nand/config-6.12

index ab81c49f8ab574d99c09f2039b9d308beaf5faac..6a872bcb0f82d8ff383b440ea24d17e7e5f2f40e 100644 (file)
                        compatible = "realtek,rtl8380-serdes-mdio", "realtek,otto-serdes-mdio";
                };
 
+               pcs {
+                       compatible = "realtek,rtl8380-pcs", "realtek,otto-pcs";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       serdes0: serdes@0 {
+                               reg = <0>;
+                       };
+                       serdes1: serdes@1 {
+                               reg = <1>;
+                       };
+                       serdes2: serdes@2 {
+                               reg = <2>;
+                       };
+                       serdes3: serdes@3 {
+                               reg = <3>;
+                       };
+                       serdes4: serdes@4 {
+                               reg = <4>;
+                       };
+                       serdes5: serdes@5 {
+                               reg = <5>;
+                       };
+               };
+
                soc_thermal: thermal {
                        compatible = "realtek,rtl8380-thermal";
                        #thermal-sensor-cells = <0>;
index 6b4f4821a2be0f5ee3bb52bd0d8df35357657d46..5e92c5071dc22eebbc8846916764abe386bd82fb 100644 (file)
                        compatible = "realtek,rtl8392-serdes-mdio", "realtek,otto-serdes-mdio";
                };
 
+               pcs {
+                       compatible = "realtek,rtl8392-pcs", "realtek,otto-pcs";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       serdes0: serdes@0 {
+                               reg = <0>;
+                       };
+                       serdes1: serdes@1 {
+                               reg = <1>;
+                       };
+                       serdes2: serdes@2 {
+                               reg = <2>;
+                       };
+                       serdes3: serdes@3 {
+                               reg = <3>;
+                       };
+                       serdes4: serdes@4 {
+                               reg = <4>;
+                       };
+                       serdes5: serdes@5 {
+                               reg = <5>;
+                       };
+                       serdes6: serdes@6 {
+                               reg = <6>;
+                       };
+                       serdes7: serdes@7 {
+                               reg = <7>;
+                       };
+                       serdes8: serdes@8 {
+                               reg = <8>;
+                       };
+                       serdes9: serdes@9 {
+                               reg = <9>;
+                       };
+                       serdes10: serdes@10 {
+                               reg = <10>;
+                       };
+                       serdes11: serdes@11 {
+                               reg = <11>;
+                       };
+                       serdes12: serdes@12 {
+                               reg = <12>;
+                       };
+                       serdes13: serdes@13 {
+                               reg = <13>;
+                       };
+               };
+
                soc_thermal: thermal {
                        compatible = "realtek,rtl8390-thermal";
                        #thermal-sensor-cells = <0>;
index ad06cef6cdfbcfc6574536d4d1dd5cb73f606117..d3c6a2dd6c4bd908b40f8038fa338c55665774ef 100644 (file)
                        compatible = "realtek,rtl9301-serdes-mdio", "realtek,otto-serdes-mdio";
                };
 
+               pcs {
+                       compatible = "realtek,rtl9301-pcs", "realtek,otto-pcs";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       serdes0: serdes@0 {
+                               reg = <0>;
+                       };
+                       serdes1: serdes@1 {
+                               reg = <1>;
+                       };
+                       serdes2: serdes@2 {
+                               reg = <2>;
+                       };
+                       serdes3: serdes@3 {
+                               reg = <3>;
+                       };
+                       serdes4: serdes@4 {
+                               reg = <4>;
+                       };
+                       serdes5: serdes@5 {
+                               reg = <5>;
+                       };
+                       serdes6: serdes@6 {
+                               reg = <6>;
+                       };
+                       serdes7: serdes@7 {
+                               reg = <7>;
+                       };
+                       serdes8: serdes@8 {
+                               reg = <8>;
+                       };
+                       serdes9: serdes@9 {
+                               reg = <9>;
+                       };
+                       serdes10: serdes@10 {
+                               reg = <10>;
+                       };
+                       serdes11: serdes@11 {
+                               reg = <11>;
+                       };
+               };
+
                soc_thermal: thermal {
                        compatible = "realtek,rtl9300-thermal";
                        #thermal-sensor-cells = <0>;
index d7b789b9bb49f72fa25b4732d409ff3fa52e877d..58121bea2eb54489c074ca6133751e330e10b2c2 100644 (file)
                        compatible = "realtek,rtl9311-serdes-mdio", "realtek,otto-serdes-mdio";
                };
 
+               pcs {
+                       compatible = "realtek,rtl9311-pcs", "realtek,otto-pcs";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       serdes0: serdes@0 {
+                               reg = <0>;
+                       };
+                       serdes1: serdes@1 {
+                               reg = <1>;
+                       };
+                       serdes2: serdes@2 {
+                               reg = <2>;
+                       };
+                       serdes3: serdes@3 {
+                               reg = <3>;
+                       };
+                       serdes4: serdes@4 {
+                               reg = <4>;
+                       };
+                       serdes5: serdes@5 {
+                               reg = <5>;
+                       };
+                       serdes6: serdes@6 {
+                               reg = <6>;
+                       };
+                       serdes7: serdes@7 {
+                               reg = <7>;
+                       };
+                       serdes8: serdes@8 {
+                               reg = <8>;
+                       };
+                       serdes9: serdes@9 {
+                               reg = <9>;
+                       };
+                       serdes10: serdes@10 {
+                               reg = <10>;
+                       };
+                       serdes11: serdes@11 {
+                               reg = <11>;
+                       };
+                       serdes12: serdes@12 {
+                               reg = <12>;
+                       };
+                       serdes13: serdes@13 {
+                               reg = <13>;
+                       };
+               };
        };
 
        pinmux@1b001358 {
diff --git a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c
new file mode 100644 (file)
index 0000000..ceedf20
--- /dev/null
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/mdio.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/phylink.h>
+#include <linux/regmap.h>
+
+#define RTPCS_PORT_CNT                         57
+
+#define RTPCS_SPEED_10                         0
+#define RTPCS_SPEED_100                                1
+#define RTPCS_SPEED_1000                       2
+#define RTPCS_SPEED_10000_LEGACY               3
+#define RTPCS_SPEED_10000                      4
+#define RTPCS_SPEED_2500                       5
+#define RTPCS_SPEED_5000                       6
+
+#define RTPCS_838X_CPU_PORT                    28
+#define RTPCS_838X_MAC_LINK_DUP_STS            0xa19c
+#define RTPCS_838X_MAC_LINK_SPD_STS            0xa190
+#define RTPCS_838X_MAC_LINK_STS                        0xa188
+#define RTPCS_838X_MAC_RX_PAUSE_STS            0xa1a4
+#define RTPCS_838X_MAC_TX_PAUSE_STS            0xa1a0
+
+#define RTPCS_839X_CPU_PORT                    52
+#define RTPCS_839X_MAC_LINK_DUP_STS            0x03b0
+#define RTPCS_839X_MAC_LINK_SPD_STS            0x03a0
+#define RTPCS_839X_MAC_LINK_STS                        0x0390
+#define RTPCS_839X_MAC_RX_PAUSE_STS            0x03c0
+#define RTPCS_839X_MAC_TX_PAUSE_STS            0x03b8
+
+#define RTPCS_83XX_MAC_LINK_SPD_BITS           2
+
+#define RTPCS_930X_CPU_PORT                    28
+#define RTPCS_930X_MAC_LINK_DUP_STS            0xcb28
+#define RTPCS_930X_MAC_LINK_SPD_STS            0xcb18
+#define RTPCS_930X_MAC_LINK_STS                        0xcb10
+#define RTPCS_930X_MAC_RX_PAUSE_STS            0xcb30
+#define RTPCS_930X_MAC_TX_PAUSE_STS            0xcb2c
+
+#define RTPCS_931X_CPU_PORT                    56
+#define RTPCS_931X_MAC_LINK_DUP_STS            0x0ef0
+#define RTPCS_931X_MAC_LINK_SPD_STS            0x0ed0
+#define RTPCS_931X_MAC_LINK_STS                        0x0ec0
+#define RTPCS_931X_MAC_RX_PAUSE_STS            0x0f00
+#define RTPCS_931X_MAC_TX_PAUSE_STS            0x0ef8
+
+#define RTPCS_93XX_MAC_LINK_SPD_BITS           4
+
+struct rtpcs_config {
+       int cpu_port;
+       int mac_link_dup_sts;
+       int mac_link_spd_bits;
+       int mac_link_spd_sts;
+       int mac_link_sts;
+       int mac_rx_pause_sts;
+       int mac_tx_pause_sts;
+       const struct phylink_pcs_ops *pcs_ops;
+};
+
+struct rtpcs_ctrl {
+       struct device *dev;
+       struct regmap *map;
+       struct mii_bus *bus;
+       const struct rtpcs_config *cfg;
+       struct rtpcs_link *link[RTPCS_PORT_CNT];
+};
+
+struct rtpcs_link {
+       struct rtpcs_ctrl *ctrl;
+       struct phylink_pcs pcs;
+       int sds;
+       int port;
+};
+
+static int rtpcs_sds_to_mmd(int sds_page, int sds_regnum)
+{
+       return (sds_page << 8) + sds_regnum;
+}
+
+static int rtpcs_sds_read(struct rtpcs_ctrl *ctrl, int sds, int page, int regnum)
+{
+       int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
+
+       return mdiobus_c45_read(ctrl->bus, sds, MDIO_MMD_VEND1, mmd_regnum);
+}
+
+/*
+ * For later use, when the SerDes registers need to be written ...
+ *
+ * static int rtpcs_sds_write(struct rtpcs_ctrl *ctrl, int sds, int page, int regnum, u16 value)
+ * {
+ *     int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
+ *
+ *     return mdiobus_c45_write(ctrl->bus, sds, MDIO_MMD_VEND1, mmd_regnum, value);
+ * }
+ */
+
+static int rtpcs_regmap_read_bits(struct rtpcs_ctrl *ctrl, int base, int bithigh, int bitlow)
+{
+       int offset = base + (bitlow / 32) * 4;
+       int bits = bithigh + 1 - bitlow;
+       int shift = bitlow % 32;
+       int value;
+
+       regmap_read(ctrl->map, offset, &value);
+       value = (value >> shift) & (BIT(bits) - 1);
+
+       return value;
+}
+
+static struct rtpcs_link *rtpcs_phylink_pcs_to_link(struct phylink_pcs *pcs)
+{
+       return container_of(pcs, struct rtpcs_link, pcs);
+}
+
+static void rtpcs_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state)
+{
+       struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
+       struct rtpcs_ctrl *ctrl = link->ctrl;
+       int port = link->port;
+       int linkup, speed;
+
+       state->link = 0;
+       state->speed = SPEED_UNKNOWN;
+       state->duplex = DUPLEX_UNKNOWN;
+       state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
+
+       /* Read MAC side link twice */
+       for (int i = 0; i < 2; i++)
+               linkup = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_sts, port, port);
+
+       if (!linkup)
+               return;
+
+       state->link = 1;
+       state->duplex = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_dup_sts, port, port);
+
+       speed = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_spd_sts,
+                                      ctrl->cfg->mac_link_spd_bits * (port + 1) - 1,
+                                      ctrl->cfg->mac_link_spd_bits * port);
+       switch (speed) {
+       case RTPCS_SPEED_10:
+               state->speed = SPEED_10;
+               break;
+       case RTPCS_SPEED_100:
+               state->speed = SPEED_100;
+               break;
+       case RTPCS_SPEED_1000:
+               state->speed = SPEED_1000;
+               break;
+       case RTPCS_SPEED_10000:
+       case RTPCS_SPEED_10000_LEGACY:
+               /*
+                * The legacy mode is ok so far with minor inconsistencies. On RTL838x this flag
+                * is either 500M or 2G. It might be that MAC_GLITE_STS register tells more. On
+                * RTL839x this is either 500M or 10G. More info might be in MAC_LINK_500M_STS.
+                * Without support for the 500M modes simply resolve to 10G.
+                */
+               state->speed = SPEED_10000;
+               break;
+       case RTPCS_SPEED_2500:
+               state->speed = SPEED_2500;
+               break;
+       case RTPCS_SPEED_5000:
+               state->speed = SPEED_5000;
+               break;
+       default:
+               dev_err(ctrl->dev, "unknown speed %d\n", speed);
+       }
+
+       if (rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_rx_pause_sts, port, port))
+               state->pause |= MLO_PAUSE_RX;
+       if (rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_tx_pause_sts, port, port))
+               state->pause |= MLO_PAUSE_TX;
+}
+
+static void rtpcs_pcs_an_restart(struct phylink_pcs *pcs)
+{
+       struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
+       struct rtpcs_ctrl *ctrl = link->ctrl;
+
+       dev_warn(ctrl->dev, "an_restart() for port %d and sds %d not yet implemented\n",
+                link->port, link->sds);
+}
+
+static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+                           phy_interface_t interface, const unsigned long *advertising,
+                           bool permit_pause_to_mac)
+{
+       struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
+       struct rtpcs_ctrl *ctrl = link->ctrl;
+
+       /*
+        * TODO: This (or copies of this) will be the central function for configuring the
+        * link between PHY and SerDes. As of now a lot of the code is scattered throughout
+        * all the other Realtek drivers. Maybe some day this will live up to the expectations.
+        */
+
+       dev_warn(ctrl->dev, "pcs_config(%s) for port %d and sds %d not yet implemented\n",
+                phy_modes(interface), link->port, link->sds);
+
+       return 0;
+}
+
+struct phylink_pcs *rtpcs_create(struct device *dev, struct device_node *np, int port);
+struct phylink_pcs *rtpcs_create(struct device *dev, struct device_node *np, int port)
+{
+       struct platform_device *pdev;
+       struct device_node *pcs_np;
+       struct rtpcs_ctrl *ctrl;
+       struct rtpcs_link *link;
+       int sds;
+
+       /*
+        * RTL838x devices have a built-in octa port RTL8218B PHY that is not attached via
+        * a SerDes. Allow to be called with an empty SerDes device node. In this case lookup
+        * the parent/driver node directly.
+        */
+       if (np) {
+               if (!of_device_is_available(np))
+                       return ERR_PTR(-ENODEV);
+
+               if (of_property_read_u32(np, "reg", &sds))
+                       return ERR_PTR(-EINVAL);
+
+               pcs_np = of_get_parent(np);
+       } else {
+               pcs_np = of_find_compatible_node(NULL, NULL, "realtek,otto-pcs");
+               sds = -1;
+       }
+
+       if (!pcs_np)
+               return ERR_PTR(-ENODEV);
+
+       if (!of_device_is_available(pcs_np)) {
+               of_node_put(pcs_np);
+               return ERR_PTR(-ENODEV);
+       }
+
+       pdev = of_find_device_by_node(pcs_np);
+       of_node_put(pcs_np);
+       if (!pdev)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       ctrl = platform_get_drvdata(pdev);
+       if (!ctrl) {
+               put_device(&pdev->dev);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       if (port < 0 || port > ctrl->cfg->cpu_port)
+               return ERR_PTR(-EINVAL);
+
+       if (sds !=-1 && rtpcs_sds_read(ctrl, sds, 0 , 0) < 0)
+               return ERR_PTR(-EINVAL);
+
+       link = kzalloc(sizeof(*link), GFP_KERNEL);
+       if (!link) {
+               put_device(&pdev->dev);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       device_link_add(dev, ctrl->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+
+       link->ctrl = ctrl;
+       link->port = port;
+       link->sds = sds;
+       link->pcs.ops = ctrl->cfg->pcs_ops;
+
+       ctrl->link[port] = link;
+
+       dev_dbg(ctrl->dev, "phylink_pcs created, port %d, sds %d\n", port, sds);
+
+       return &link->pcs;
+}
+EXPORT_SYMBOL(rtpcs_create);
+
+static struct mii_bus *rtpcs_probe_serdes_bus(struct rtpcs_ctrl *ctrl)
+{
+       struct device_node *np;
+       struct mii_bus *bus;
+
+       np = of_find_compatible_node(NULL, NULL, "realtek,otto-serdes-mdio");
+       if (!np) {
+               dev_err(ctrl->dev, "SerDes mdio bus not found in DT");
+               return ERR_PTR(-ENODEV);
+       }
+
+       bus = of_mdio_find_bus(np);
+       of_node_put(np);
+       if (!bus) {
+               dev_warn(ctrl->dev, "SerDes mdio bus not (yet) active");
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       if (!of_device_is_available(np)) {
+               dev_err(ctrl->dev, "SerDes mdio bus not usable");
+               return ERR_PTR(-ENODEV);
+       }
+
+       return bus;
+}
+
+static int rtpcs_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct rtpcs_ctrl *ctrl;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->dev = dev;
+       ctrl->cfg = (const struct rtpcs_config *)device_get_match_data(ctrl->dev);
+       ctrl->map = syscon_node_to_regmap(np->parent);
+       if (IS_ERR(ctrl->map))
+               return PTR_ERR(ctrl->map);
+
+       ctrl->bus = rtpcs_probe_serdes_bus(ctrl);
+       if (IS_ERR(ctrl->bus))
+               return PTR_ERR(ctrl->bus);
+       /*
+        * rtpcs_create() relies on that fact that data is attached to the platform device to
+        * determine if the driver is ready. Do this after everything is initialized properly.
+        */
+       platform_set_drvdata(pdev, ctrl);
+
+       dev_info(dev, "Realtek PCS driver initialized\n");
+
+       return 0;
+}
+
+static const struct phylink_pcs_ops rtpcs_838x_pcs_ops = {
+       .pcs_an_restart         = rtpcs_pcs_an_restart,
+       .pcs_config             = rtpcs_pcs_config,
+       .pcs_get_state          = rtpcs_pcs_get_state,
+};
+
+static const struct rtpcs_config rtpcs_838x_cfg = {
+       .cpu_port               = RTPCS_838X_CPU_PORT,
+       .mac_link_dup_sts       = RTPCS_838X_MAC_LINK_DUP_STS,
+       .mac_link_spd_sts       = RTPCS_838X_MAC_LINK_SPD_STS,
+       .mac_link_spd_bits      = RTPCS_83XX_MAC_LINK_SPD_BITS,
+       .mac_link_sts           = RTPCS_838X_MAC_LINK_STS,
+       .mac_rx_pause_sts       = RTPCS_838X_MAC_RX_PAUSE_STS,
+       .mac_tx_pause_sts       = RTPCS_838X_MAC_TX_PAUSE_STS,
+       .pcs_ops                = &rtpcs_838x_pcs_ops,
+};
+
+static const struct phylink_pcs_ops rtpcs_839x_pcs_ops = {
+       .pcs_an_restart         = rtpcs_pcs_an_restart,
+       .pcs_config             = rtpcs_pcs_config,
+       .pcs_get_state          = rtpcs_pcs_get_state,
+};
+
+static const struct rtpcs_config rtpcs_839x_cfg = {
+       .cpu_port               = RTPCS_839X_CPU_PORT,
+       .mac_link_dup_sts       = RTPCS_839X_MAC_LINK_DUP_STS,
+       .mac_link_spd_sts       = RTPCS_839X_MAC_LINK_SPD_STS,
+       .mac_link_spd_bits      = RTPCS_83XX_MAC_LINK_SPD_BITS,
+       .mac_link_sts           = RTPCS_839X_MAC_LINK_STS,
+       .mac_rx_pause_sts       = RTPCS_839X_MAC_RX_PAUSE_STS,
+       .mac_tx_pause_sts       = RTPCS_839X_MAC_TX_PAUSE_STS,
+       .pcs_ops                = &rtpcs_839x_pcs_ops,
+};
+
+static const struct phylink_pcs_ops rtpcs_930x_pcs_ops = {
+       .pcs_an_restart         = rtpcs_pcs_an_restart,
+       .pcs_config             = rtpcs_pcs_config,
+       .pcs_get_state          = rtpcs_pcs_get_state,
+};
+
+static const struct rtpcs_config rtpcs_930x_cfg = {
+       .cpu_port               = RTPCS_930X_CPU_PORT,
+       .mac_link_dup_sts       = RTPCS_930X_MAC_LINK_DUP_STS,
+       .mac_link_spd_sts       = RTPCS_930X_MAC_LINK_SPD_STS,
+       .mac_link_spd_bits      = RTPCS_93XX_MAC_LINK_SPD_BITS,
+       .mac_link_sts           = RTPCS_930X_MAC_LINK_STS,
+       .mac_rx_pause_sts       = RTPCS_930X_MAC_RX_PAUSE_STS,
+       .mac_tx_pause_sts       = RTPCS_930X_MAC_TX_PAUSE_STS,
+       .pcs_ops                = &rtpcs_930x_pcs_ops,
+};
+
+static const struct phylink_pcs_ops rtpcs_931x_pcs_ops = {
+       .pcs_an_restart         = rtpcs_pcs_an_restart,
+       .pcs_config             = rtpcs_pcs_config,
+       .pcs_get_state          = rtpcs_pcs_get_state,
+};
+
+static const struct rtpcs_config rtpcs_931x_cfg = {
+       .cpu_port               = RTPCS_931X_CPU_PORT,
+       .mac_link_dup_sts       = RTPCS_931X_MAC_LINK_DUP_STS,
+       .mac_link_spd_sts       = RTPCS_931X_MAC_LINK_SPD_STS,
+       .mac_link_spd_bits      = RTPCS_93XX_MAC_LINK_SPD_BITS,
+       .mac_link_sts           = RTPCS_931X_MAC_LINK_STS,
+       .mac_rx_pause_sts       = RTPCS_931X_MAC_RX_PAUSE_STS,
+       .mac_tx_pause_sts       = RTPCS_931X_MAC_TX_PAUSE_STS,
+       .pcs_ops                = &rtpcs_931x_pcs_ops,
+};
+
+static const struct of_device_id rtpcs_of_match[] = {
+       {
+               .compatible = "realtek,rtl8380-pcs",
+               .data = &rtpcs_838x_cfg,
+       },
+       {
+               .compatible = "realtek,rtl8392-pcs",
+               .data = &rtpcs_839x_cfg,
+       },
+       {
+               .compatible = "realtek,rtl9301-pcs",
+               .data = &rtpcs_930x_cfg,
+       },
+       {
+               .compatible = "realtek,rtl9311-pcs",
+               .data = &rtpcs_931x_cfg,
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtpcs_of_match);
+
+static struct platform_driver rtpcs_driver = {
+       .driver = {
+               .name = "realtek-otto-pcs",
+               .of_match_table = rtpcs_of_match
+       },
+       .probe = rtpcs_probe,
+};
+module_platform_driver(rtpcs_driver);
+
+MODULE_AUTHOR("Markus Stockhausen <markus.stockhausen@gmx.de>");
+MODULE_DESCRIPTION("Realtek Otto SerDes PCS driver");
+MODULE_LICENSE("GPL v2");
\ No newline at end of file
diff --git a/target/linux/realtek/patches-6.12/730-add-pcs-rtl-otto.patch b/target/linux/realtek/patches-6.12/730-add-pcs-rtl-otto.patch
new file mode 100644 (file)
index 0000000..27927bd
--- /dev/null
@@ -0,0 +1,37 @@
+From ad75da9aaa8765b2115e7b40ee4f6dbcd60c3321 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+Date: Weg, 17 Sep 2025 20:23:31 +0200
+Subject: [PATCH] net: pcs: Add Realtek Otto SerDes controller
+
+SoCs in Realtek's Otto platform such as the RTL83xx and RTL93xx
+have multiple SerDes to drive the PHYs. Provide a PCS driver
+to configure them.
+
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+---
+--- a/drivers/net/pcs/Kconfig
++++ b/drivers/net/pcs/Kconfig
+@@ -36,6 +36,14 @@ config PCS_MTK_USXGMII
+         1000Base-X, 2500Base-X and Cisco SGMII are supported on the same
+         differential pairs via an embedded LynxI PHY.
++config PCS_RTL_OTTO
++      tristate "Realtek Otto SerDes PCS"
++      depends on MACH_REALTEK_RTL || COMPILE_TEST
++      select PHYLINK
++      select REGMAP
++      help
++        This module provides a driver for the Realtek SerDes PCS
++
+ config PCS_RZN1_MIIC
+       tristate "Renesas RZ/N1 MII converter"
+       depends on OF && (ARCH_RZN1 || COMPILE_TEST)
+--- a/drivers/net/pcs/Makefile
++++ b/drivers/net/pcs/Makefile
+@@ -7,5 +7,6 @@ pcs_xpcs-$(CONFIG_PCS_XPCS)    := pcs-xpcs.
+ obj-$(CONFIG_PCS_XPCS)                += pcs_xpcs.o
+ obj-$(CONFIG_PCS_LYNX)                += pcs-lynx.o
+ obj-$(CONFIG_PCS_MTK_LYNXI)   += pcs-mtk-lynxi.o
++obj-$(CONFIG_PCS_RTL_OTTO)    += pcs-rtl-otto.o
+ obj-$(CONFIG_PCS_RZN1_MIIC)   += pcs-rzn1-miic.o
+ obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
index d156ce1a175bb8ff25fc2a32f1395883011de667..8afd239b3b2148c9e52ad16c5df42280d4331cd0 100644 (file)
@@ -193,6 +193,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y
index 56ae0bfe77f36bd4fc8ecb65582f7bb46b741637..022cafea463c69bc8aaf3d5280959b99e458a6b5 100644 (file)
@@ -199,6 +199,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y
index e160b060ac09b0a841faa7407c437cf7ca53f7bb..5b3b4260190725d19ceda4cfd21245ab31827956 100644 (file)
@@ -182,6 +182,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y
index 075b06f3626e2c3989db952e9896e1a1e1ad3215..c4a2806421d5ad6f710d8e96c9694c5ccc6b037f 100644 (file)
@@ -185,6 +185,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y
index 9c64303ec2efb4032c6f83d51e763ff7a1827692..ee51cdb2566f505f8b988c263047eb3fb4026a8a 100644 (file)
@@ -193,6 +193,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y
index 25e8b8ff4fcd8f031f1668c42dd38658dc0650b0..529739d7acb9c0b6efaf1342df5c42cd20608a8d 100644 (file)
@@ -197,6 +197,7 @@ CONFIG_PAGE_POOL=y
 CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
 CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
 CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PCS_RTL_OTTO=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHYLIB=y