kernel: modules: netdevices: add realtek DSA modules
authorLuiz Angelo Daros de Luca <luizluca@gmail.com>
Tue, 24 Oct 2023 18:56:03 +0000 (15:56 -0300)
committerRobert Marko <robimarko@gmail.com>
Thu, 6 Feb 2025 08:51:13 +0000 (09:51 +0100)
Uses upstream DSA switch modules (rtl8365mb, rtl8366), similar to
RTL8367C and rtl8366rb swconfig drivers.

The package dependencies exclude targets built without kernel CONFIG_OF.

It also fixes the rtl8366rb LED support.

Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/17182
Signed-off-by: Robert Marko <robimarko@gmail.com>
19 files changed:
package/kernel/linux/modules/netdevices.mk
target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch [new file with mode: 0644]

index 24a41c6b3f832ed3eb13140cf206944abf3d1d5f..d41870155551acf15e9ef075757dd603eea4c4d1 100644 (file)
@@ -637,6 +637,65 @@ endef
 
 $(eval $(call KernelPackage,dsa-qca8k))
 
+
+define KernelPackage/dsa-realtek
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Realtek common module RTL83xx DSA switch family
+  DEPENDS:=+kmod-dsa +kmod-phy-realtek +kmod-regmap-core @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml
+  KCONFIG:= \
+       CONFIG_NET_DSA_REALTEK \
+       CONFIG_NET_DSA_REALTEK_MDIO=y \
+       CONFIG_NET_DSA_REALTEK_SMI=y
+  FILES:= $(LINUX_DIR)/drivers/net/dsa/realtek/realtek_dsa.ko
+endef
+
+define KernelPackage/dsa-realtek/description
+  Common kernel module for Realtek RTL83xx DSA switch family
+endef
+
+$(eval $(call KernelPackage,dsa-realtek))
+
+
+define KernelPackage/dsa-rtl8366rb
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Realtek RTL8365MB switch DSA support
+  DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml
+  KCONFIG:= \
+       CONFIG_NET_DSA_REALTEK_RTL8366RB \
+       CONFIG_NET_DSA_TAG_RTL4_A
+  FILES:= \
+       $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8366.ko \
+       $(LINUX_DIR)/net/dsa/tag_rtl4_a.ko
+  AUTOLOAD:=$(call AutoLoad,42,rtl8366,1)
+endef
+
+define KernelPackage/dsa-rtl8366rb/description
+  DSA based kernel modules for the Realtek RTL8366RB switch family
+endef
+
+$(eval $(call KernelPackage,dsa-rtl8366rb))
+
+
+define KernelPackage/dsa-rtl8365mb
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Realtek RTL8365MB switch DSA support
+  DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml
+  KCONFIG:= \
+       CONFIG_NET_DSA_REALTEK_RTL8365MB \
+       CONFIG_NET_DSA_TAG_RTL8_4
+  FILES:= \
+       $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8365mb.ko \
+       $(LINUX_DIR)/net/dsa/tag_rtl8_4.ko
+  AUTOLOAD:=$(call AutoLoad,42,rtl8365mb,1)
+endef
+
+define KernelPackage/dsa-rtl8365mb/description
+  DSA based kernel modules for the Realtek RTL8365MB switch family
+endef
+
+$(eval $(call KernelPackage,dsa-rtl8365mb))
+
+
 define KernelPackage/swconfig
   SUBMENU:=$(NETWORK_DEVICES_MENU)
   TITLE:=switch configuration API
diff --git a/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch b/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch
new file mode 100644 (file)
index 0000000..d19338e
--- /dev/null
@@ -0,0 +1,66 @@
+From d48a5472b8f2b29800bb25913f9403765005f1bc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Mon, 18 Sep 2023 21:19:14 +0200
+Subject: [PATCH] net: dsa: realtek: Convert to platform remove callback
+ returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart
+from emitting a warning) and this typically results in resource leaks.
+To improve here there is a quest to make the remove callback return
+void. In the first step of this quest all drivers are converted to
+.remove_new() which already returns void. Eventually after all drivers
+are converted, .remove_new() is renamed to .remove().
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+---
+ drivers/net/dsa/realtek/realtek-smi.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -506,12 +506,12 @@ static int realtek_smi_probe(struct plat
+       return 0;
+ }
+-static int realtek_smi_remove(struct platform_device *pdev)
++static void realtek_smi_remove(struct platform_device *pdev)
+ {
+       struct realtek_priv *priv = platform_get_drvdata(pdev);
+       if (!priv)
+-              return 0;
++              return;
+       dsa_unregister_switch(priv->ds);
+       if (priv->slave_mii_bus)
+@@ -520,8 +520,6 @@ static int realtek_smi_remove(struct pla
+       /* leave the device reset asserted */
+       if (priv->reset)
+               gpiod_set_value(priv->reset, 1);
+-
+-      return 0;
+ }
+ static void realtek_smi_shutdown(struct platform_device *pdev)
+@@ -559,7 +557,7 @@ static struct platform_driver realtek_sm
+               .of_match_table = realtek_smi_of_match,
+       },
+       .probe  = realtek_smi_probe,
+-      .remove = realtek_smi_remove,
++      .remove_new = realtek_smi_remove,
+       .shutdown = realtek_smi_shutdown,
+ };
+ module_platform_driver(realtek_smi_driver);
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch b/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch
new file mode 100644 (file)
index 0000000..82958ec
--- /dev/null
@@ -0,0 +1,30 @@
+From 33f4336cbd32c21717b60d013693a0bd51a27db6 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:37 -0300
+Subject: net: dsa: realtek: drop cleanup from realtek_ops
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It was never used and never referenced.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -91,7 +91,6 @@ struct realtek_ops {
+       int     (*detect)(struct realtek_priv *priv);
+       int     (*reset_chip)(struct realtek_priv *priv);
+       int     (*setup)(struct realtek_priv *priv);
+-      void    (*cleanup)(struct realtek_priv *priv);
+       int     (*get_mib_counter)(struct realtek_priv *priv,
+                                  int port,
+                                  struct rtl8366_mib_counter *mib,
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch b/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch
new file mode 100644 (file)
index 0000000..d50ea1b
--- /dev/null
@@ -0,0 +1,146 @@
+From ded3813b44fe11a3bbd2c9d7df8870e8c19a7ccd Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:38 -0300
+Subject: net: dsa: realtek: introduce REALTEK_DSA namespace
+
+Create a namespace to group the exported symbols.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-mdio.c |  1 +
+ drivers/net/dsa/realtek/realtek-smi.c  |  1 +
+ drivers/net/dsa/realtek/rtl8365mb.c    |  1 +
+ drivers/net/dsa/realtek/rtl8366-core.c | 22 +++++++++++-----------
+ drivers/net/dsa/realtek/rtl8366rb.c    |  1 +
+ 5 files changed, 15 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -288,3 +288,4 @@ mdio_module_driver(realtek_mdio_driver);
+ MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
+ MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -565,3 +565,4 @@ module_platform_driver(realtek_smi_drive
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -2178,3 +2178,4 @@ EXPORT_SYMBOL_GPL(rtl8365mb_variant);
+ MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+ MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/rtl8366-core.c
++++ b/drivers/net/dsa/realtek/rtl8366-core.c
+@@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_pr
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_mc_is_used);
++EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA);
+ /**
+  * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration
+@@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv
+       return ret;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
++EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA);
+ int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port,
+                    unsigned int vid)
+@@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_set_pvid);
++EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA);
+ int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable)
+ {
+@@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek
+       priv->vlan4k_enabled = enable;
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA);
+ int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable)
+ {
+@@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_p
+       return ret;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA);
+ int rtl8366_reset_vlan(struct realtek_priv *priv)
+ {
+@@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_pr
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
++EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA);
+ int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_vlan *vlan,
+@@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch *
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA);
+ int rtl8366_vlan_del(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_vlan *vlan)
+@@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch *
+       return 0;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA);
+ void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+                        uint8_t *data)
+@@ -407,7 +407,7 @@ void rtl8366_get_strings(struct dsa_swit
+                       mib->name, ETH_GSTRING_LEN);
+       }
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_get_strings);
++EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA);
+ int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
+ {
+@@ -421,7 +421,7 @@ int rtl8366_get_sset_count(struct dsa_sw
+       return priv->num_mib_counters;
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
++EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA);
+ void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+ {
+@@ -445,4 +445,4 @@ void rtl8366_get_ethtool_stats(struct ds
+               data[i] = mibvalue;
+       }
+ }
+-EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);
++EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -1852,3 +1852,4 @@ EXPORT_SYMBOL_GPL(rtl8366rb_variant);
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(REALTEK_DSA);
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch b/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch
new file mode 100644 (file)
index 0000000..aa6dfcf
--- /dev/null
@@ -0,0 +1,539 @@
+From bce254b839abe67577bebdef0838796af409c229 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:39 -0300
+Subject: net: dsa: realtek: convert variants into real drivers
+
+Previously, the interface modules realtek-smi and realtek-mdio served as
+a platform and an MDIO driver, respectively. Each interface module
+redundantly specified the same compatible strings for both variants and
+referenced symbols from the variants.
+
+Now, each variant module has been transformed into a unified driver
+serving both as a platform and an MDIO driver. This modification
+reverses the relationship between the interface and variant modules,
+with the variant module now utilizing symbols from the interface
+modules.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/Kconfig        | 20 +++----
+ drivers/net/dsa/realtek/realtek-mdio.c | 68 +++++++++++++++---------
+ drivers/net/dsa/realtek/realtek-mdio.h | 48 +++++++++++++++++
+ drivers/net/dsa/realtek/realtek-smi.c  | 73 +++++++++++++++-----------
+ drivers/net/dsa/realtek/realtek-smi.h  | 48 +++++++++++++++++
+ drivers/net/dsa/realtek/rtl8365mb.c    | 54 ++++++++++++++++++-
+ drivers/net/dsa/realtek/rtl8366rb.c    | 54 ++++++++++++++++++-
+ 7 files changed, 292 insertions(+), 73 deletions(-)
+ create mode 100644 drivers/net/dsa/realtek/realtek-mdio.h
+ create mode 100644 drivers/net/dsa/realtek/realtek-smi.h
+
+--- a/drivers/net/dsa/realtek/Kconfig
++++ b/drivers/net/dsa/realtek/Kconfig
+@@ -16,37 +16,29 @@ menuconfig NET_DSA_REALTEK
+ if NET_DSA_REALTEK
+ config NET_DSA_REALTEK_MDIO
+-      tristate "Realtek MDIO interface driver"
++      tristate "Realtek MDIO interface support"
+       depends on OF
+-      depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB
+-      depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB
+-      depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB
+       help
+         Select to enable support for registering switches configured
+         through MDIO.
+ config NET_DSA_REALTEK_SMI
+-      tristate "Realtek SMI interface driver"
++      tristate "Realtek SMI interface support"
+       depends on OF
+-      depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB
+-      depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB
+-      depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB
+       help
+         Select to enable support for registering switches connected
+         through SMI.
+ config NET_DSA_REALTEK_RTL8365MB
+-      tristate "Realtek RTL8365MB switch subdriver"
+-      imply NET_DSA_REALTEK_SMI
+-      imply NET_DSA_REALTEK_MDIO
++      tristate "Realtek RTL8365MB switch driver"
++      depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO
+       select NET_DSA_TAG_RTL8_4
+       help
+         Select to enable support for Realtek RTL8365MB-VC and RTL8367S.
+ config NET_DSA_REALTEK_RTL8366RB
+-      tristate "Realtek RTL8366RB switch subdriver"
+-      imply NET_DSA_REALTEK_SMI
+-      imply NET_DSA_REALTEK_MDIO
++      tristate "Realtek RTL8366RB switch driver"
++      depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO
+       select NET_DSA_TAG_RTL4_A
+       help
+         Select to enable support for Realtek RTL8366RB.
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -25,6 +25,7 @@
+ #include <linux/regmap.h>
+ #include "realtek.h"
++#include "realtek-mdio.h"
+ /* Read/write via mdiobus */
+ #define REALTEK_MDIO_CTRL0_REG                31
+@@ -140,7 +141,19 @@ static const struct regmap_config realte
+       .disable_locking = true,
+ };
+-static int realtek_mdio_probe(struct mdio_device *mdiodev)
++/**
++ * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
++ * @mdiodev: mdio_device to probe on.
++ *
++ * This function should be used as the .probe in an mdio_driver. It
++ * initializes realtek_priv and read data from the device-tree node. The switch
++ * is hard reset if a method is provided. It checks the switch chip ID and,
++ * finally, a DSA switch is registered.
++ *
++ * Context: Can sleep. Takes and releases priv->map_lock.
++ * Return: Returns 0 on success, a negative error on failure.
++ */
++int realtek_mdio_probe(struct mdio_device *mdiodev)
+ {
+       struct realtek_priv *priv;
+       struct device *dev = &mdiodev->dev;
+@@ -235,8 +248,20 @@ static int realtek_mdio_probe(struct mdi
+       return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);
+-static void realtek_mdio_remove(struct mdio_device *mdiodev)
++/**
++ * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch
++ * @mdiodev: mdio_device to be removed.
++ *
++ * This function should be used as the .remove_new in an mdio_driver. First
++ * it unregisters the DSA switch and cleans internal data. If a method is
++ * provided, the hard reset is asserted to avoid traffic leakage.
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void realtek_mdio_remove(struct mdio_device *mdiodev)
+ {
+       struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
+@@ -249,8 +274,21 @@ static void realtek_mdio_remove(struct m
+       if (priv->reset)
+               gpiod_set_value(priv->reset, 1);
+ }
++EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
+-static void realtek_mdio_shutdown(struct mdio_device *mdiodev)
++/**
++ * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
++ * @mdiodev: mdio_device shutting down.
++ *
++ * This function should be used as the .shutdown in an mdio_driver. It shuts
++ * down the DSA switch and cleans the platform driver data, to prevent
++ * realtek_mdio_remove() from running afterwards, which is possible if the
++ * parent bus implements its own .shutdown() as .remove().
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void realtek_mdio_shutdown(struct mdio_device *mdiodev)
+ {
+       struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
+@@ -261,29 +299,7 @@ static void realtek_mdio_shutdown(struct
+       dev_set_drvdata(&mdiodev->dev, NULL);
+ }
+-
+-static const struct of_device_id realtek_mdio_of_match[] = {
+-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
+-      { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
+-#endif
+-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
+-      { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
+-#endif
+-      { /* sentinel */ },
+-};
+-MODULE_DEVICE_TABLE(of, realtek_mdio_of_match);
+-
+-static struct mdio_driver realtek_mdio_driver = {
+-      .mdiodrv.driver = {
+-              .name = "realtek-mdio",
+-              .of_match_table = realtek_mdio_of_match,
+-      },
+-      .probe  = realtek_mdio_probe,
+-      .remove = realtek_mdio_remove,
+-      .shutdown = realtek_mdio_shutdown,
+-};
+-
+-mdio_module_driver(realtek_mdio_driver);
++EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
+ MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
+ MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
+--- /dev/null
++++ b/drivers/net/dsa/realtek/realtek-mdio.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++
++#ifndef _REALTEK_MDIO_H
++#define _REALTEK_MDIO_H
++
++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO)
++
++static inline int realtek_mdio_driver_register(struct mdio_driver *drv)
++{
++      return mdio_driver_register(drv);
++}
++
++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)
++{
++      mdio_driver_unregister(drv);
++}
++
++int realtek_mdio_probe(struct mdio_device *mdiodev);
++void realtek_mdio_remove(struct mdio_device *mdiodev);
++void realtek_mdio_shutdown(struct mdio_device *mdiodev);
++
++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */
++
++static inline int realtek_mdio_driver_register(struct mdio_driver *drv)
++{
++      return 0;
++}
++
++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)
++{
++}
++
++static inline int realtek_mdio_probe(struct mdio_device *mdiodev)
++{
++      return -ENOENT;
++}
++
++static inline void realtek_mdio_remove(struct mdio_device *mdiodev)
++{
++}
++
++static inline void realtek_mdio_shutdown(struct mdio_device *mdiodev)
++{
++}
++
++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */
++
++#endif /* _REALTEK_MDIO_H */
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -40,6 +40,7 @@
+ #include <linux/if_bridge.h>
+ #include "realtek.h"
++#include "realtek-smi.h"
+ #define REALTEK_SMI_ACK_RETRY_COUNT           5
+@@ -408,7 +409,19 @@ err_put_node:
+       return ret;
+ }
+-static int realtek_smi_probe(struct platform_device *pdev)
++/**
++ * realtek_smi_probe() - Probe a platform device for an SMI-connected switch
++ * @pdev: platform_device to probe on.
++ *
++ * This function should be used as the .probe in a platform_driver. It
++ * initializes realtek_priv and read data from the device-tree node. The switch
++ * is hard reset if a method is provided. It checks the switch chip ID and,
++ * finally, a DSA switch is registered.
++ *
++ * Context: Can sleep. Takes and releases priv->map_lock.
++ * Return: Returns 0 on success, a negative error on failure.
++ */
++int realtek_smi_probe(struct platform_device *pdev)
+ {
+       const struct realtek_variant *var;
+       struct device *dev = &pdev->dev;
+@@ -505,8 +518,20 @@ static int realtek_smi_probe(struct plat
+       }
+       return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
+-static void realtek_smi_remove(struct platform_device *pdev)
++/**
++ * realtek_smi_remove() - Remove the driver of a SMI-connected switch
++ * @pdev: platform_device to be removed.
++ *
++ * This function should be used as the .remove_new in a platform_driver. First
++ * it unregisters the DSA switch and cleans internal data. If a method is
++ * provided, the hard reset is asserted to avoid traffic leakage.
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void realtek_smi_remove(struct platform_device *pdev)
+ {
+       struct realtek_priv *priv = platform_get_drvdata(pdev);
+@@ -521,8 +546,21 @@ static void realtek_smi_remove(struct pl
+       if (priv->reset)
+               gpiod_set_value(priv->reset, 1);
+ }
++EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
+-static void realtek_smi_shutdown(struct platform_device *pdev)
++/**
++ * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch
++ * @pdev: platform_device shutting down.
++ *
++ * This function should be used as the .shutdown in a platform_driver. It shuts
++ * down the DSA switch and cleans the platform driver data, to prevent
++ * realtek_smi_remove() from running afterwards, which is possible if the
++ * parent bus implements its own .shutdown() as .remove().
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void realtek_smi_shutdown(struct platform_device *pdev)
+ {
+       struct realtek_priv *priv = platform_get_drvdata(pdev);
+@@ -533,34 +571,7 @@ static void realtek_smi_shutdown(struct
+       platform_set_drvdata(pdev, NULL);
+ }
+-
+-static const struct of_device_id realtek_smi_of_match[] = {
+-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
+-      {
+-              .compatible = "realtek,rtl8366rb",
+-              .data = &rtl8366rb_variant,
+-      },
+-#endif
+-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
+-      {
+-              .compatible = "realtek,rtl8365mb",
+-              .data = &rtl8365mb_variant,
+-      },
+-#endif
+-      { /* sentinel */ },
+-};
+-MODULE_DEVICE_TABLE(of, realtek_smi_of_match);
+-
+-static struct platform_driver realtek_smi_driver = {
+-      .driver = {
+-              .name = "realtek-smi",
+-              .of_match_table = realtek_smi_of_match,
+-      },
+-      .probe  = realtek_smi_probe,
+-      .remove_new = realtek_smi_remove,
+-      .shutdown = realtek_smi_shutdown,
+-};
+-module_platform_driver(realtek_smi_driver);
++EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
+--- /dev/null
++++ b/drivers/net/dsa/realtek/realtek-smi.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++
++#ifndef _REALTEK_SMI_H
++#define _REALTEK_SMI_H
++
++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI)
++
++static inline int realtek_smi_driver_register(struct platform_driver *drv)
++{
++      return platform_driver_register(drv);
++}
++
++static inline void realtek_smi_driver_unregister(struct platform_driver *drv)
++{
++      platform_driver_unregister(drv);
++}
++
++int realtek_smi_probe(struct platform_device *pdev);
++void realtek_smi_remove(struct platform_device *pdev);
++void realtek_smi_shutdown(struct platform_device *pdev);
++
++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */
++
++static inline int realtek_smi_driver_register(struct platform_driver *drv)
++{
++      return 0;
++}
++
++static inline void realtek_smi_driver_unregister(struct platform_driver *drv)
++{
++}
++
++static inline int realtek_smi_probe(struct platform_device *pdev)
++{
++      return -ENOENT;
++}
++
++static inline void realtek_smi_remove(struct platform_device *pdev)
++{
++}
++
++static inline void realtek_smi_shutdown(struct platform_device *pdev)
++{
++}
++
++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */
++
++#endif  /* _REALTEK_SMI_H */
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -101,6 +101,8 @@
+ #include <linux/if_vlan.h>
+ #include "realtek.h"
++#include "realtek-smi.h"
++#include "realtek-mdio.h"
+ /* Family-specific data and limits */
+ #define RTL8365MB_PHYADDRMAX          7
+@@ -2173,7 +2175,57 @@ const struct realtek_variant rtl8365mb_v
+       .cmd_write = 0xb8,
+       .chip_data_sz = sizeof(struct rtl8365mb),
+ };
+-EXPORT_SYMBOL_GPL(rtl8365mb_variant);
++
++static const struct of_device_id rtl8365mb_of_match[] = {
++      { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
++      { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, rtl8365mb_of_match);
++
++static struct platform_driver rtl8365mb_smi_driver = {
++      .driver = {
++              .name = "rtl8365mb-smi",
++              .of_match_table = rtl8365mb_of_match,
++      },
++      .probe  = realtek_smi_probe,
++      .remove_new = realtek_smi_remove,
++      .shutdown = realtek_smi_shutdown,
++};
++
++static struct mdio_driver rtl8365mb_mdio_driver = {
++      .mdiodrv.driver = {
++              .name = "rtl8365mb-mdio",
++              .of_match_table = rtl8365mb_of_match,
++      },
++      .probe  = realtek_mdio_probe,
++      .remove = realtek_mdio_remove,
++      .shutdown = realtek_mdio_shutdown,
++};
++
++static int rtl8365mb_init(void)
++{
++      int ret;
++
++      ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver);
++      if (ret)
++              return ret;
++
++      ret = realtek_smi_driver_register(&rtl8365mb_smi_driver);
++      if (ret) {
++              realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver);
++              return ret;
++      }
++
++      return 0;
++}
++module_init(rtl8365mb_init);
++
++static void __exit rtl8365mb_exit(void)
++{
++      realtek_smi_driver_unregister(&rtl8365mb_smi_driver);
++      realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver);
++}
++module_exit(rtl8365mb_exit);
+ MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+ MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -22,6 +22,8 @@
+ #include <linux/regmap.h>
+ #include "realtek.h"
++#include "realtek-smi.h"
++#include "realtek-mdio.h"
+ #define RTL8366RB_PORT_NUM_CPU                5
+ #define RTL8366RB_NUM_PORTS           6
+@@ -1847,7 +1849,57 @@ const struct realtek_variant rtl8366rb_v
+       .cmd_write = 0xa8,
+       .chip_data_sz = sizeof(struct rtl8366rb),
+ };
+-EXPORT_SYMBOL_GPL(rtl8366rb_variant);
++
++static const struct of_device_id rtl8366rb_of_match[] = {
++      { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
++      { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, rtl8366rb_of_match);
++
++static struct platform_driver rtl8366rb_smi_driver = {
++      .driver = {
++              .name = "rtl8366rb-smi",
++              .of_match_table = rtl8366rb_of_match,
++      },
++      .probe  = realtek_smi_probe,
++      .remove_new = realtek_smi_remove,
++      .shutdown = realtek_smi_shutdown,
++};
++
++static struct mdio_driver rtl8366rb_mdio_driver = {
++      .mdiodrv.driver = {
++              .name = "rtl8366rb-mdio",
++              .of_match_table = rtl8366rb_of_match,
++      },
++      .probe  = realtek_mdio_probe,
++      .remove = realtek_mdio_remove,
++      .shutdown = realtek_mdio_shutdown,
++};
++
++static int rtl8366rb_init(void)
++{
++      int ret;
++
++      ret = realtek_mdio_driver_register(&rtl8366rb_mdio_driver);
++      if (ret)
++              return ret;
++
++      ret = realtek_smi_driver_register(&rtl8366rb_smi_driver);
++      if (ret) {
++              realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver);
++              return ret;
++      }
++
++      return 0;
++}
++module_init(rtl8366rb_init);
++
++static void __exit rtl8366rb_exit(void)
++{
++      realtek_smi_driver_unregister(&rtl8366rb_smi_driver);
++      realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver);
++}
++module_exit(rtl8366rb_exit);
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch");
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch b/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch
new file mode 100644 (file)
index 0000000..b0e074b
--- /dev/null
@@ -0,0 +1,91 @@
+From 4667a1db2f550d23e01ba655fce331196ead6e92 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:40 -0300
+Subject: net: dsa: realtek: keep variant reference in
+ realtek_priv
+
+Instead of copying values from the variant, we can keep a reference in
+realtek_priv.
+
+This is a preliminary change for sharing code betwen interfaces. It will
+allow to move most of the probe into a common module while still allow
+code specific to each interface to read variant fields.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-mdio.c |  4 +---
+ drivers/net/dsa/realtek/realtek-smi.c  | 10 ++++------
+ drivers/net/dsa/realtek/realtek.h      |  5 ++---
+ 3 files changed, 7 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -196,9 +196,7 @@ int realtek_mdio_probe(struct mdio_devic
+       priv->dev = &mdiodev->dev;
+       priv->chip_data = (void *)priv + sizeof(*priv);
+-      priv->clk_delay = var->clk_delay;
+-      priv->cmd_read = var->cmd_read;
+-      priv->cmd_write = var->cmd_write;
++      priv->variant = var;
+       priv->ops = var->ops;
+       priv->write_reg_noack = realtek_mdio_write;
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -46,7 +46,7 @@
+ static inline void realtek_smi_clk_delay(struct realtek_priv *priv)
+ {
+-      ndelay(priv->clk_delay);
++      ndelay(priv->variant->clk_delay);
+ }
+ static void realtek_smi_start(struct realtek_priv *priv)
+@@ -209,7 +209,7 @@ static int realtek_smi_read_reg(struct r
+       realtek_smi_start(priv);
+       /* Send READ command */
+-      ret = realtek_smi_write_byte(priv, priv->cmd_read);
++      ret = realtek_smi_write_byte(priv, priv->variant->cmd_read);
+       if (ret)
+               goto out;
+@@ -250,7 +250,7 @@ static int realtek_smi_write_reg(struct
+       realtek_smi_start(priv);
+       /* Send WRITE command */
+-      ret = realtek_smi_write_byte(priv, priv->cmd_write);
++      ret = realtek_smi_write_byte(priv, priv->variant->cmd_write);
+       if (ret)
+               goto out;
+@@ -459,9 +459,7 @@ int realtek_smi_probe(struct platform_de
+       /* Link forward and backward */
+       priv->dev = dev;
+-      priv->clk_delay = var->clk_delay;
+-      priv->cmd_read = var->cmd_read;
+-      priv->cmd_write = var->cmd_write;
++      priv->variant = var;
+       priv->ops = var->ops;
+       priv->setup_interface = realtek_smi_setup_mdio;
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -58,9 +58,8 @@ struct realtek_priv {
+       struct mii_bus          *bus;
+       int                     mdio_addr;
+-      unsigned int            clk_delay;
+-      u8                      cmd_read;
+-      u8                      cmd_write;
++      const struct realtek_variant *variant;
++
+       spinlock_t              lock; /* Locks around command writes */
+       struct dsa_switch       *ds;
+       struct irq_domain       *irqdomain;
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch b/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch
new file mode 100644 (file)
index 0000000..544b1b2
--- /dev/null
@@ -0,0 +1,875 @@
+From 8be040ecd94c1a9a137927d18534edfae0a9b68a Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:41 -0300
+Subject: net: dsa: realtek: common rtl83xx module
+
+Some code can be shared between both interface modules (MDIO and SMI)
+and among variants. These interface functions migrated to a common
+module:
+
+- rtl83xx_lock
+- rtl83xx_unlock
+- rtl83xx_probe
+- rtl83xx_register_switch
+- rtl83xx_unregister_switch
+- rtl83xx_shutdown
+- rtl83xx_remove
+
+The reset during probe was moved to the end of the common probe. This way,
+we avoid a reset if anything else fails.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/Makefile       |   2 +
+ drivers/net/dsa/realtek/realtek-mdio.c | 152 +++-------------
+ drivers/net/dsa/realtek/realtek-smi.c  | 166 ++++-------------
+ drivers/net/dsa/realtek/realtek.h      |   1 +
+ drivers/net/dsa/realtek/rtl8365mb.c    |   9 +-
+ drivers/net/dsa/realtek/rtl8366rb.c    |   9 +-
+ drivers/net/dsa/realtek/rtl83xx.c      | 235 +++++++++++++++++++++++++
+ drivers/net/dsa/realtek/rtl83xx.h      |  21 +++
+ 8 files changed, 322 insertions(+), 273 deletions(-)
+ create mode 100644 drivers/net/dsa/realtek/rtl83xx.c
+ create mode 100644 drivers/net/dsa/realtek/rtl83xx.h
+
+--- a/drivers/net/dsa/realtek/Makefile
++++ b/drivers/net/dsa/realtek/Makefile
+@@ -1,4 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_NET_DSA_REALTEK)         += realtek_dsa.o
++realtek_dsa-objs                      := rtl83xx.o
+ obj-$(CONFIG_NET_DSA_REALTEK_MDIO)    += realtek-mdio.o
+ obj-$(CONFIG_NET_DSA_REALTEK_SMI)     += realtek-smi.o
+ obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -26,6 +26,7 @@
+ #include "realtek.h"
+ #include "realtek-mdio.h"
++#include "rtl83xx.h"
+ /* Read/write via mdiobus */
+ #define REALTEK_MDIO_CTRL0_REG                31
+@@ -100,147 +101,41 @@ out_unlock:
+       return ret;
+ }
+-static void realtek_mdio_lock(void *ctx)
+-{
+-      struct realtek_priv *priv = ctx;
+-
+-      mutex_lock(&priv->map_lock);
+-}
+-
+-static void realtek_mdio_unlock(void *ctx)
+-{
+-      struct realtek_priv *priv = ctx;
+-
+-      mutex_unlock(&priv->map_lock);
+-}
+-
+-static const struct regmap_config realtek_mdio_regmap_config = {
+-      .reg_bits = 10, /* A4..A0 R4..R0 */
+-      .val_bits = 16,
+-      .reg_stride = 1,
+-      /* PHY regs are at 0x8000 */
+-      .max_register = 0xffff,
+-      .reg_format_endian = REGMAP_ENDIAN_BIG,
++static const struct realtek_interface_info realtek_mdio_info = {
+       .reg_read = realtek_mdio_read,
+       .reg_write = realtek_mdio_write,
+-      .cache_type = REGCACHE_NONE,
+-      .lock = realtek_mdio_lock,
+-      .unlock = realtek_mdio_unlock,
+-};
+-
+-static const struct regmap_config realtek_mdio_nolock_regmap_config = {
+-      .reg_bits = 10, /* A4..A0 R4..R0 */
+-      .val_bits = 16,
+-      .reg_stride = 1,
+-      /* PHY regs are at 0x8000 */
+-      .max_register = 0xffff,
+-      .reg_format_endian = REGMAP_ENDIAN_BIG,
+-      .reg_read = realtek_mdio_read,
+-      .reg_write = realtek_mdio_write,
+-      .cache_type = REGCACHE_NONE,
+-      .disable_locking = true,
+ };
+ /**
+  * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
+  * @mdiodev: mdio_device to probe on.
+  *
+- * This function should be used as the .probe in an mdio_driver. It
+- * initializes realtek_priv and read data from the device-tree node. The switch
+- * is hard reset if a method is provided. It checks the switch chip ID and,
+- * finally, a DSA switch is registered.
++ * This function should be used as the .probe in an mdio_driver. After
++ * calling the common probe function for both interfaces, it initializes the
++ * values specific for MDIO-connected devices. Finally, it calls a common
++ * function to register the DSA switch.
+  *
+  * Context: Can sleep. Takes and releases priv->map_lock.
+  * Return: Returns 0 on success, a negative error on failure.
+  */
+ int realtek_mdio_probe(struct mdio_device *mdiodev)
+ {
+-      struct realtek_priv *priv;
+       struct device *dev = &mdiodev->dev;
+-      const struct realtek_variant *var;
+-      struct regmap_config rc;
+-      struct device_node *np;
++      struct realtek_priv *priv;
+       int ret;
+-      var = of_device_get_match_data(dev);
+-      if (!var)
+-              return -EINVAL;
+-
+-      priv = devm_kzalloc(&mdiodev->dev,
+-                          size_add(sizeof(*priv), var->chip_data_sz),
+-                          GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-
+-      mutex_init(&priv->map_lock);
+-
+-      rc = realtek_mdio_regmap_config;
+-      rc.lock_arg = priv;
+-      priv->map = devm_regmap_init(dev, NULL, priv, &rc);
+-      if (IS_ERR(priv->map)) {
+-              ret = PTR_ERR(priv->map);
+-              dev_err(dev, "regmap init failed: %d\n", ret);
+-              return ret;
+-      }
++      priv = rtl83xx_probe(dev, &realtek_mdio_info);
++      if (IS_ERR(priv))
++              return PTR_ERR(priv);
+-      rc = realtek_mdio_nolock_regmap_config;
+-      priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
+-      if (IS_ERR(priv->map_nolock)) {
+-              ret = PTR_ERR(priv->map_nolock);
+-              dev_err(dev, "regmap init failed: %d\n", ret);
+-              return ret;
+-      }
+-
+-      priv->mdio_addr = mdiodev->addr;
+       priv->bus = mdiodev->bus;
+-      priv->dev = &mdiodev->dev;
+-      priv->chip_data = (void *)priv + sizeof(*priv);
+-
+-      priv->variant = var;
+-      priv->ops = var->ops;
+-
++      priv->mdio_addr = mdiodev->addr;
+       priv->write_reg_noack = realtek_mdio_write;
++      priv->ds_ops = priv->variant->ds_ops_mdio;
+-      np = dev->of_node;
+-
+-      dev_set_drvdata(dev, priv);
+-
+-      /* TODO: if power is software controlled, set up any regulators here */
+-      priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
+-
+-      priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+-      if (IS_ERR(priv->reset)) {
+-              dev_err(dev, "failed to get RESET GPIO\n");
+-              return PTR_ERR(priv->reset);
+-      }
+-
+-      if (priv->reset) {
+-              gpiod_set_value(priv->reset, 1);
+-              dev_dbg(dev, "asserted RESET\n");
+-              msleep(REALTEK_HW_STOP_DELAY);
+-              gpiod_set_value(priv->reset, 0);
+-              msleep(REALTEK_HW_START_DELAY);
+-              dev_dbg(dev, "deasserted RESET\n");
+-      }
+-
+-      ret = priv->ops->detect(priv);
+-      if (ret) {
+-              dev_err(dev, "unable to detect switch\n");
+-              return ret;
+-      }
+-
+-      priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
+-      if (!priv->ds)
+-              return -ENOMEM;
+-
+-      priv->ds->dev = dev;
+-      priv->ds->num_ports = priv->num_ports;
+-      priv->ds->priv = priv;
+-      priv->ds->ops = var->ds_ops_mdio;
+-
+-      ret = dsa_register_switch(priv->ds);
++      ret = rtl83xx_register_switch(priv);
+       if (ret) {
+-              dev_err(priv->dev, "unable to register switch ret = %d\n", ret);
++              rtl83xx_remove(priv);
+               return ret;
+       }
+@@ -253,8 +148,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe,
+  * @mdiodev: mdio_device to be removed.
+  *
+  * This function should be used as the .remove_new in an mdio_driver. First
+- * it unregisters the DSA switch and cleans internal data. If a method is
+- * provided, the hard reset is asserted to avoid traffic leakage.
++ * it unregisters the DSA switch and then it calls the common remove function.
+  *
+  * Context: Can sleep.
+  * Return: Nothing.
+@@ -266,11 +160,9 @@ void realtek_mdio_remove(struct mdio_dev
+       if (!priv)
+               return;
+-      dsa_unregister_switch(priv->ds);
++      rtl83xx_unregister_switch(priv);
+-      /* leave the device reset asserted */
+-      if (priv->reset)
+-              gpiod_set_value(priv->reset, 1);
++      rtl83xx_remove(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
+@@ -278,10 +170,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove
+  * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
+  * @mdiodev: mdio_device shutting down.
+  *
+- * This function should be used as the .shutdown in an mdio_driver. It shuts
+- * down the DSA switch and cleans the platform driver data, to prevent
+- * realtek_mdio_remove() from running afterwards, which is possible if the
+- * parent bus implements its own .shutdown() as .remove().
++ * This function should be used as the .shutdown in a platform_driver. It calls
++ * the common shutdown function.
+  *
+  * Context: Can sleep.
+  * Return: Nothing.
+@@ -293,9 +183,7 @@ void realtek_mdio_shutdown(struct mdio_d
+       if (!priv)
+               return;
+-      dsa_switch_shutdown(priv->ds);
+-
+-      dev_set_drvdata(&mdiodev->dev, NULL);
++      rtl83xx_shutdown(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -41,6 +41,7 @@
+ #include "realtek.h"
+ #include "realtek-smi.h"
++#include "rtl83xx.h"
+ #define REALTEK_SMI_ACK_RETRY_COUNT           5
+@@ -311,47 +312,6 @@ static int realtek_smi_read(void *ctx, u
+       return realtek_smi_read_reg(priv, reg, val);
+ }
+-static void realtek_smi_lock(void *ctx)
+-{
+-      struct realtek_priv *priv = ctx;
+-
+-      mutex_lock(&priv->map_lock);
+-}
+-
+-static void realtek_smi_unlock(void *ctx)
+-{
+-      struct realtek_priv *priv = ctx;
+-
+-      mutex_unlock(&priv->map_lock);
+-}
+-
+-static const struct regmap_config realtek_smi_regmap_config = {
+-      .reg_bits = 10, /* A4..A0 R4..R0 */
+-      .val_bits = 16,
+-      .reg_stride = 1,
+-      /* PHY regs are at 0x8000 */
+-      .max_register = 0xffff,
+-      .reg_format_endian = REGMAP_ENDIAN_BIG,
+-      .reg_read = realtek_smi_read,
+-      .reg_write = realtek_smi_write,
+-      .cache_type = REGCACHE_NONE,
+-      .lock = realtek_smi_lock,
+-      .unlock = realtek_smi_unlock,
+-};
+-
+-static const struct regmap_config realtek_smi_nolock_regmap_config = {
+-      .reg_bits = 10, /* A4..A0 R4..R0 */
+-      .val_bits = 16,
+-      .reg_stride = 1,
+-      /* PHY regs are at 0x8000 */
+-      .max_register = 0xffff,
+-      .reg_format_endian = REGMAP_ENDIAN_BIG,
+-      .reg_read = realtek_smi_read,
+-      .reg_write = realtek_smi_write,
+-      .cache_type = REGCACHE_NONE,
+-      .disable_locking = true,
+-};
+-
+ static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
+ {
+       struct realtek_priv *priv = bus->priv;
+@@ -409,111 +369,56 @@ err_put_node:
+       return ret;
+ }
++static const struct realtek_interface_info realtek_smi_info = {
++      .reg_read = realtek_smi_read,
++      .reg_write = realtek_smi_write,
++};
++
+ /**
+  * realtek_smi_probe() - Probe a platform device for an SMI-connected switch
+  * @pdev: platform_device to probe on.
+  *
+- * This function should be used as the .probe in a platform_driver. It
+- * initializes realtek_priv and read data from the device-tree node. The switch
+- * is hard reset if a method is provided. It checks the switch chip ID and,
+- * finally, a DSA switch is registered.
++ * This function should be used as the .probe in a platform_driver. After
++ * calling the common probe function for both interfaces, it initializes the
++ * values specific for SMI-connected devices. Finally, it calls a common
++ * function to register the DSA switch.
+  *
+  * Context: Can sleep. Takes and releases priv->map_lock.
+  * Return: Returns 0 on success, a negative error on failure.
+  */
+ int realtek_smi_probe(struct platform_device *pdev)
+ {
+-      const struct realtek_variant *var;
+       struct device *dev = &pdev->dev;
+       struct realtek_priv *priv;
+-      struct regmap_config rc;
+-      struct device_node *np;
+       int ret;
+-      var = of_device_get_match_data(dev);
+-      np = dev->of_node;
+-
+-      priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-      priv->chip_data = (void *)priv + sizeof(*priv);
+-
+-      mutex_init(&priv->map_lock);
+-
+-      rc = realtek_smi_regmap_config;
+-      rc.lock_arg = priv;
+-      priv->map = devm_regmap_init(dev, NULL, priv, &rc);
+-      if (IS_ERR(priv->map)) {
+-              ret = PTR_ERR(priv->map);
+-              dev_err(dev, "regmap init failed: %d\n", ret);
+-              return ret;
+-      }
+-
+-      rc = realtek_smi_nolock_regmap_config;
+-      priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
+-      if (IS_ERR(priv->map_nolock)) {
+-              ret = PTR_ERR(priv->map_nolock);
+-              dev_err(dev, "regmap init failed: %d\n", ret);
+-              return ret;
+-      }
+-
+-      /* Link forward and backward */
+-      priv->dev = dev;
+-      priv->variant = var;
+-      priv->ops = var->ops;
+-
+-      priv->setup_interface = realtek_smi_setup_mdio;
+-      priv->write_reg_noack = realtek_smi_write_reg_noack;
+-
+-      dev_set_drvdata(dev, priv);
+-      spin_lock_init(&priv->lock);
+-
+-      /* TODO: if power is software controlled, set up any regulators here */
+-
+-      priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+-      if (IS_ERR(priv->reset)) {
+-              dev_err(dev, "failed to get RESET GPIO\n");
+-              return PTR_ERR(priv->reset);
+-      }
+-      if (priv->reset) {
+-              gpiod_set_value(priv->reset, 1);
+-              dev_dbg(dev, "asserted RESET\n");
+-              msleep(REALTEK_HW_STOP_DELAY);
+-              gpiod_set_value(priv->reset, 0);
+-              msleep(REALTEK_HW_START_DELAY);
+-              dev_dbg(dev, "deasserted RESET\n");
+-      }
++      priv = rtl83xx_probe(dev, &realtek_smi_info);
++      if (IS_ERR(priv))
++              return PTR_ERR(priv);
+       /* Fetch MDIO pins */
+       priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW);
+-      if (IS_ERR(priv->mdc))
++      if (IS_ERR(priv->mdc)) {
++              rtl83xx_remove(priv);
+               return PTR_ERR(priv->mdc);
++      }
++
+       priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW);
+-      if (IS_ERR(priv->mdio))
++      if (IS_ERR(priv->mdio)) {
++              rtl83xx_remove(priv);
+               return PTR_ERR(priv->mdio);
+-
+-      priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
+-
+-      ret = priv->ops->detect(priv);
+-      if (ret) {
+-              dev_err(dev, "unable to detect switch\n");
+-              return ret;
+       }
+-      priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
+-      if (!priv->ds)
+-              return -ENOMEM;
+-
+-      priv->ds->dev = dev;
+-      priv->ds->num_ports = priv->num_ports;
+-      priv->ds->priv = priv;
++      priv->write_reg_noack = realtek_smi_write_reg_noack;
++      priv->setup_interface = realtek_smi_setup_mdio;
++      priv->ds_ops = priv->variant->ds_ops_smi;
+-      priv->ds->ops = var->ds_ops_smi;
+-      ret = dsa_register_switch(priv->ds);
++      ret = rtl83xx_register_switch(priv);
+       if (ret) {
+-              dev_err_probe(dev, ret, "unable to register switch\n");
++              rtl83xx_remove(priv);
+               return ret;
+       }
++
+       return 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
+@@ -523,8 +428,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe,
+  * @pdev: platform_device to be removed.
+  *
+  * This function should be used as the .remove_new in a platform_driver. First
+- * it unregisters the DSA switch and cleans internal data. If a method is
+- * provided, the hard reset is asserted to avoid traffic leakage.
++ * it unregisters the DSA switch and cleans internal data. Finally, it calls
++ * the common remove function.
+  *
+  * Context: Can sleep.
+  * Return: Nothing.
+@@ -536,13 +441,12 @@ void realtek_smi_remove(struct platform_
+       if (!priv)
+               return;
+-      dsa_unregister_switch(priv->ds);
++      rtl83xx_unregister_switch(priv);
++
+       if (priv->slave_mii_bus)
+               of_node_put(priv->slave_mii_bus->dev.of_node);
+-      /* leave the device reset asserted */
+-      if (priv->reset)
+-              gpiod_set_value(priv->reset, 1);
++      rtl83xx_remove(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
+@@ -550,10 +454,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove,
+  * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch
+  * @pdev: platform_device shutting down.
+  *
+- * This function should be used as the .shutdown in a platform_driver. It shuts
+- * down the DSA switch and cleans the platform driver data, to prevent
+- * realtek_smi_remove() from running afterwards, which is possible if the
+- * parent bus implements its own .shutdown() as .remove().
++ * This function should be used as the .shutdown in a platform_driver. It calls
++ * the common shutdown function.
+  *
+  * Context: Can sleep.
+  * Return: Nothing.
+@@ -565,9 +467,7 @@ void realtek_smi_shutdown(struct platfor
+       if (!priv)
+               return;
+-      dsa_switch_shutdown(priv->ds);
+-
+-      platform_set_drvdata(pdev, NULL);
++      rtl83xx_shutdown(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -62,6 +62,7 @@ struct realtek_priv {
+       spinlock_t              lock; /* Locks around command writes */
+       struct dsa_switch       *ds;
++      const struct dsa_switch_ops *ds_ops;
+       struct irq_domain       *irqdomain;
+       bool                    leds_disabled;
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -103,6 +103,7 @@
+ #include "realtek.h"
+ #include "realtek-smi.h"
+ #include "realtek-mdio.h"
++#include "rtl83xx.h"
+ /* Family-specific data and limits */
+ #define RTL8365MB_PHYADDRMAX          7
+@@ -691,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct
+       u32 val;
+       int ret;
+-      mutex_lock(&priv->map_lock);
++      rtl83xx_lock(priv);
+       ret = rtl8365mb_phy_poll_busy(priv);
+       if (ret)
+@@ -724,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct
+       *data = val & 0xFFFF;
+ out:
+-      mutex_unlock(&priv->map_lock);
++      rtl83xx_unlock(priv);
+       return ret;
+ }
+@@ -735,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struc
+       u32 val;
+       int ret;
+-      mutex_lock(&priv->map_lock);
++      rtl83xx_lock(priv);
+       ret = rtl8365mb_phy_poll_busy(priv);
+       if (ret)
+@@ -766,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struc
+               goto out;
+ out:
+-      mutex_unlock(&priv->map_lock);
++      rtl83xx_unlock(priv);
+       return 0;
+ }
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -24,6 +24,7 @@
+ #include "realtek.h"
+ #include "realtek-smi.h"
+ #include "realtek-mdio.h"
++#include "rtl83xx.h"
+ #define RTL8366RB_PORT_NUM_CPU                5
+ #define RTL8366RB_NUM_PORTS           6
+@@ -1634,7 +1635,7 @@ static int rtl8366rb_phy_read(struct rea
+       if (phy > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+-      mutex_lock(&priv->map_lock);
++      rtl83xx_lock(priv);
+       ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                          RTL8366RB_PHY_CTRL_READ);
+@@ -1662,7 +1663,7 @@ static int rtl8366rb_phy_read(struct rea
+               phy, regnum, reg, val);
+ out:
+-      mutex_unlock(&priv->map_lock);
++      rtl83xx_unlock(priv);
+       return ret;
+ }
+@@ -1676,7 +1677,7 @@ static int rtl8366rb_phy_write(struct re
+       if (phy > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+-      mutex_lock(&priv->map_lock);
++      rtl83xx_lock(priv);
+       ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                          RTL8366RB_PHY_CTRL_WRITE);
+@@ -1693,7 +1694,7 @@ static int rtl8366rb_phy_write(struct re
+               goto out;
+ out:
+-      mutex_unlock(&priv->map_lock);
++      rtl83xx_unlock(priv);
+       return ret;
+ }
+--- /dev/null
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -0,0 +1,236 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/of_device.h> /* krnl 6.1 only */
++
++#include "realtek.h"
++#include "rtl83xx.h"
++
++/**
++ * rtl83xx_lock() - Locks the mutex used by regmaps
++ * @ctx: realtek_priv pointer
++ *
++ * This function is passed to regmap to be used as the lock function.
++ * It is also used externally to block regmap before executing multiple
++ * operations that must happen in sequence (which will use
++ * realtek_priv.map_nolock instead).
++ *
++ * Context: Can sleep. Holds priv->map_lock lock.
++ * Return: nothing
++ */
++void rtl83xx_lock(void *ctx)
++{
++      struct realtek_priv *priv = ctx;
++
++      mutex_lock(&priv->map_lock);
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA);
++
++/**
++ * rtl83xx_unlock() - Unlocks the mutex used by regmaps
++ * @ctx: realtek_priv pointer
++ *
++ * This function unlocks the lock acquired by rtl83xx_lock.
++ *
++ * Context: Releases priv->map_lock lock.
++ * Return: nothing
++ */
++void rtl83xx_unlock(void *ctx)
++{
++      struct realtek_priv *priv = ctx;
++
++      mutex_unlock(&priv->map_lock);
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA);
++
++/**
++ * rtl83xx_probe() - probe a Realtek switch
++ * @dev: the device being probed
++ * @interface_info: specific management interface info.
++ *
++ * This function initializes realtek_priv and reads data from the device tree
++ * node. The switch is hard resetted if a method is provided.
++ *
++ * Context: Can sleep.
++ * Return: Pointer to the realtek_priv or ERR_PTR() in case of failure.
++ *
++ * The realtek_priv pointer does not need to be freed as it is controlled by
++ * devres.
++ */
++struct realtek_priv *
++rtl83xx_probe(struct device *dev,
++            const struct realtek_interface_info *interface_info)
++{
++      const struct realtek_variant *var;
++      struct realtek_priv *priv;
++      struct regmap_config rc = {
++              .reg_bits = 10, /* A4..A0 R4..R0 */
++              .val_bits = 16,
++              .reg_stride = 1,
++              .max_register = 0xffff,
++              .reg_format_endian = REGMAP_ENDIAN_BIG,
++              .reg_read = interface_info->reg_read,
++              .reg_write = interface_info->reg_write,
++              .cache_type = REGCACHE_NONE,
++              .lock = rtl83xx_lock,
++              .unlock = rtl83xx_unlock,
++      };
++      int ret;
++
++      var = of_device_get_match_data(dev);
++      if (!var)
++              return ERR_PTR(-EINVAL);
++
++      priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz),
++                          GFP_KERNEL);
++      if (!priv)
++              return ERR_PTR(-ENOMEM);
++
++      mutex_init(&priv->map_lock);
++
++      rc.lock_arg = priv;
++      priv->map = devm_regmap_init(dev, NULL, priv, &rc);
++      if (IS_ERR(priv->map)) {
++              ret = PTR_ERR(priv->map);
++              dev_err(dev, "regmap init failed: %d\n", ret);
++              return ERR_PTR(ret);
++      }
++
++      rc.disable_locking = true;
++      priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
++      if (IS_ERR(priv->map_nolock)) {
++              ret = PTR_ERR(priv->map_nolock);
++              dev_err(dev, "regmap init failed: %d\n", ret);
++              return ERR_PTR(ret);
++      }
++
++      /* Link forward and backward */
++      priv->dev = dev;
++      priv->variant = var;
++      priv->ops = var->ops;
++      priv->chip_data = (void *)priv + sizeof(*priv);
++
++      spin_lock_init(&priv->lock);
++
++      priv->leds_disabled = of_property_read_bool(dev->of_node,
++                                                  "realtek,disable-leds");
++
++      /* TODO: if power is software controlled, set up any regulators here */
++      priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
++      if (IS_ERR(priv->reset)) {
++              dev_err(dev, "failed to get RESET GPIO\n");
++              return ERR_CAST(priv->reset);
++      }
++
++      dev_set_drvdata(dev, priv);
++
++      if (priv->reset) {
++              gpiod_set_value(priv->reset, 1);
++              dev_dbg(dev, "asserted RESET\n");
++              msleep(REALTEK_HW_STOP_DELAY);
++              gpiod_set_value(priv->reset, 0);
++              msleep(REALTEK_HW_START_DELAY);
++              dev_dbg(dev, "deasserted RESET\n");
++      }
++
++      return priv;
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA);
++
++/**
++ * rtl83xx_register_switch() - detects and register a switch
++ * @priv: realtek_priv pointer
++ *
++ * This function first checks the switch chip ID and register a DSA
++ * switch.
++ *
++ * Context: Can sleep. Takes and releases priv->map_lock.
++ * Return: 0 on success, negative value for failure.
++ */
++int rtl83xx_register_switch(struct realtek_priv *priv)
++{
++      struct dsa_switch *ds;
++      int ret;
++
++      ret = priv->ops->detect(priv);
++      if (ret) {
++              dev_err_probe(priv->dev, ret, "unable to detect switch\n");
++              return ret;
++      }
++
++      ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL);
++      if (!ds)
++              return -ENOMEM;
++
++      ds->priv = priv;
++      ds->dev = priv->dev;
++      ds->ops = priv->ds_ops;
++      ds->num_ports = priv->num_ports;
++      priv->ds = ds;
++
++      ret = dsa_register_switch(ds);
++      if (ret) {
++              dev_err_probe(priv->dev, ret, "unable to register switch\n");
++              return ret;
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA);
++
++/**
++ * rtl83xx_unregister_switch() - unregister a switch
++ * @priv: realtek_priv pointer
++ *
++ * This function unregister a DSA switch.
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void rtl83xx_unregister_switch(struct realtek_priv *priv)
++{
++      dsa_unregister_switch(priv->ds);
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA);
++
++/**
++ * rtl83xx_shutdown() - shutdown a switch
++ * @priv: realtek_priv pointer
++ *
++ * This function shuts down the DSA switch and cleans the platform driver data,
++ * to prevent realtek_{smi,mdio}_remove() from running afterwards, which is
++ * possible if the parent bus implements its own .shutdown() as .remove().
++ *
++ * Context: Can sleep.
++ * Return: Nothing.
++ */
++void rtl83xx_shutdown(struct realtek_priv *priv)
++{
++      dsa_switch_shutdown(priv->ds);
++
++      dev_set_drvdata(priv->dev, NULL);
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA);
++
++/**
++ * rtl83xx_remove() - Cleanup a realtek switch driver
++ * @priv: realtek_priv pointer
++ *
++ * If a method is provided, this function asserts the hard reset of the switch
++ * in order to avoid leaking traffic when the driver is gone.
++ *
++ * Context: Might sleep if priv->gdev->chip->can_sleep.
++ * Return: nothing
++ */
++void rtl83xx_remove(struct realtek_priv *priv)
++{
++      /* leave the device reset asserted */
++      if (priv->reset)
++              gpiod_set_value(priv->reset, 1);
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
++
++MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
++MODULE_DESCRIPTION("Realtek DSA switches common module");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/net/dsa/realtek/rtl83xx.h
+@@ -0,0 +1,21 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++
++#ifndef _RTL83XX_H
++#define _RTL83XX_H
++
++struct realtek_interface_info {
++      int (*reg_read)(void *ctx, u32 reg, u32 *val);
++      int (*reg_write)(void *ctx, u32 reg, u32 val);
++};
++
++void rtl83xx_lock(void *ctx);
++void rtl83xx_unlock(void *ctx);
++struct realtek_priv *
++rtl83xx_probe(struct device *dev,
++            const struct realtek_interface_info *interface_info);
++int rtl83xx_register_switch(struct realtek_priv *priv);
++void rtl83xx_unregister_switch(struct realtek_priv *priv);
++void rtl83xx_shutdown(struct realtek_priv *priv);
++void rtl83xx_remove(struct realtek_priv *priv);
++
++#endif /* _RTL83XX_H */
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch b/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch
new file mode 100644 (file)
index 0000000..2754b62
--- /dev/null
@@ -0,0 +1,93 @@
+From 98b75c1c149c653ad11a440636213eb070325158 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:42 -0300
+Subject: net: dsa: realtek: merge rtl83xx and interface
+ modules into realtek_dsa
+
+Since rtl83xx and realtek-{smi,mdio} are always loaded together,
+we can optimize resource usage by consolidating them into a single
+module.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/Kconfig        |  4 ++--
+ drivers/net/dsa/realtek/Makefile       | 11 +++++++++--
+ drivers/net/dsa/realtek/realtek-mdio.c |  5 -----
+ drivers/net/dsa/realtek/realtek-smi.c  |  5 -----
+ drivers/net/dsa/realtek/rtl83xx.c      |  1 +
+ 5 files changed, 12 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/dsa/realtek/Kconfig
++++ b/drivers/net/dsa/realtek/Kconfig
+@@ -16,14 +16,14 @@ menuconfig NET_DSA_REALTEK
+ if NET_DSA_REALTEK
+ config NET_DSA_REALTEK_MDIO
+-      tristate "Realtek MDIO interface support"
++      bool "Realtek MDIO interface support"
+       depends on OF
+       help
+         Select to enable support for registering switches configured
+         through MDIO.
+ config NET_DSA_REALTEK_SMI
+-      tristate "Realtek SMI interface support"
++      bool "Realtek SMI interface support"
+       depends on OF
+       help
+         Select to enable support for registering switches connected
+--- a/drivers/net/dsa/realtek/Makefile
++++ b/drivers/net/dsa/realtek/Makefile
+@@ -1,8 +1,15 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_NET_DSA_REALTEK)         += realtek_dsa.o
+ realtek_dsa-objs                      := rtl83xx.o
+-obj-$(CONFIG_NET_DSA_REALTEK_MDIO)    += realtek-mdio.o
+-obj-$(CONFIG_NET_DSA_REALTEK_SMI)     += realtek-smi.o
++
++ifdef CONFIG_NET_DSA_REALTEK_MDIO
++realtek_dsa-objs += realtek-mdio.o
++endif
++
++ifdef CONFIG_NET_DSA_REALTEK_SMI
++realtek_dsa-objs += realtek-smi.o
++endif
++
+ obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o
+ rtl8366-objs                          := rtl8366-core.o rtl8366rb.o
+ obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -186,8 +186,3 @@ void realtek_mdio_shutdown(struct mdio_d
+       rtl83xx_shutdown(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
+-
+-MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
+-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
+-MODULE_LICENSE("GPL");
+-MODULE_IMPORT_NS(REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -470,8 +470,3 @@ void realtek_smi_shutdown(struct platfor
+       rtl83xx_shutdown(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
+-
+-MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
+-MODULE_LICENSE("GPL");
+-MODULE_IMPORT_NS(REALTEK_DSA);
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -232,5 +232,6 @@ void rtl83xx_remove(struct realtek_priv
+ EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
+ MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
++MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Realtek DSA switches common module");
+ MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch b/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch
new file mode 100644 (file)
index 0000000..423e750
--- /dev/null
@@ -0,0 +1,34 @@
+From 8685c98d45c54346caf005de69988e13c731c533 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:43 -0300
+Subject: net: dsa: realtek: get internal MDIO node by name
+
+The binding docs requires for SMI-connected devices that the switch
+must have a child node named "mdio" and with a compatible string of
+"realtek,smi-mdio". Meanwile, for MDIO-connected switches, the binding
+docs only requires a child node named "mdio".
+
+This patch changes the driver to use the common denominator for both
+interfaces, looking for the MDIO node by name, ignoring the compatible
+string.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-smi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -333,7 +333,7 @@ static int realtek_smi_setup_mdio(struct
+       struct device_node *mdio_np;
+       int ret;
+-      mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio");
++      mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
+       if (!mdio_np) {
+               dev_err(priv->dev, "no MDIO bus node\n");
+               return -ENODEV;
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch b/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch
new file mode 100644 (file)
index 0000000..1958c7f
--- /dev/null
@@ -0,0 +1,83 @@
+From 68c66d8d8a19088967a0ab6bb98cb5ecc80ca0be Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:44 -0300
+Subject: net: dsa: realtek: clean slave_mii_bus setup
+
+Remove the line assigning dev.of_node in mdio_bus as subsequent
+of_mdiobus_register will always overwrite it.
+
+As discussed in [1], allow the DSA core to be simplified, by not
+assigning ds->slave_mii_bus when the MDIO bus is described in OF, as it
+is unnecessary.
+
+Since commit 3b73a7b8ec38 ("net: mdio_bus: add refcounting for fwnodes
+to mdiobus"), we can put the "mdio" node just after the MDIO bus
+registration.
+
+[1] https://lkml.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/T/#u
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-smi.c | 13 +++----------
+ 1 file changed, 3 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -331,7 +331,7 @@ static int realtek_smi_setup_mdio(struct
+ {
+       struct realtek_priv *priv =  ds->priv;
+       struct device_node *mdio_np;
+-      int ret;
++      int ret = 0;
+       mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
+       if (!mdio_np) {
+@@ -344,15 +344,14 @@ static int realtek_smi_setup_mdio(struct
+               ret = -ENOMEM;
+               goto err_put_node;
+       }
++
+       priv->slave_mii_bus->priv = priv;
+       priv->slave_mii_bus->name = "SMI slave MII";
+       priv->slave_mii_bus->read = realtek_smi_mdio_read;
+       priv->slave_mii_bus->write = realtek_smi_mdio_write;
+       snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
+                ds->index);
+-      priv->slave_mii_bus->dev.of_node = mdio_np;
+       priv->slave_mii_bus->parent = priv->dev;
+-      ds->slave_mii_bus = priv->slave_mii_bus;
+       ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np);
+       if (ret) {
+@@ -361,8 +360,6 @@ static int realtek_smi_setup_mdio(struct
+               goto err_put_node;
+       }
+-      return 0;
+-
+ err_put_node:
+       of_node_put(mdio_np);
+@@ -428,8 +425,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe,
+  * @pdev: platform_device to be removed.
+  *
+  * This function should be used as the .remove_new in a platform_driver. First
+- * it unregisters the DSA switch and cleans internal data. Finally, it calls
+- * the common remove function.
++ * it unregisters the DSA switch and then it calls the common remove function.
+  *
+  * Context: Can sleep.
+  * Return: Nothing.
+@@ -443,9 +439,6 @@ void realtek_smi_remove(struct platform_
+       rtl83xx_unregister_switch(priv);
+-      if (priv->slave_mii_bus)
+-              of_node_put(priv->slave_mii_bus->dev.of_node);
+-
+       rtl83xx_remove(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch b/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch
new file mode 100644 (file)
index 0000000..50e96d7
--- /dev/null
@@ -0,0 +1,198 @@
+From b4bd77971f3c290c4694ed710cc6967593b10bc2 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:45 -0300
+Subject: net: dsa: realtek: migrate slave_mii_bus setup to
+ realtek_dsa
+
+In the user MDIO driver, despite numerous references to SMI, including
+its compatible string, there's nothing inherently specific about the SMI
+interface in the user MDIO bus. Consequently, the code has been migrated
+to the rtl83xx module. All references to SMI have been eliminated.
+
+The MDIO bus id was changed from Realtek-<switch id> to the switch
+devname suffixed with :slave_mii, giving more information about the bus
+it is referencing.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-smi.c | 57 +---------------------
+ drivers/net/dsa/realtek/rtl83xx.c     | 68 +++++++++++++++++++++++++++
+ drivers/net/dsa/realtek/rtl83xx.h     |  1 +
+ 3 files changed, 70 insertions(+), 56 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -31,7 +31,6 @@
+ #include <linux/spinlock.h>
+ #include <linux/skbuff.h>
+ #include <linux/of.h>
+-#include <linux/of_mdio.h>
+ #include <linux/delay.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/platform_device.h>
+@@ -312,60 +311,6 @@ static int realtek_smi_read(void *ctx, u
+       return realtek_smi_read_reg(priv, reg, val);
+ }
+-static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
+-{
+-      struct realtek_priv *priv = bus->priv;
+-
+-      return priv->ops->phy_read(priv, addr, regnum);
+-}
+-
+-static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum,
+-                                u16 val)
+-{
+-      struct realtek_priv *priv = bus->priv;
+-
+-      return priv->ops->phy_write(priv, addr, regnum, val);
+-}
+-
+-static int realtek_smi_setup_mdio(struct dsa_switch *ds)
+-{
+-      struct realtek_priv *priv =  ds->priv;
+-      struct device_node *mdio_np;
+-      int ret = 0;
+-
+-      mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
+-      if (!mdio_np) {
+-              dev_err(priv->dev, "no MDIO bus node\n");
+-              return -ENODEV;
+-      }
+-
+-      priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev);
+-      if (!priv->slave_mii_bus) {
+-              ret = -ENOMEM;
+-              goto err_put_node;
+-      }
+-
+-      priv->slave_mii_bus->priv = priv;
+-      priv->slave_mii_bus->name = "SMI slave MII";
+-      priv->slave_mii_bus->read = realtek_smi_mdio_read;
+-      priv->slave_mii_bus->write = realtek_smi_mdio_write;
+-      snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
+-               ds->index);
+-      priv->slave_mii_bus->parent = priv->dev;
+-
+-      ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np);
+-      if (ret) {
+-              dev_err(priv->dev, "unable to register MDIO bus %s\n",
+-                      priv->slave_mii_bus->id);
+-              goto err_put_node;
+-      }
+-
+-err_put_node:
+-      of_node_put(mdio_np);
+-
+-      return ret;
+-}
+-
+ static const struct realtek_interface_info realtek_smi_info = {
+       .reg_read = realtek_smi_read,
+       .reg_write = realtek_smi_write,
+@@ -407,7 +352,7 @@ int realtek_smi_probe(struct platform_de
+       }
+       priv->write_reg_noack = realtek_smi_write_reg_noack;
+-      priv->setup_interface = realtek_smi_setup_mdio;
++      priv->setup_interface = rtl83xx_setup_user_mdio;
+       priv->ds_ops = priv->variant->ds_ops_smi;
+       ret = rtl83xx_register_switch(priv);
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -3,6 +3,7 @@
+ #include <linux/module.h>
+ #include <linux/regmap.h>
+ #include <linux/of_device.h> /* krnl 6.1 only */
++#include <linux/of_mdio.h>
+ #include "realtek.h"
+ #include "rtl83xx.h"
+@@ -44,6 +45,73 @@ void rtl83xx_unlock(void *ctx)
+ }
+ EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA);
++static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum)
++{
++      struct realtek_priv *priv = bus->priv;
++
++      return priv->ops->phy_read(priv, addr, regnum);
++}
++
++static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum,
++                                 u16 val)
++{
++      struct realtek_priv *priv = bus->priv;
++
++      return priv->ops->phy_write(priv, addr, regnum, val);
++}
++
++/**
++ * rtl83xx_setup_user_mdio() - register the user mii bus driver
++ * @ds: DSA switch associated with this slave_mii_bus
++ *
++ * Registers the MDIO bus for built-in Ethernet PHYs, and associates it with
++ * the mandatory 'mdio' child OF node of the switch.
++ *
++ * Context: Can sleep.
++ * Return: 0 on success, negative value for failure.
++ */
++int rtl83xx_setup_user_mdio(struct dsa_switch *ds)
++{
++      struct realtek_priv *priv = ds->priv;
++      struct device_node *mdio_np;
++      struct mii_bus *bus;
++      int ret = 0;
++
++      mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
++      if (!mdio_np) {
++              dev_err(priv->dev, "no MDIO bus node\n");
++              return -ENODEV;
++      }
++
++      bus = devm_mdiobus_alloc(priv->dev);
++      if (!bus) {
++              ret = -ENOMEM;
++              goto err_put_node;
++      }
++
++      bus->priv = priv;
++      bus->name = "Realtek user MII";
++      bus->read = rtl83xx_user_mdio_read;
++      bus->write = rtl83xx_user_mdio_write;
++      snprintf(bus->id, MII_BUS_ID_SIZE, "%s:slave_mii", dev_name(priv->dev));
++      bus->parent = priv->dev;
++
++      ret = devm_of_mdiobus_register(priv->dev, bus, mdio_np);
++      if (ret) {
++              dev_err(priv->dev, "unable to register MDIO bus %s\n",
++                      bus->id);
++              goto err_put_node;
++      }
++
++      priv->slave_mii_bus = bus;
++
++err_put_node:
++      of_node_put(mdio_np);
++
++      return ret;
++}
++EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA);
++
+ /**
+  * rtl83xx_probe() - probe a Realtek switch
+  * @dev: the device being probed
+--- a/drivers/net/dsa/realtek/rtl83xx.h
++++ b/drivers/net/dsa/realtek/rtl83xx.h
+@@ -10,6 +10,7 @@ struct realtek_interface_info {
+ void rtl83xx_lock(void *ctx);
+ void rtl83xx_unlock(void *ctx);
++int rtl83xx_setup_user_mdio(struct dsa_switch *ds);
+ struct realtek_priv *
+ rtl83xx_probe(struct device *dev,
+             const struct realtek_interface_info *interface_info);
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch b/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch
new file mode 100644 (file)
index 0000000..b50a335
--- /dev/null
@@ -0,0 +1,261 @@
+From bba140a566ed075304c49c52ab32c0016cab624a Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:46 -0300
+Subject: net: dsa: realtek: use the same mii bus driver for
+ both interfaces
+
+The realtek-mdio will now use this driver instead of the generic DSA
+driver ("dsa user smi"), which should not be used with OF[1].
+
+With a single ds_ops for both interfaces, the ds_ops in realtek_priv is
+no longer necessary. Now, the realtek_variant.ds_ops can be used
+directly.
+
+The realtek_priv.setup_interface() has been removed as we can directly
+call the new common function.
+
+[1] https://lkml.kernel.org/netdev/20220630200423.tieprdu5fpabflj7@bang-olufsen.dk/T/
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek-mdio.c |  1 -
+ drivers/net/dsa/realtek/realtek-smi.c  |  2 -
+ drivers/net/dsa/realtek/realtek.h      |  5 +--
+ drivers/net/dsa/realtek/rtl8365mb.c    | 49 +++---------------------
+ drivers/net/dsa/realtek/rtl8366rb.c    | 52 +++-----------------------
+ drivers/net/dsa/realtek/rtl83xx.c      |  2 +-
+ 6 files changed, 14 insertions(+), 97 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek-mdio.c
++++ b/drivers/net/dsa/realtek/realtek-mdio.c
+@@ -131,7 +131,6 @@ int realtek_mdio_probe(struct mdio_devic
+       priv->bus = mdiodev->bus;
+       priv->mdio_addr = mdiodev->addr;
+       priv->write_reg_noack = realtek_mdio_write;
+-      priv->ds_ops = priv->variant->ds_ops_mdio;
+       ret = rtl83xx_register_switch(priv);
+       if (ret) {
+--- a/drivers/net/dsa/realtek/realtek-smi.c
++++ b/drivers/net/dsa/realtek/realtek-smi.c
+@@ -352,8 +352,6 @@ int realtek_smi_probe(struct platform_de
+       }
+       priv->write_reg_noack = realtek_smi_write_reg_noack;
+-      priv->setup_interface = rtl83xx_setup_user_mdio;
+-      priv->ds_ops = priv->variant->ds_ops_smi;
+       ret = rtl83xx_register_switch(priv);
+       if (ret) {
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -62,7 +62,6 @@ struct realtek_priv {
+       spinlock_t              lock; /* Locks around command writes */
+       struct dsa_switch       *ds;
+-      const struct dsa_switch_ops *ds_ops;
+       struct irq_domain       *irqdomain;
+       bool                    leds_disabled;
+@@ -73,7 +72,6 @@ struct realtek_priv {
+       struct rtl8366_mib_counter *mib_counters;
+       const struct realtek_ops *ops;
+-      int                     (*setup_interface)(struct dsa_switch *ds);
+       int                     (*write_reg_noack)(void *ctx, u32 addr, u32 data);
+       int                     vlan_enabled;
+@@ -115,8 +113,7 @@ struct realtek_ops {
+ };
+ struct realtek_variant {
+-      const struct dsa_switch_ops *ds_ops_smi;
+-      const struct dsa_switch_ops *ds_ops_mdio;
++      const struct dsa_switch_ops *ds_ops;
+       const struct realtek_ops *ops;
+       unsigned int clk_delay;
+       u8 cmd_read;
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -828,17 +828,6 @@ static int rtl8365mb_phy_write(struct re
+       return 0;
+ }
+-static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum)
+-{
+-      return rtl8365mb_phy_read(ds->priv, phy, regnum);
+-}
+-
+-static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum,
+-                                 u16 val)
+-{
+-      return rtl8365mb_phy_write(ds->priv, phy, regnum, val);
+-}
+-
+ static const struct rtl8365mb_extint *
+ rtl8365mb_get_port_extint(struct realtek_priv *priv, int port)
+ {
+@@ -2018,12 +2007,10 @@ static int rtl8365mb_setup(struct dsa_sw
+       if (ret)
+               goto out_teardown_irq;
+-      if (priv->setup_interface) {
+-              ret = priv->setup_interface(ds);
+-              if (ret) {
+-                      dev_err(priv->dev, "could not set up MDIO bus\n");
+-                      goto out_teardown_irq;
+-              }
++      ret = rtl83xx_setup_user_mdio(ds);
++      if (ret) {
++              dev_err(priv->dev, "could not set up MDIO bus\n");
++              goto out_teardown_irq;
+       }
+       /* Start statistics counter polling */
+@@ -2117,28 +2104,7 @@ static int rtl8365mb_detect(struct realt
+       return 0;
+ }
+-static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = {
+-      .get_tag_protocol = rtl8365mb_get_tag_protocol,
+-      .change_tag_protocol = rtl8365mb_change_tag_protocol,
+-      .setup = rtl8365mb_setup,
+-      .teardown = rtl8365mb_teardown,
+-      .phylink_get_caps = rtl8365mb_phylink_get_caps,
+-      .phylink_mac_config = rtl8365mb_phylink_mac_config,
+-      .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down,
+-      .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up,
+-      .port_stp_state_set = rtl8365mb_port_stp_state_set,
+-      .get_strings = rtl8365mb_get_strings,
+-      .get_ethtool_stats = rtl8365mb_get_ethtool_stats,
+-      .get_sset_count = rtl8365mb_get_sset_count,
+-      .get_eth_phy_stats = rtl8365mb_get_phy_stats,
+-      .get_eth_mac_stats = rtl8365mb_get_mac_stats,
+-      .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats,
+-      .get_stats64 = rtl8365mb_get_stats64,
+-      .port_change_mtu = rtl8365mb_port_change_mtu,
+-      .port_max_mtu = rtl8365mb_port_max_mtu,
+-};
+-
+-static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = {
++static const struct dsa_switch_ops rtl8365mb_switch_ops = {
+       .get_tag_protocol = rtl8365mb_get_tag_protocol,
+       .change_tag_protocol = rtl8365mb_change_tag_protocol,
+       .setup = rtl8365mb_setup,
+@@ -2147,8 +2113,6 @@ static const struct dsa_switch_ops rtl83
+       .phylink_mac_config = rtl8365mb_phylink_mac_config,
+       .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down,
+       .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up,
+-      .phy_read = rtl8365mb_dsa_phy_read,
+-      .phy_write = rtl8365mb_dsa_phy_write,
+       .port_stp_state_set = rtl8365mb_port_stp_state_set,
+       .get_strings = rtl8365mb_get_strings,
+       .get_ethtool_stats = rtl8365mb_get_ethtool_stats,
+@@ -2168,8 +2132,7 @@ static const struct realtek_ops rtl8365m
+ };
+ const struct realtek_variant rtl8365mb_variant = {
+-      .ds_ops_smi = &rtl8365mb_switch_ops_smi,
+-      .ds_ops_mdio = &rtl8365mb_switch_ops_mdio,
++      .ds_ops = &rtl8365mb_switch_ops,
+       .ops = &rtl8365mb_ops,
+       .clk_delay = 10,
+       .cmd_read = 0xb9,
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -1035,12 +1035,10 @@ static int rtl8366rb_setup(struct dsa_sw
+       if (ret)
+               dev_info(priv->dev, "no interrupt support\n");
+-      if (priv->setup_interface) {
+-              ret = priv->setup_interface(ds);
+-              if (ret) {
+-                      dev_err(priv->dev, "could not set up MDIO bus\n");
+-                      return -ENODEV;
+-              }
++      ret = rtl83xx_setup_user_mdio(ds);
++      if (ret) {
++              dev_err(priv->dev, "could not set up MDIO bus\n");
++              return -ENODEV;
+       }
+       return 0;
+@@ -1699,17 +1697,6 @@ out:
+       return ret;
+ }
+-static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum)
+-{
+-      return rtl8366rb_phy_read(ds->priv, phy, regnum);
+-}
+-
+-static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum,
+-                                 u16 val)
+-{
+-      return rtl8366rb_phy_write(ds->priv, phy, regnum, val);
+-}
+-
+ static int rtl8366rb_reset_chip(struct realtek_priv *priv)
+ {
+       int timeout = 10;
+@@ -1775,35 +1762,9 @@ static int rtl8366rb_detect(struct realt
+       return 0;
+ }
+-static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = {
+-      .get_tag_protocol = rtl8366_get_tag_protocol,
+-      .setup = rtl8366rb_setup,
+-      .phylink_get_caps = rtl8366rb_phylink_get_caps,
+-      .phylink_mac_link_up = rtl8366rb_mac_link_up,
+-      .phylink_mac_link_down = rtl8366rb_mac_link_down,
+-      .get_strings = rtl8366_get_strings,
+-      .get_ethtool_stats = rtl8366_get_ethtool_stats,
+-      .get_sset_count = rtl8366_get_sset_count,
+-      .port_bridge_join = rtl8366rb_port_bridge_join,
+-      .port_bridge_leave = rtl8366rb_port_bridge_leave,
+-      .port_vlan_filtering = rtl8366rb_vlan_filtering,
+-      .port_vlan_add = rtl8366_vlan_add,
+-      .port_vlan_del = rtl8366_vlan_del,
+-      .port_enable = rtl8366rb_port_enable,
+-      .port_disable = rtl8366rb_port_disable,
+-      .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
+-      .port_bridge_flags = rtl8366rb_port_bridge_flags,
+-      .port_stp_state_set = rtl8366rb_port_stp_state_set,
+-      .port_fast_age = rtl8366rb_port_fast_age,
+-      .port_change_mtu = rtl8366rb_change_mtu,
+-      .port_max_mtu = rtl8366rb_max_mtu,
+-};
+-
+-static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = {
++static const struct dsa_switch_ops rtl8366rb_switch_ops = {
+       .get_tag_protocol = rtl8366_get_tag_protocol,
+       .setup = rtl8366rb_setup,
+-      .phy_read = rtl8366rb_dsa_phy_read,
+-      .phy_write = rtl8366rb_dsa_phy_write,
+       .phylink_get_caps = rtl8366rb_phylink_get_caps,
+       .phylink_mac_link_up = rtl8366rb_mac_link_up,
+       .phylink_mac_link_down = rtl8366rb_mac_link_down,
+@@ -1842,8 +1803,7 @@ static const struct realtek_ops rtl8366r
+ };
+ const struct realtek_variant rtl8366rb_variant = {
+-      .ds_ops_smi = &rtl8366rb_switch_ops_smi,
+-      .ds_ops_mdio = &rtl8366rb_switch_ops_mdio,
++      .ds_ops = &rtl8366rb_switch_ops,
+       .ops = &rtl8366rb_ops,
+       .clk_delay = 10,
+       .cmd_read = 0xa9,
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -233,7 +233,7 @@ int rtl83xx_register_switch(struct realt
+       ds->priv = priv;
+       ds->dev = priv->dev;
+-      ds->ops = priv->ds_ops;
++      ds->ops = priv->variant->ds_ops;
+       ds->num_ports = priv->num_ports;
+       priv->ds = ds;
diff --git a/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch b/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch
new file mode 100644 (file)
index 0000000..4cb55f2
--- /dev/null
@@ -0,0 +1,180 @@
+From 9fc469b2943d9b1ff2a7800f823e7cd7a5cac0ca Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Fri, 9 Feb 2024 02:03:47 -0300
+Subject: net: dsa: realtek: embed dsa_switch into realtek_priv
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Embed dsa_switch within realtek_priv to eliminate the need for a second
+memory allocation.
+
+Suggested-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek.h   |  2 +-
+ drivers/net/dsa/realtek/rtl8365mb.c | 15 +++++++++------
+ drivers/net/dsa/realtek/rtl8366rb.c |  3 ++-
+ drivers/net/dsa/realtek/rtl83xx.c   | 15 +++++++--------
+ 4 files changed, 19 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -61,7 +61,7 @@ struct realtek_priv {
+       const struct realtek_variant *variant;
+       spinlock_t              lock; /* Locks around command writes */
+-      struct dsa_switch       *ds;
++      struct dsa_switch       ds;
+       struct irq_domain       *irqdomain;
+       bool                    leds_disabled;
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -870,6 +870,7 @@ static int rtl8365mb_ext_config_rgmii(st
+ {
+       const struct rtl8365mb_extint *extint =
+               rtl8365mb_get_port_extint(priv, port);
++      struct dsa_switch *ds = &priv->ds;
+       struct device_node *dn;
+       struct dsa_port *dp;
+       int tx_delay = 0;
+@@ -880,7 +881,7 @@ static int rtl8365mb_ext_config_rgmii(st
+       if (!extint)
+               return -ENODEV;
+-      dp = dsa_to_port(priv->ds, port);
++      dp = dsa_to_port(ds, port);
+       dn = dp->dn;
+       /* Set the RGMII TX/RX delay
+@@ -1534,6 +1535,7 @@ static void rtl8365mb_get_stats64(struct
+ static void rtl8365mb_stats_setup(struct realtek_priv *priv)
+ {
+       struct rtl8365mb *mb = priv->chip_data;
++      struct dsa_switch *ds = &priv->ds;
+       int i;
+       /* Per-chip global mutex to protect MIB counter access, since doing
+@@ -1544,7 +1546,7 @@ static void rtl8365mb_stats_setup(struct
+       for (i = 0; i < priv->num_ports; i++) {
+               struct rtl8365mb_port *p = &mb->ports[i];
+-              if (dsa_is_unused_port(priv->ds, i))
++              if (dsa_is_unused_port(ds, i))
+                       continue;
+               /* Per-port spinlock to protect the stats64 data */
+@@ -1560,12 +1562,13 @@ static void rtl8365mb_stats_setup(struct
+ static void rtl8365mb_stats_teardown(struct realtek_priv *priv)
+ {
+       struct rtl8365mb *mb = priv->chip_data;
++      struct dsa_switch *ds = &priv->ds;
+       int i;
+       for (i = 0; i < priv->num_ports; i++) {
+               struct rtl8365mb_port *p = &mb->ports[i];
+-              if (dsa_is_unused_port(priv->ds, i))
++              if (dsa_is_unused_port(ds, i))
+                       continue;
+               cancel_delayed_work_sync(&p->mib_work);
+@@ -1964,7 +1967,7 @@ static int rtl8365mb_setup(struct dsa_sw
+               dev_info(priv->dev, "no interrupt support\n");
+       /* Configure CPU tagging */
+-      dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) {
++      dsa_switch_for_each_cpu_port(cpu_dp, ds) {
+               cpu->mask |= BIT(cpu_dp->index);
+               if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
+@@ -1979,7 +1982,7 @@ static int rtl8365mb_setup(struct dsa_sw
+       for (i = 0; i < priv->num_ports; i++) {
+               struct rtl8365mb_port *p = &mb->ports[i];
+-              if (dsa_is_unused_port(priv->ds, i))
++              if (dsa_is_unused_port(ds, i))
+                       continue;
+               /* Forward only to the CPU */
+@@ -1996,7 +1999,7 @@ static int rtl8365mb_setup(struct dsa_sw
+                * ports will still forward frames to the CPU despite being
+                * administratively down by default.
+                */
+-              rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED);
++              rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+               /* Set up per-port private data */
+               p->priv = priv;
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -1565,6 +1565,7 @@ static int rtl8366rb_get_mc_index(struct
+ static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index)
+ {
++      struct dsa_switch *ds = &priv->ds;
+       struct rtl8366rb *rb;
+       bool pvid_enabled;
+       int ret;
+@@ -1589,7 +1590,7 @@ static int rtl8366rb_set_mc_index(struct
+        * not drop any untagged or C-tagged frames. Make sure to update the
+        * filtering setting.
+        */
+-      if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port)))
++      if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port)))
+               ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled);
+       return ret;
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -218,7 +218,7 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REAL
+  */
+ int rtl83xx_register_switch(struct realtek_priv *priv)
+ {
+-      struct dsa_switch *ds;
++      struct dsa_switch *ds = &priv->ds;
+       int ret;
+       ret = priv->ops->detect(priv);
+@@ -227,15 +227,10 @@ int rtl83xx_register_switch(struct realt
+               return ret;
+       }
+-      ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL);
+-      if (!ds)
+-              return -ENOMEM;
+-
+       ds->priv = priv;
+       ds->dev = priv->dev;
+       ds->ops = priv->variant->ds_ops;
+       ds->num_ports = priv->num_ports;
+-      priv->ds = ds;
+       ret = dsa_register_switch(ds);
+       if (ret) {
+@@ -258,7 +253,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_register_sw
+  */
+ void rtl83xx_unregister_switch(struct realtek_priv *priv)
+ {
+-      dsa_unregister_switch(priv->ds);
++      struct dsa_switch *ds = &priv->ds;
++
++      dsa_unregister_switch(ds);
+ }
+ EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA);
+@@ -275,7 +272,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_
+  */
+ void rtl83xx_shutdown(struct realtek_priv *priv)
+ {
+-      dsa_switch_shutdown(priv->ds);
++      struct dsa_switch *ds = &priv->ds;
++
++      dsa_switch_shutdown(ds);
+       dev_set_drvdata(priv->dev, NULL);
+ }
diff --git a/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch b/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch
new file mode 100644 (file)
index 0000000..a3ccf5d
--- /dev/null
@@ -0,0 +1,38 @@
+From 32e4a5447ed9fa904a2dfcf4609c64bce053b4e8 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Mon, 12 Feb 2024 18:34:33 -0300
+Subject: [PATCH] net: dsa: realtek: fix digital interface select macro for
+ EXT0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+While no supported devices currently utilize EXT0, the register reserves
+the bits for an EXT0. EXT0 is utilized by devices from the generation
+prior to rtl8365mb, such as those supported by the driver library
+rtl8367b.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20240212-realtek-fix_ext0-v1-1-f3d2536d191a@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/realtek/rtl8365mb.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/realtek/rtl8365mb.c
++++ b/drivers/net/dsa/realtek/rtl8365mb.c
+@@ -209,10 +209,10 @@
+ #define RTL8365MB_EXT_PORT_MODE_100FX         13
+ /* External interface mode configuration registers 0~1 */
+-#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0               0x1305 /* EXT1 */
++#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0               0x1305 /* EXT0,EXT1 */
+ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1               0x13C3 /* EXT2 */
+ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \
+-              ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \
++              ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \
+                (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \
+                0x0)
+ #define   RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \
diff --git a/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch b/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch
new file mode 100644 (file)
index 0000000..e42dfc5
--- /dev/null
@@ -0,0 +1,34 @@
+From 28001bb1955fcfa63e535848c4289fcd7bb88daf Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Sun, 25 Feb 2024 13:29:53 -0300
+Subject: [PATCH 1/3] dt-bindings: net: dsa: realtek: reset-gpios is not
+ required
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The 'reset-gpios' should not be mandatory. although they might be
+required for some devices if the switch reset was left asserted by a
+previous driver, such as the bootloader.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Cc: devicetree@vger.kernel.org
+Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/realtek.yaml | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml
++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml
+@@ -125,7 +125,6 @@ else:
+     - mdc-gpios
+     - mdio-gpios
+     - mdio
+-    - reset-gpios
+ required:
+   - compatible
diff --git a/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch
new file mode 100644 (file)
index 0000000..7e8a240
--- /dev/null
@@ -0,0 +1,33 @@
+From 5fc2d68fc81801162188995e4d3dc0b26747dd76 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Sun, 25 Feb 2024 13:29:54 -0300
+Subject: [PATCH 2/3] dt-bindings: net: dsa: realtek: add reset controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Realtek switches can use a reset controller instead of reset-gpios.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Cc: devicetree@vger.kernel.org
+Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/realtek.yaml | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml
++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml
+@@ -59,6 +59,9 @@ properties:
+     description: GPIO to be used to reset the whole device
+     maxItems: 1
++  resets:
++    maxItems: 1
++
+   realtek,disable-leds:
+     type: boolean
+     description: |
diff --git a/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch
new file mode 100644 (file)
index 0000000..7a280bf
--- /dev/null
@@ -0,0 +1,123 @@
+From 56998aa6b7f0f31ce8df23c00701af2d8e8a1f1a Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Sun, 25 Feb 2024 13:29:55 -0300
+Subject: [PATCH 3/3] net: dsa: realtek: support reset controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for resetting the device using a reset controller,
+complementing the existing GPIO reset functionality (reset-gpios).
+
+Although the reset is optional and the driver performs a soft reset
+during setup, if the initial reset pin state was asserted, the driver
+will not detect the device until the reset is deasserted.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/realtek.h |  2 ++
+ drivers/net/dsa/realtek/rtl83xx.c | 42 +++++++++++++++++++++++++++----
+ drivers/net/dsa/realtek/rtl83xx.h |  2 ++
+ 3 files changed, 41 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/realtek/realtek.h
++++ b/drivers/net/dsa/realtek/realtek.h
+@@ -12,6 +12,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/gpio/consumer.h>
+ #include <net/dsa.h>
++#include <linux/reset.h>
+ #define REALTEK_HW_STOP_DELAY         25      /* msecs */
+ #define REALTEK_HW_START_DELAY                100     /* msecs */
+@@ -48,6 +49,7 @@ struct rtl8366_vlan_4k {
+ struct realtek_priv {
+       struct device           *dev;
++      struct reset_control    *reset_ctl;
+       struct gpio_desc        *reset;
+       struct gpio_desc        *mdc;
+       struct gpio_desc        *mdio;
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -185,6 +185,13 @@ rtl83xx_probe(struct device *dev,
+                                                   "realtek,disable-leds");
+       /* TODO: if power is software controlled, set up any regulators here */
++      priv->reset_ctl = devm_reset_control_get_optional(dev, NULL);
++      if (IS_ERR(priv->reset_ctl)) {
++              ret = PTR_ERR(priv->reset_ctl);
++              dev_err_probe(dev, ret, "failed to get reset control\n");
++              return ERR_CAST(priv->reset_ctl);
++      }
++
+       priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(priv->reset)) {
+               dev_err(dev, "failed to get RESET GPIO\n");
+@@ -193,11 +200,11 @@ rtl83xx_probe(struct device *dev,
+       dev_set_drvdata(dev, priv);
+-      if (priv->reset) {
+-              gpiod_set_value(priv->reset, 1);
++      if (priv->reset_ctl || priv->reset) {
++              rtl83xx_reset_assert(priv);
+               dev_dbg(dev, "asserted RESET\n");
+               msleep(REALTEK_HW_STOP_DELAY);
+-              gpiod_set_value(priv->reset, 0);
++              rtl83xx_reset_deassert(priv);
+               msleep(REALTEK_HW_START_DELAY);
+               dev_dbg(dev, "deasserted RESET\n");
+       }
+@@ -293,11 +300,36 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R
+ void rtl83xx_remove(struct realtek_priv *priv)
+ {
+       /* leave the device reset asserted */
+-      if (priv->reset)
+-              gpiod_set_value(priv->reset, 1);
++      rtl83xx_reset_assert(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
++void rtl83xx_reset_assert(struct realtek_priv *priv)
++{
++      int ret;
++
++      ret = reset_control_assert(priv->reset_ctl);
++      if (ret)
++              dev_warn(priv->dev,
++                       "Failed to assert the switch reset control: %pe\n",
++                       ERR_PTR(ret));
++
++      gpiod_set_value(priv->reset, true);
++}
++
++void rtl83xx_reset_deassert(struct realtek_priv *priv)
++{
++      int ret;
++
++      ret = reset_control_deassert(priv->reset_ctl);
++      if (ret)
++              dev_warn(priv->dev,
++                       "Failed to deassert the switch reset control: %pe\n",
++                       ERR_PTR(ret));
++
++      gpiod_set_value(priv->reset, false);
++}
++
+ MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+ MODULE_DESCRIPTION("Realtek DSA switches common module");
+--- a/drivers/net/dsa/realtek/rtl83xx.h
++++ b/drivers/net/dsa/realtek/rtl83xx.h
+@@ -18,5 +18,7 @@ int rtl83xx_register_switch(struct realt
+ void rtl83xx_unregister_switch(struct realtek_priv *priv);
+ void rtl83xx_shutdown(struct realtek_priv *priv);
+ void rtl83xx_remove(struct realtek_priv *priv);
++void rtl83xx_reset_assert(struct realtek_priv *priv);
++void rtl83xx_reset_deassert(struct realtek_priv *priv);
+ #endif /* _RTL83XX_H */
diff --git a/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch b/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch
new file mode 100644 (file)
index 0000000..7299a25
--- /dev/null
@@ -0,0 +1,45 @@
+From 4f580e9aced1816398c1c64f178302a22b8ea6e2 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Sat, 27 Apr 2024 02:11:29 -0300
+Subject: [PATCH 2/3] net: dsa: realtek: do not assert reset on remove
+
+The necessity of asserting the reset on removal was previously
+questioned, as DSA's own cleanup methods should suffice to prevent
+traffic leakage[1].
+
+When a driver has subdrivers controlled by devres, they will be
+unregistered after the main driver's .remove is executed. If it asserts
+a reset, the subdrivers will be unable to communicate with the hardware
+during their cleanup. For LEDs, this means that they will fail to turn
+off, resulting in a timeout error.
+
+[1] https://lore.kernel.org/r/20240123215606.26716-9-luizluca@gmail.com/
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/rtl83xx.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/realtek/rtl83xx.c
++++ b/drivers/net/dsa/realtek/rtl83xx.c
+@@ -291,16 +291,13 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R
+  * rtl83xx_remove() - Cleanup a realtek switch driver
+  * @priv: realtek_priv pointer
+  *
+- * If a method is provided, this function asserts the hard reset of the switch
+- * in order to avoid leaking traffic when the driver is gone.
++ * Placehold for common cleanup procedures.
+  *
+- * Context: Might sleep if priv->gdev->chip->can_sleep.
++ * Context: Any
+  * Return: nothing
+  */
+ void rtl83xx_remove(struct realtek_priv *priv)
+ {
+-      /* leave the device reset asserted */
+-      rtl83xx_reset_assert(priv);
+ }
+ EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
diff --git a/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch b/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch
new file mode 100644 (file)
index 0000000..4c9ea7f
--- /dev/null
@@ -0,0 +1,396 @@
+From 32d617005475a71ebcc4ec8b2791e8d1481e9a10 Mon Sep 17 00:00:00 2001
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Date: Sat, 27 Apr 2024 02:11:30 -0300
+Subject: [PATCH 3/3] net: dsa: realtek: add LED drivers for rtl8366rb
+
+This commit introduces LED drivers for rtl8366rb, enabling LEDs to be
+described in the device tree using the same format as qca8k. Each port
+can configure up to 4 LEDs.
+
+If all LEDs in a group use the default state "keep", they will use the
+default behavior after a reset. Changing the brightness of one LED,
+either manually or by a trigger, will disable the default hardware
+trigger and switch the entire LED group to manually controlled LEDs.
+Once in this mode, there is no way to revert to hardware-controlled LEDs
+(except by resetting the switch).
+
+Software triggers function as expected with manually controlled LEDs.
+
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek/rtl8366rb.c | 304 ++++++++++++++++++++++++----
+ 1 file changed, 265 insertions(+), 39 deletions(-)
+
+--- a/drivers/net/dsa/realtek/rtl8366rb.c
++++ b/drivers/net/dsa/realtek/rtl8366rb.c
+@@ -180,6 +180,7 @@
+ #define RTL8366RB_VLAN_INGRESS_CTRL2_REG      0x037f
+ /* LED control registers */
++/* The LED blink rate is global; it is used by all triggers in all groups. */
+ #define RTL8366RB_LED_BLINKRATE_REG           0x0430
+ #define RTL8366RB_LED_BLINKRATE_MASK          0x0007
+ #define RTL8366RB_LED_BLINKRATE_28MS          0x0000
+@@ -195,31 +196,21 @@
+       (4 * (led_group))
+ #define RTL8366RB_LED_CTRL_MASK(led_group)    \
+       (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group))
+-#define RTL8366RB_LED_OFF                     0x0
+-#define RTL8366RB_LED_DUP_COL                 0x1
+-#define RTL8366RB_LED_LINK_ACT                        0x2
+-#define RTL8366RB_LED_SPD1000                 0x3
+-#define RTL8366RB_LED_SPD100                  0x4
+-#define RTL8366RB_LED_SPD10                   0x5
+-#define RTL8366RB_LED_SPD1000_ACT             0x6
+-#define RTL8366RB_LED_SPD100_ACT              0x7
+-#define RTL8366RB_LED_SPD10_ACT                       0x8
+-#define RTL8366RB_LED_SPD100_10_ACT           0x9
+-#define RTL8366RB_LED_FIBER                   0xa
+-#define RTL8366RB_LED_AN_FAULT                        0xb
+-#define RTL8366RB_LED_LINK_RX                 0xc
+-#define RTL8366RB_LED_LINK_TX                 0xd
+-#define RTL8366RB_LED_MASTER                  0xe
+-#define RTL8366RB_LED_FORCE                   0xf
+ /* The RTL8366RB_LED_X_X registers are used to manually set the LED state only
+  * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is
+- * RTL8366RB_LED_FORCE. Otherwise, it is ignored.
++ * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored.
+  */
+ #define RTL8366RB_LED_0_1_CTRL_REG            0x0432
+-#define RTL8366RB_LED_1_OFFSET                        6
+ #define RTL8366RB_LED_2_3_CTRL_REG            0x0433
+-#define RTL8366RB_LED_3_OFFSET                        6
++#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \
++      ((led_group) <= 1 ? \
++              RTL8366RB_LED_0_1_CTRL_REG : \
++              RTL8366RB_LED_2_3_CTRL_REG)
++#define RTL8366RB_LED_0_X_CTRL_MASK           GENMASK(5, 0)
++#define RTL8366RB_LED_X_1_CTRL_MASK           GENMASK(11, 6)
++#define RTL8366RB_LED_2_X_CTRL_MASK           GENMASK(5, 0)
++#define RTL8366RB_LED_X_3_CTRL_MASK           GENMASK(11, 6)
+ #define RTL8366RB_MIB_COUNT                   33
+ #define RTL8366RB_GLOBAL_MIB_COUNT            1
+@@ -363,14 +354,44 @@
+ #define RTL8366RB_GREEN_FEATURE_TX    BIT(0)
+ #define RTL8366RB_GREEN_FEATURE_RX    BIT(2)
++enum rtl8366_ledgroup_mode {
++      RTL8366RB_LEDGROUP_OFF                  = 0x0,
++      RTL8366RB_LEDGROUP_DUP_COL              = 0x1,
++      RTL8366RB_LEDGROUP_LINK_ACT             = 0x2,
++      RTL8366RB_LEDGROUP_SPD1000              = 0x3,
++      RTL8366RB_LEDGROUP_SPD100               = 0x4,
++      RTL8366RB_LEDGROUP_SPD10                = 0x5,
++      RTL8366RB_LEDGROUP_SPD1000_ACT          = 0x6,
++      RTL8366RB_LEDGROUP_SPD100_ACT           = 0x7,
++      RTL8366RB_LEDGROUP_SPD10_ACT            = 0x8,
++      RTL8366RB_LEDGROUP_SPD100_10_ACT        = 0x9,
++      RTL8366RB_LEDGROUP_FIBER                = 0xa,
++      RTL8366RB_LEDGROUP_AN_FAULT             = 0xb,
++      RTL8366RB_LEDGROUP_LINK_RX              = 0xc,
++      RTL8366RB_LEDGROUP_LINK_TX              = 0xd,
++      RTL8366RB_LEDGROUP_MASTER               = 0xe,
++      RTL8366RB_LEDGROUP_FORCE                = 0xf,
++
++      __RTL8366RB_LEDGROUP_MODE_MAX
++};
++
++struct rtl8366rb_led {
++      u8 port_num;
++      u8 led_group;
++      struct realtek_priv *priv;
++      struct led_classdev cdev;
++};
++
+ /**
+  * struct rtl8366rb - RTL8366RB-specific data
+  * @max_mtu: per-port max MTU setting
+  * @pvid_enabled: if PVID is set for respective port
++ * @leds: per-port and per-ledgroup led info
+  */
+ struct rtl8366rb {
+       unsigned int max_mtu[RTL8366RB_NUM_PORTS];
+       bool pvid_enabled[RTL8366RB_NUM_PORTS];
++      struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS];
+ };
+ static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+@@ -813,6 +834,217 @@ static int rtl8366rb_jam_table(const str
+       return 0;
+ }
++static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv,
++                                    u8 led_group,
++                                    enum rtl8366_ledgroup_mode mode)
++{
++      int ret;
++      u32 val;
++
++      val = mode << RTL8366RB_LED_CTRL_OFFSET(led_group);
++
++      ret = regmap_update_bits(priv->map,
++                               RTL8366RB_LED_CTRL_REG,
++                               RTL8366RB_LED_CTRL_MASK(led_group),
++                               val);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port)
++{
++      switch (led_group) {
++      case 0:
++              return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
++      case 1:
++              return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
++      case 2:
++              return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
++      case 3:
++              return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
++      default:
++              return 0;
++      }
++}
++
++static int rb8366rb_get_port_led(struct rtl8366rb_led *led)
++{
++      struct realtek_priv *priv = led->priv;
++      u8 led_group = led->led_group;
++      u8 port_num = led->port_num;
++      int ret;
++      u32 val;
++
++      ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group),
++                        &val);
++      if (ret) {
++              dev_err(priv->dev, "error reading LED on port %d group %d\n",
++                      led_group, port_num);
++              return ret;
++      }
++
++      return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num));
++}
++
++static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable)
++{
++      struct realtek_priv *priv = led->priv;
++      u8 led_group = led->led_group;
++      u8 port_num = led->port_num;
++      int ret;
++
++      ret = regmap_update_bits(priv->map,
++                               RTL8366RB_LED_X_X_CTRL_REG(led_group),
++                               rtl8366rb_led_group_port_mask(led_group,
++                                                             port_num),
++                               enable ? 0xffff : 0);
++      if (ret) {
++              dev_err(priv->dev, "error updating LED on port %d group %d\n",
++                      led_group, port_num);
++              return ret;
++      }
++
++      /* Change the LED group to manual controlled LEDs if required */
++      ret = rb8366rb_set_ledgroup_mode(priv, led_group,
++                                       RTL8366RB_LEDGROUP_FORCE);
++
++      if (ret) {
++              dev_err(priv->dev, "error updating LED GROUP group %d\n",
++                      led_group);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int
++rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev,
++                                     enum led_brightness brightness)
++{
++      struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led,
++                                               cdev);
++
++      return rb8366rb_set_port_led(led, brightness == LED_ON);
++}
++
++static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp,
++                             struct fwnode_handle *led_fwnode)
++{
++      struct rtl8366rb *rb = priv->chip_data;
++      struct led_init_data init_data = { };
++      enum led_default_state state;
++      struct rtl8366rb_led *led;
++      u32 led_group;
++      int ret;
++
++      ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group);
++      if (ret)
++              return ret;
++
++      if (led_group >= RTL8366RB_NUM_LEDGROUPS) {
++              dev_warn(priv->dev, "Invalid LED reg %d defined for port %d",
++                       led_group, dp->index);
++              return -EINVAL;
++      }
++
++      led = &rb->leds[dp->index][led_group];
++      led->port_num = dp->index;
++      led->led_group = led_group;
++      led->priv = priv;
++
++      state = led_init_default_state_get(led_fwnode);
++      switch (state) {
++      case LEDS_DEFSTATE_ON:
++              led->cdev.brightness = 1;
++              rb8366rb_set_port_led(led, 1);
++              break;
++      case LEDS_DEFSTATE_KEEP:
++              led->cdev.brightness =
++                      rb8366rb_get_port_led(led);
++              break;
++      case LEDS_DEFSTATE_OFF:
++      default:
++              led->cdev.brightness = 0;
++              rb8366rb_set_port_led(led, 0);
++      }
++
++      led->cdev.max_brightness = 1;
++      led->cdev.brightness_set_blocking =
++              rtl8366rb_cled_brightness_set_blocking;
++      init_data.fwnode = led_fwnode;
++      init_data.devname_mandatory = true;
++
++      init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d",
++                                       dp->ds->index, dp->index, led_group);
++      if (!init_data.devicename)
++              return -ENOMEM;
++
++      ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data);
++      if (ret) {
++              dev_warn(priv->dev, "Failed to init LED %d for port %d",
++                       led_group, dp->index);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv)
++{
++      int ret = 0;
++      int i;
++
++      regmap_update_bits(priv->map,
++                         RTL8366RB_INTERRUPT_CONTROL_REG,
++                         RTL8366RB_P4_RGMII_LED,
++                         0);
++
++      for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) {
++              ret = rb8366rb_set_ledgroup_mode(priv, i,
++                                               RTL8366RB_LEDGROUP_OFF);
++              if (ret)
++                      return ret;
++      }
++
++      return ret;
++}
++
++static int rtl8366rb_setup_leds(struct realtek_priv *priv)
++{
++      struct device_node *leds_np, *led_np;
++      struct dsa_switch *ds = &priv->ds;
++      struct dsa_port *dp;
++      int ret = 0;
++
++      dsa_switch_for_each_port(dp, ds) {
++              if (!dp->dn)
++                      continue;
++
++              leds_np = of_get_child_by_name(dp->dn, "leds");
++              if (!leds_np) {
++                      dev_dbg(priv->dev, "No leds defined for port %d",
++                              dp->index);
++                      continue;
++              }
++
++              for_each_child_of_node(leds_np, led_np) {
++                      ret = rtl8366rb_setup_led(priv, dp,
++                                                of_fwnode_handle(led_np));
++                      if (ret) {
++                              of_node_put(led_np);
++                              break;
++                      }
++              }
++
++              of_node_put(leds_np);
++              if (ret)
++                      return ret;
++      }
++      return 0;
++}
++
+ static int rtl8366rb_setup(struct dsa_switch *ds)
+ {
+       struct realtek_priv *priv = ds->priv;
+@@ -821,7 +1053,6 @@ static int rtl8366rb_setup(struct dsa_sw
+       u32 chip_ver = 0;
+       u32 chip_id = 0;
+       int jam_size;
+-      u32 val;
+       int ret;
+       int i;
+@@ -997,7 +1228,9 @@ static int rtl8366rb_setup(struct dsa_sw
+       if (ret)
+               return ret;
+-      /* Set blinking, TODO: make this configurable */
++      /* Set blinking, used by all LED groups using HW triggers.
++       * TODO: make this configurable
++       */
+       ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG,
+                                RTL8366RB_LED_BLINKRATE_MASK,
+                                RTL8366RB_LED_BLINKRATE_56MS);
+@@ -1005,26 +1238,19 @@ static int rtl8366rb_setup(struct dsa_sw
+               return ret;
+       /* Set up LED activity:
+-       * Each port has 4 LEDs, we configure all ports to the same
+-       * behaviour (no individual config) but we can set up each
+-       * LED separately.
++       * Each port has 4 LEDs on fixed groups. Each group shares the same
++       * hardware trigger across all ports. LEDs can only be indiviually
++       * controlled setting the LED group to fixed mode and using the driver
++       * to toggle them LEDs on/off.
+        */
+       if (priv->leds_disabled) {
+-              /* Turn everything off */
+-              regmap_update_bits(priv->map,
+-                                 RTL8366RB_INTERRUPT_CONTROL_REG,
+-                                 RTL8366RB_P4_RGMII_LED,
+-                                 0);
+-
+-              for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) {
+-                      val = RTL8366RB_LED_OFF << RTL8366RB_LED_CTRL_OFFSET(i);
+-                      ret = regmap_update_bits(priv->map,
+-                                               RTL8366RB_LED_CTRL_REG,
+-                                               RTL8366RB_LED_CTRL_MASK(i),
+-                                               val);
+-                      if (ret)
+-                              return ret;
+-              }
++              ret = rtl8366rb_setup_all_leds_off(priv);
++              if (ret)
++                      return ret;
++      } else {
++              ret = rtl8366rb_setup_leds(priv);
++              if (ret)
++                      return ret;
+       }
+       ret = rtl8366_reset_vlan(priv);