From: Daniel Golle Date: Tue, 21 Apr 2026 02:26:14 +0000 (+0100) Subject: generic: 6.18: import updated standalone PCS handling X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=18cbd83a1443e7fe39fa2ccbad5fc492b531acbd;p=thirdparty%2Fopenwrt.git generic: 6.18: import updated standalone PCS handling Import pending series introducing support for standalone PCS drivers. This has previously already been used by the airoha target, and is also the base for the closer-to-upstream patches for MediaTek MT7988 10G SerDes support. In order to not having to diverge from upstream also backport series for standardized handling for PHY and PCS SerDes pair polarity. Signed-off-by: Daniel Golle --- diff --git a/target/linux/ath79/patches-6.18/700-phy-add-ath79-usb-phys.patch b/target/linux/ath79/patches-6.18/700-phy-add-ath79-usb-phys.patch index 74731f069ac..5fab4e33656 100644 --- a/target/linux/ath79/patches-6.18/700-phy-add-ath79-usb-phys.patch +++ b/target/linux/ath79/patches-6.18/700-phy-add-ath79-usb-phys.patch @@ -15,7 +15,7 @@ Signed-off-by: John Crispin --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig -@@ -25,6 +25,22 @@ config GENERIC_PHY_MIPI_DPHY +@@ -47,6 +47,22 @@ config GENERIC_PHY_MIPI_DPHY Provides a number of helpers a core functions for MIPI D-PHY drivers to us. @@ -40,9 +40,9 @@ Signed-off-by: John Crispin depends on OF && (ARCH_LPC18XX || COMPILE_TEST) --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile -@@ -4,6 +4,8 @@ - # - +@@ -6,6 +6,8 @@ + obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o + obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_AR7100_USB) += phy-ar7100-usb.o +obj-$(CONFIG_PHY_AR7200_USB) += phy-ar7200-usb.o diff --git a/target/linux/generic/backport-6.18/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch b/target/linux/generic/backport-6.18/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch new file mode 100644 index 00000000000..65f0496b754 --- /dev/null +++ b/target/linux/generic/backport-6.18/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch @@ -0,0 +1,79 @@ +From 0e4d7df2f3b2e59c1bccc09ea099b7a6c2dda886 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 28 Jan 2026 10:51:56 +0000 +Subject: [PATCH] net: phylink: fix NULL pointer deref in + phylink_major_config() + +When a MAC driver returns a PCS for an interface mode, and then we +attempt to switch to a different mode that doesn't require a PCS, +this causes phylink to oops: + +Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010 +Mem abort info: + ESR = 0x0000000096000044 + EC = 0x25: DABT (current EL), IL = 32 bits + SET = 0, FnV = 0 + EA = 0, S1PTW = 0 + FSC = 0x04: level 0 translation fault +Data abort info: + ISV = 0, ISS = 0x00000044, ISS2 = 0x00000000 + CM = 0, WnR = 1, TnD = 0, TagAccess = 0 + GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 +user pgtable: 4k pages, 48-bit VAs, pgdp=0000000137f96000 +[0000000000000010] pgd=0000000000000000, p4d=0000000000000000 +Internal error: Oops: 0000000096000044 [#1] SMP +Modules linked in: -- +CPU: 1 UID: 0 PID: 55 Comm: kworker/u33:0 Not tainted 6.19.0-rc5-00581-g73cb8467a63e #1 PREEMPT +Hardware name: Qualcomm Technologies, Inc. Lemans Ride Rev3 (DT) +Workqueue: events_power_efficient phylink_resolve +pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS +BTYPE=--) +pc : phylink_major_config+0x408/0x948 +lr : phylink_major_config+0x3fc/0x948 +sp : ffff800080353c60 +x29: ffff800080353cb0 x28: ffffb305068a8a00 x27: ffffb305068a8000 +x26: ffff000080092100 x25: 0000000000000000 x24: 0000000000000000 +x23: 0000000000000001 x22: 0000000000000000 x21: ffffb3050555b3d0 +x20: ffff800080353d10 x19: ffff0000b6059400 x18: 00000000ffffffff +x17: 74756f2f79687020 x16: ffffb305045e4f18 x15: 6769666e6f632072 +x14: 6f6a616d203a3168 x13: 782d657361623030 x12: ffffb305068c6a98 +x11: 0000000000000583 x10: 0000000000000018 x9 : ffffb305068c6a98 +x8 : 0000000100006583 x7 : 0000000000000000 x6 : ffff00008083cc40 +x5 : ffff00008083cc40 x4 : 0000000000000001 x3 : 0000000000000001 +x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000b269e5a8 +Call trace: + phylink_major_config+0x408/0x948 (P) + phylink_resolve+0x294/0x6e4 + process_one_work+0x148/0x28c + worker_thread+0x2d8/0x3d8 + kthread+0x134/0x208 + ret_from_fork+0x10/0x20 +Code: d63f0020 f9400e60 b4000040 f900081f (f9000ad3) +---[ end trace 0000000000000000 ]--- + +This is caused by "pcs" being NULL when we attempt to execute: + + pcs->phylink = pl; + +Make this conditional on pcs being non-null. + +Fixes: 486dc391ef43 ("net: phylink: allow mac_select_pcs() to remove a PCS") +Reported-by: Mohd Ayaan Anwar +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1vl39Q-00000006utm-229h@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1287,7 +1287,8 @@ static void phylink_major_config(struct + if (pl->pcs) + pl->pcs->phylink = NULL; + +- pcs->phylink = pl; ++ if (pcs) ++ pcs->phylink = pl; + + pl->pcs = pcs; + } diff --git a/target/linux/generic/backport-6.18/703-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch b/target/linux/generic/backport-6.18/703-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch new file mode 100644 index 00000000000..d1cd6f1712a --- /dev/null +++ b/target/linux/generic/backport-6.18/703-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch @@ -0,0 +1,61 @@ +From 7bf588dc62a05c1866efe098e1b188fd879aa2cf Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 14:19:51 +0200 +Subject: [PATCH] net: phylink: simplify phylink_resolve() -> + phylink_major_config() path + +This is a trivial change with no functional effect which replaces the +pattern: + +if (a) { + if (b) { + do_stuff(); + } +} + +with: + +if (a && b) { + do_stuff(); +}; + +The purpose is to reduce the delta of a subsequent functional change. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20260119121954.1624535-2-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1681,18 +1681,16 @@ static void phylink_resolve(struct work_ + if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + +- if (mac_config) { +- if (link_state.interface != pl->link_config.interface) { +- /* The interface has changed, force the link down and +- * then reconfigure. +- */ +- if (cur_link_state) { +- phylink_link_down(pl); +- cur_link_state = false; +- } +- phylink_major_config(pl, false, &link_state); +- pl->link_config.interface = link_state.interface; ++ if (mac_config && link_state.interface != pl->link_config.interface) { ++ /* The interface has changed, so force the link down and then ++ * reconfigure. ++ */ ++ if (cur_link_state) { ++ phylink_link_down(pl); ++ cur_link_state = false; + } ++ phylink_major_config(pl, false, &link_state); ++ pl->link_config.interface = link_state.interface; + } + + /* If configuration of the interface failed, force the link down diff --git a/target/linux/generic/backport-6.18/785-v7.0-01-dt-bindings-phy-rename-transmit-amplitude.yaml-to-ph.patch b/target/linux/generic/backport-6.18/785-v7.0-01-dt-bindings-phy-rename-transmit-amplitude.yaml-to-ph.patch new file mode 100644 index 00000000000..c1f397fc7ab --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-01-dt-bindings-phy-rename-transmit-amplitude.yaml-to-ph.patch @@ -0,0 +1,235 @@ +From b7b4dcd96e3dfbb955d152c9ce4b490498b0f4b4 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Sun, 11 Jan 2026 11:39:30 +0200 +Subject: [PATCH 1/5] dt-bindings: phy: rename transmit-amplitude.yaml to + phy-common-props.yaml + +I would like to add more properties similar to tx-p2p-microvolt, and I +don't think it makes sense to create one schema for each such property +(transmit-amplitude.yaml, lane-polarity.yaml, transmit-equalization.yaml +etc). + +Instead, let's rename to phy-common-props.yaml, which makes it a more +adequate host schema for all the above properties. + +Signed-off-by: Vladimir Oltean +Acked-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260111093940.975359-2-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + .../{transmit-amplitude.yaml => phy-common-props.yaml} | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + rename Documentation/devicetree/bindings/phy/{transmit-amplitude.yaml => phy-common-props.yaml} (90%) + +--- a/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml ++++ /dev/null +@@ -1,103 +0,0 @@ +-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +-%YAML 1.2 +---- +-$id: http://devicetree.org/schemas/phy/transmit-amplitude.yaml# +-$schema: http://devicetree.org/meta-schemas/core.yaml# +- +-title: Common PHY and network PCS transmit amplitude property +- +-description: +- Binding describing the peak-to-peak transmit amplitude for common PHYs +- and network PCSes. +- +-maintainers: +- - Marek Behún +- +-properties: +- tx-p2p-microvolt: +- description: +- Transmit amplitude voltages in microvolts, peak-to-peak. If this property +- contains multiple values for various PHY modes, the +- 'tx-p2p-microvolt-names' property must be provided and contain +- corresponding mode names. +- +- tx-p2p-microvolt-names: +- description: | +- Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' +- property. Required only if multiple voltages are provided. +- +- If a value of 'default' is provided, the system should use it for any PHY +- mode that is otherwise not defined here. If 'default' is not provided, the +- system should use manufacturer default value. +- minItems: 1 +- maxItems: 16 +- items: +- enum: +- - default +- +- # ethernet modes +- - sgmii +- - qsgmii +- - xgmii +- - 1000base-x +- - 2500base-x +- - 5gbase-r +- - rxaui +- - xaui +- - 10gbase-kr +- - usxgmii +- - 10gbase-r +- - 25gbase-r +- +- # PCIe modes +- - pcie +- - pcie1 +- - pcie2 +- - pcie3 +- - pcie4 +- - pcie5 +- - pcie6 +- +- # USB modes +- - usb +- - usb-ls +- - usb-fs +- - usb-hs +- - usb-ss +- - usb-ss+ +- - usb-4 +- +- # storage modes +- - sata +- - ufs-hs +- - ufs-hs-a +- - ufs-hs-b +- +- # display modes +- - lvds +- - dp +- - dp-rbr +- - dp-hbr +- - dp-hbr2 +- - dp-hbr3 +- - dp-uhbr-10 +- - dp-uhbr-13.5 +- - dp-uhbr-20 +- +- # camera modes +- - mipi-dphy +- - mipi-dphy-univ +- - mipi-dphy-v2.5-univ +- +-dependencies: +- tx-p2p-microvolt-names: [ tx-p2p-microvolt ] +- +-additionalProperties: true +- +-examples: +- - | +- phy: phy { +- #phy-cells = <1>; +- tx-p2p-microvolt = <915000>, <1100000>, <1200000>; +- tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss"; +- }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml +@@ -0,0 +1,103 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/phy-common-props.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Common PHY and network PCS properties ++ ++description: ++ Common PHY and network PCS properties, such as peak-to-peak transmit ++ amplitude. ++ ++maintainers: ++ - Marek Behún ++ ++properties: ++ tx-p2p-microvolt: ++ description: ++ Transmit amplitude voltages in microvolts, peak-to-peak. If this property ++ contains multiple values for various PHY modes, the ++ 'tx-p2p-microvolt-names' property must be provided and contain ++ corresponding mode names. ++ ++ tx-p2p-microvolt-names: ++ description: | ++ Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' ++ property. Required only if multiple voltages are provided. ++ ++ If a value of 'default' is provided, the system should use it for any PHY ++ mode that is otherwise not defined here. If 'default' is not provided, the ++ system should use manufacturer default value. ++ minItems: 1 ++ maxItems: 16 ++ items: ++ enum: ++ - default ++ ++ # ethernet modes ++ - sgmii ++ - qsgmii ++ - xgmii ++ - 1000base-x ++ - 2500base-x ++ - 5gbase-r ++ - rxaui ++ - xaui ++ - 10gbase-kr ++ - usxgmii ++ - 10gbase-r ++ - 25gbase-r ++ ++ # PCIe modes ++ - pcie ++ - pcie1 ++ - pcie2 ++ - pcie3 ++ - pcie4 ++ - pcie5 ++ - pcie6 ++ ++ # USB modes ++ - usb ++ - usb-ls ++ - usb-fs ++ - usb-hs ++ - usb-ss ++ - usb-ss+ ++ - usb-4 ++ ++ # storage modes ++ - sata ++ - ufs-hs ++ - ufs-hs-a ++ - ufs-hs-b ++ ++ # display modes ++ - lvds ++ - dp ++ - dp-rbr ++ - dp-hbr ++ - dp-hbr2 ++ - dp-hbr3 ++ - dp-uhbr-10 ++ - dp-uhbr-13.5 ++ - dp-uhbr-20 ++ ++ # camera modes ++ - mipi-dphy ++ - mipi-dphy-univ ++ - mipi-dphy-v2.5-univ ++ ++dependencies: ++ tx-p2p-microvolt-names: [ tx-p2p-microvolt ] ++ ++additionalProperties: true ++ ++examples: ++ - | ++ phy: phy { ++ #phy-cells = <1>; ++ tx-p2p-microvolt = <915000>, <1100000>, <1200000>; ++ tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss"; ++ }; diff --git a/target/linux/generic/backport-6.18/785-v7.0-02-dt-bindings-phy-common-props-create-a-reusable-proto.patch b/target/linux/generic/backport-6.18/785-v7.0-02-dt-bindings-phy-common-props-create-a-reusable-proto.patch new file mode 100644 index 00000000000..543e718c6f0 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-02-dt-bindings-phy-common-props-create-a-reusable-proto.patch @@ -0,0 +1,69 @@ +From 33c79865c7d3cc84705ed133c101794902e60269 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Sun, 11 Jan 2026 11:39:31 +0200 +Subject: [PATCH 2/5] dt-bindings: phy-common-props: create a reusable + "protocol-names" definition + +Other properties also need to be defined per protocol than just +tx-p2p-microvolt-names. Create a common definition to avoid copying a 55 +line property. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260111093940.975359-3-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + .../bindings/phy/phy-common-props.yaml | 34 +++++++++++-------- + 1 file changed, 19 insertions(+), 15 deletions(-) + +--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml ++++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml +@@ -13,22 +13,12 @@ description: + maintainers: + - Marek Behún + +-properties: +- tx-p2p-microvolt: ++$defs: ++ protocol-names: + description: +- Transmit amplitude voltages in microvolts, peak-to-peak. If this property +- contains multiple values for various PHY modes, the +- 'tx-p2p-microvolt-names' property must be provided and contain +- corresponding mode names. +- +- tx-p2p-microvolt-names: +- description: | +- Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' +- property. Required only if multiple voltages are provided. +- +- If a value of 'default' is provided, the system should use it for any PHY +- mode that is otherwise not defined here. If 'default' is not provided, the +- system should use manufacturer default value. ++ Names of the PHY modes. If a value of 'default' is provided, the system ++ should use it for any PHY mode that is otherwise not defined here. If ++ 'default' is not provided, the system should use manufacturer default value. + minItems: 1 + maxItems: 16 + items: +@@ -89,6 +79,20 @@ properties: + - mipi-dphy-univ + - mipi-dphy-v2.5-univ + ++properties: ++ tx-p2p-microvolt: ++ description: ++ Transmit amplitude voltages in microvolts, peak-to-peak. If this property ++ contains multiple values for various PHY modes, the ++ 'tx-p2p-microvolt-names' property must be provided and contain ++ corresponding mode names. ++ ++ tx-p2p-microvolt-names: ++ description: ++ Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' ++ property. Required only if multiple voltages are provided. ++ $ref: "#/$defs/protocol-names" ++ + dependencies: + tx-p2p-microvolt-names: [ tx-p2p-microvolt ] + diff --git a/target/linux/generic/backport-6.18/785-v7.0-03-dt-bindings-phy-common-props-ensure-protocol-names-a.patch b/target/linux/generic/backport-6.18/785-v7.0-03-dt-bindings-phy-common-props-ensure-protocol-names-a.patch new file mode 100644 index 00000000000..fd74b41cd6f --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-03-dt-bindings-phy-common-props-ensure-protocol-names-a.patch @@ -0,0 +1,42 @@ +From 01fc2215940c20bbb22fa196a331ec9d50e45452 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Sun, 11 Jan 2026 11:39:32 +0200 +Subject: [PATCH 3/5] dt-bindings: phy-common-props: ensure protocol-names are + unique + +Rob Herring points out that "The default for .*-names is the entries +don't have to be unique.": +https://lore.kernel.org/linux-phy/20251204155219.GA1533839-robh@kernel.org/ + +Let's use uniqueItems: true to make sure the schema enforces this. It +doesn't make sense in this case to have duplicate properties for the +same SerDes protocol. + +Note that this can only be done with the $defs + $ref pattern as +established by the previous commit. When the tx-p2p-microvolt-names +constraints were expressed directly under "properties", it would have +been validated by the string-array meta-schema, which does not support +the 'uniqueItems' keyword as can be seen below. + +properties:tx-p2p-microvolt-names: Additional properties are not allowed ('uniqueItems' was unexpected) + from schema $id: http://devicetree.org/meta-schemas/string-array.yaml + +Suggested-by: Rob Herring +Signed-off-by: Vladimir Oltean +Reviewed-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260111093940.975359-4-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + Documentation/devicetree/bindings/phy/phy-common-props.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml ++++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml +@@ -21,6 +21,7 @@ $defs: + 'default' is not provided, the system should use manufacturer default value. + minItems: 1 + maxItems: 16 ++ uniqueItems: true + items: + enum: + - default diff --git a/target/linux/generic/backport-6.18/785-v7.0-04-dt-bindings-phy-common-props-RX-and-TX-lane-polarity.patch b/target/linux/generic/backport-6.18/785-v7.0-04-dt-bindings-phy-common-props-RX-and-TX-lane-polarity.patch new file mode 100644 index 00000000000..70f9a559593 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-04-dt-bindings-phy-common-props-RX-and-TX-lane-polarity.patch @@ -0,0 +1,177 @@ +From fceb17ac05e772ffc82f1f008e876bf7752f0576 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Sun, 11 Jan 2026 11:39:33 +0200 +Subject: [PATCH 4/5] dt-bindings: phy-common-props: RX and TX lane polarity + inversion + +Differential signaling is a technique for high-speed protocols to be +more resilient to noise. At the transmit side we have a positive and a +negative signal which are mirror images of each other. At the receiver, +if we subtract the negative signal (say of amplitude -A) from the +positive signal (say +A), we recover the original single-ended signal at +twice its original amplitude. But any noise, like one coming from EMI +from outside sources, is supposed to have an almost equal impact upon +the positive (A + E, E being for "error") and negative signal (-A + E). +So (A + E) - (-A + E) eliminates this noise, and this is what makes +differential signaling useful. + +Except that in order to work, there must be strict requirements observed +during PCB design and layout, like the signal traces needing to have the +same length and be physically close to each other, and many others. + +Sometimes it is not easy to fulfill all these requirements, a simple +case to understand is when on chip A's pins, the positive pin is on the +left and the negative is on the right, but on the chip B's pins (with +which A tries to communicate), positive is on the right and negative on +the left. The signals would need to cross, using vias and other ugly +stuff that affects signal integrity (introduces impedance +discontinuities which cause reflections, etc). + +So sometimes, board designers intentionally connect differential lanes +the wrong way, and expect somebody else to invert that signal to recover +useful data. This is where RX and TX polarity inversion comes in as a +generic concept that applies to any high-speed serial protocol as long +as it uses differential signaling. + +I've stopped two attempts to introduce more vendor-specific descriptions +of this only in the past month: +https://lore.kernel.org/linux-phy/20251110110536.2596490-1-horatiu.vultur@microchip.com/ +https://lore.kernel.org/netdev/20251028000959.3kiac5kwo5pcl4ft@skbuf/ + +and in the kernel we already have merged: +- "st,px_rx_pol_inv" +- "st,pcie-tx-pol-inv" +- "st,sata-tx-pol-inv" +- "mediatek,pnswap" +- "airoha,pnswap-rx" +- "airoha,pnswap-tx" + +and maybe more. So it is pretty general. + +One additional element of complexity is introduced by the fact that for +some protocols, receivers can automatically detect and correct for an +inverted lane polarity (example: the PCIe LTSSM does this in the +Polling.Configuration state; the USB 3.1 Link Layer Test Specification +says that the detection and correction of the lane polarity inversion in +SuperSpeed operation shall be enabled in Polling.RxEQ.). Whereas for +other protocols (SGMII, SATA, 10GBase-R, etc etc), the polarity is all +manual and there is no detection mechanism mandated by their respective +standards. + +So why would one even describe rx-polarity and tx-polarity for protocols +like PCIe, if it had to always be PHY_POL_AUTO? + +Related question: why would we define the polarity as an array per +protocol? Isn't the physical PCB layout protocol-agnostic, and aren't we +describing the same physical reality from the lens of different protocols? + +The answer to both questions is because multi-protocol PHYs exist +(supporting e.g. USB2 and USB3, or SATA and PCIe, or PCIe and Ethernet +over the same lane), one would need to manually set the polarity for +SATA/Ethernet, while leaving it at auto for PCIe/USB 3.0+. + +I also investigated from another angle: what if polarity inversion in +the PHY is one layer, and then the PCIe/USB3 LTSSM polarity detection is +another layer on top? Then rx-polarity = doesn't make +sense, it can still be rx-polarity = or , +and the link training state machine figures things out on top of that. +This would radically simplify the design, as the elimination of +PHY_POL_AUTO inherently means that the need for a property array per +protocol also goes away. + +I don't know how things are in the general case, but at least in the 10G +and 28G Lynx SerDes blocks from NXP Layerscape devices, this isn't the +case, and there's only a single level of RX polarity inversion: in the +SerDes lane. In the case of PCIe, the controller is in charge of driving +the RDAT_INV bit autonomously, and it is read-only to software. + +So the existence of this kind of SerDes lane proves the need for +PHY_POL_AUTO to be a third state. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260111093940.975359-5-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + .../bindings/phy/phy-common-props.yaml | 49 +++++++++++++++++++ + include/dt-bindings/phy/phy.h | 4 ++ + 2 files changed, 53 insertions(+) + +--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml ++++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml +@@ -94,15 +94,64 @@ properties: + property. Required only if multiple voltages are provided. + $ref: "#/$defs/protocol-names" + ++ rx-polarity: ++ description: ++ An array of values indicating whether the differential receiver's ++ polarity is inverted. Each value can be one of ++ PHY_POL_NORMAL (0) which means the negative signal is decoded from the ++ RXN input, and the positive signal from the RXP input; ++ PHY_POL_INVERT (1) which means the negative signal is decoded from the ++ RXP input, and the positive signal from the RXN input; ++ PHY_POL_AUTO (2) which means the receiver performs automatic polarity ++ detection and correction, which is a mandatory part of link training for ++ some protocols (PCIe, USB SS). ++ ++ The values are defined in . If the property is ++ absent, the default value is undefined. ++ ++ Note that the RXP and RXN inputs refer to the block that this property is ++ under, and do not necessarily directly translate to external pins. ++ ++ If this property contains multiple values for various protocols, the ++ 'rx-polarity-names' property must be provided. ++ $ref: /schemas/types.yaml#/definitions/uint32-array ++ minItems: 1 ++ maxItems: 16 ++ items: ++ enum: [0, 1, 2] ++ ++ rx-polarity-names: ++ $ref: '#/$defs/protocol-names' ++ ++ tx-polarity: ++ description: ++ Like 'rx-polarity', except it applies to differential transmitters, ++ and only the values of PHY_POL_NORMAL and PHY_POL_INVERT are possible. ++ $ref: /schemas/types.yaml#/definitions/uint32-array ++ minItems: 1 ++ maxItems: 16 ++ items: ++ enum: [0, 1] ++ ++ tx-polarity-names: ++ $ref: '#/$defs/protocol-names' ++ + dependencies: + tx-p2p-microvolt-names: [ tx-p2p-microvolt ] ++ rx-polarity-names: [ rx-polarity ] ++ tx-polarity-names: [ tx-polarity ] + + additionalProperties: true + + examples: + - | ++ #include ++ + phy: phy { + #phy-cells = <1>; + tx-p2p-microvolt = <915000>, <1100000>, <1200000>; + tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss"; ++ rx-polarity = , ; ++ rx-polarity-names = "usb-ss", "default"; ++ tx-polarity = ; + }; +--- a/include/dt-bindings/phy/phy.h ++++ b/include/dt-bindings/phy/phy.h +@@ -24,4 +24,8 @@ + #define PHY_TYPE_CPHY 11 + #define PHY_TYPE_USXGMII 12 + ++#define PHY_POL_NORMAL 0 ++#define PHY_POL_INVERT 1 ++#define PHY_POL_AUTO 2 ++ + #endif /* _DT_BINDINGS_PHY */ diff --git a/target/linux/generic/backport-6.18/785-v7.0-05-phy-add-phy_get_rx_polarity-and-phy_get_tx_polarity.patch b/target/linux/generic/backport-6.18/785-v7.0-05-phy-add-phy_get_rx_polarity-and-phy_get_tx_polarity.patch new file mode 100644 index 00000000000..96795277581 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-05-phy-add-phy_get_rx_polarity-and-phy_get_tx_polarity.patch @@ -0,0 +1,758 @@ +From e7556b59ba65179612bce3fa56bb53d1b4fb20db Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Sun, 11 Jan 2026 11:39:34 +0200 +Subject: [PATCH 5/5] phy: add phy_get_rx_polarity() and phy_get_tx_polarity() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add helpers in the generic PHY folder which can be used using 'select +PHY_COMMON_PROPS' from Kconfig, without otherwise needing to +enable GENERIC_PHY. + +These helpers need to deal with the slight messiness of the fact that +the polarity properties are arrays per protocol, and with the fact that +there is no default value mandated by the standard properties, all +default values depend on driver and protocol (PHY_POL_NORMAL may be a +good default for SGMII, whereas PHY_POL_AUTO may be a good default for +PCIe). + +Push the supported mask of polarities to these helpers, to simplify +drivers such that they don't need to validate what's in the device tree +(or other firmware description). + +Add a KUnit test suite to make sure that the API produces the expected +results. The fact that we use fwnode structures means we can validate +with software nodes, and as opposed to the device_property API, we can +bypass the need to have a device structure. + +Co-developed-by: Bjørn Mork +Signed-off-by: Bjørn Mork +Signed-off-by: Vladimir Oltean +Link: https://patch.msgid.link/20260111093940.975359-6-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + drivers/phy/Kconfig | 22 ++ + drivers/phy/Makefile | 2 + + drivers/phy/phy-common-props-test.c | 422 +++++++++++++++++++++++++++ + drivers/phy/phy-common-props.c | 209 +++++++++++++ + include/linux/phy/phy-common-props.h | 32 ++ + 6 files changed, 697 insertions(+) + create mode 100644 drivers/phy/phy-common-props-test.c + create mode 100644 drivers/phy/phy-common-props.c + create mode 100644 include/linux/phy/phy-common-props.h + +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -5,6 +5,28 @@ + + menu "PHY Subsystem" + ++config PHY_COMMON_PROPS ++ bool ++ help ++ This parses properties common between generic PHYs and Ethernet PHYs. ++ ++ Select this from consumer drivers to gain access to helpers for ++ parsing properties from the ++ Documentation/devicetree/bindings/phy/phy-common-props.yaml schema. ++ ++config PHY_COMMON_PROPS_TEST ++ tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS ++ select PHY_COMMON_PROPS ++ depends on KUNIT ++ default KUNIT_ALL_TESTS ++ help ++ This builds KUnit tests for the PHY common property API. ++ ++ For more information on KUnit and unit tests in general, ++ please refer to the KUnit documentation in Documentation/dev-tools/kunit/. ++ ++ When in doubt, say N. ++ + config GENERIC_PHY + bool "PHY Core" + help +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -3,6 +3,8 @@ + # Makefile for the phy drivers. + # + ++obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o ++obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o + obj-$(CONFIG_GENERIC_PHY) += phy-core.o + obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o + obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o +--- /dev/null ++++ b/drivers/phy/phy-common-props-test.c +@@ -0,0 +1,422 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * phy-common-props-test.c -- Unit tests for PHY common properties API ++ * ++ * Copyright 2025-2026 NXP ++ */ ++#include ++#include ++#include ++#include ++ ++/* Test: rx-polarity property is missing */ ++static void phy_test_rx_polarity_is_missing(struct kunit *test) ++{ ++ static const struct property_entry entries[] = { ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: rx-polarity has more values than rx-polarity-names */ ++static void phy_test_rx_polarity_more_values_than_names(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL }; ++ static const char * const rx_pol_names[] = { "sgmii", "2500base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: rx-polarity has 1 value and rx-polarity-names does not exist */ ++static void phy_test_rx_polarity_single_value_no_names(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_INVERT }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: rx-polarity-names has more values than rx-polarity */ ++static void phy_test_rx_polarity_more_names_than_values(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: rx-polarity and rx-polarity-names have same length, find the name */ ++static void phy_test_rx_polarity_find_by_name(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_AUTO }; ++ static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "usb-ss" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL); ++ ++ ret = phy_get_manual_rx_polarity(node, "2500base-x", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ ret = phy_get_rx_polarity(node, "usb-ss", BIT(PHY_POL_AUTO), ++ PHY_POL_AUTO, &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_AUTO); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: same length, name not found, no "default" - error */ ++static void phy_test_rx_polarity_name_not_found_no_default(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const rx_pol_names[] = { "2500base-x", "1000base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: same length, name not found, but "default" exists */ ++static void phy_test_rx_polarity_name_not_found_with_default(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const rx_pol_names[] = { "2500base-x", "default" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: polarity found but value is unsupported */ ++static void phy_test_rx_polarity_unsupported_value(struct kunit *test) ++{ ++ static const u32 rx_pol[] = { PHY_POL_AUTO }; ++ static const char * const rx_pol_names[] = { "sgmii" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_rx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: tx-polarity property is missing */ ++static void phy_test_tx_polarity_is_missing(struct kunit *test) ++{ ++ static const struct property_entry entries[] = { ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: tx-polarity has more values than tx-polarity-names */ ++static void phy_test_tx_polarity_more_values_than_names(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL }; ++ static const char * const tx_pol_names[] = { "sgmii", "2500base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: tx-polarity has 1 value and tx-polarity-names does not exist */ ++static void phy_test_tx_polarity_single_value_no_names(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_INVERT }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: tx-polarity-names has more values than tx-polarity */ ++static void phy_test_tx_polarity_more_names_than_values(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: tx-polarity and tx-polarity-names have same length, find the name */ ++static void phy_test_tx_polarity_find_by_name(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL }; ++ static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL); ++ ++ ret = phy_get_manual_tx_polarity(node, "2500base-x", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ ret = phy_get_manual_tx_polarity(node, "1000base-x", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: same length, name not found, no "default" - error */ ++static void phy_test_tx_polarity_name_not_found_no_default(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const tx_pol_names[] = { "2500base-x", "1000base-x" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EINVAL); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: same length, name not found, but "default" exists */ ++static void phy_test_tx_polarity_name_not_found_with_default(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT }; ++ static const char * const tx_pol_names[] = { "2500base-x", "default" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, 0); ++ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT); ++ ++ fwnode_remove_software_node(node); ++} ++ ++/* Test: polarity found but value is unsupported (AUTO for TX) */ ++static void phy_test_tx_polarity_unsupported_value(struct kunit *test) ++{ ++ static const u32 tx_pol[] = { PHY_POL_AUTO }; ++ static const char * const tx_pol_names[] = { "sgmii" }; ++ static const struct property_entry entries[] = { ++ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol), ++ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names), ++ {} ++ }; ++ struct fwnode_handle *node; ++ unsigned int val; ++ int ret; ++ ++ node = fwnode_create_software_node(entries, NULL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); ++ ++ ret = phy_get_manual_tx_polarity(node, "sgmii", &val); ++ KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP); ++ ++ fwnode_remove_software_node(node); ++} ++ ++static struct kunit_case phy_common_props_test_cases[] = { ++ KUNIT_CASE(phy_test_rx_polarity_is_missing), ++ KUNIT_CASE(phy_test_rx_polarity_more_values_than_names), ++ KUNIT_CASE(phy_test_rx_polarity_single_value_no_names), ++ KUNIT_CASE(phy_test_rx_polarity_more_names_than_values), ++ KUNIT_CASE(phy_test_rx_polarity_find_by_name), ++ KUNIT_CASE(phy_test_rx_polarity_name_not_found_no_default), ++ KUNIT_CASE(phy_test_rx_polarity_name_not_found_with_default), ++ KUNIT_CASE(phy_test_rx_polarity_unsupported_value), ++ KUNIT_CASE(phy_test_tx_polarity_is_missing), ++ KUNIT_CASE(phy_test_tx_polarity_more_values_than_names), ++ KUNIT_CASE(phy_test_tx_polarity_single_value_no_names), ++ KUNIT_CASE(phy_test_tx_polarity_more_names_than_values), ++ KUNIT_CASE(phy_test_tx_polarity_find_by_name), ++ KUNIT_CASE(phy_test_tx_polarity_name_not_found_no_default), ++ KUNIT_CASE(phy_test_tx_polarity_name_not_found_with_default), ++ KUNIT_CASE(phy_test_tx_polarity_unsupported_value), ++ {} ++}; ++ ++static struct kunit_suite phy_common_props_test_suite = { ++ .name = "phy-common-props", ++ .test_cases = phy_common_props_test_cases, ++}; ++ ++kunit_test_suite(phy_common_props_test_suite); ++ ++MODULE_DESCRIPTION("Test module for PHY common properties API"); ++MODULE_AUTHOR("Vladimir Oltean "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/phy/phy-common-props.c +@@ -0,0 +1,209 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * phy-common-props.c -- Common PHY properties ++ * ++ * Copyright 2025-2026 NXP ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * fwnode_get_u32_prop_for_name - Find u32 property by name, or default value ++ * @fwnode: Pointer to firmware node, or NULL to use @default_val ++ * @name: Property name used as lookup key in @names_title (must not be NULL) ++ * @props_title: Name of u32 array property holding values ++ * @names_title: Name of string array property holding lookup keys ++ * @default_val: Default value if @fwnode is NULL or @props_title is empty ++ * @val: Pointer to store the returned value ++ * ++ * This function retrieves a u32 value from @props_title based on a name lookup ++ * in @names_title. The value stored in @val is determined as follows: ++ * ++ * - If @fwnode is NULL or @props_title is empty: @default_val is used ++ * - If @props_title has exactly one element and @names_title is empty: ++ * that element is used ++ * - Otherwise: @val is set to the element at the same index where @name is ++ * found in @names_title. ++ * - If @name is not found, the function looks for a "default" entry in ++ * @names_title and uses the corresponding value from @props_title ++ * ++ * When both @props_title and @names_title are present, they must have the ++ * same number of elements (except when @props_title has exactly one element). ++ * ++ * Return: zero on success, negative error on failure. ++ */ ++static int fwnode_get_u32_prop_for_name(struct fwnode_handle *fwnode, ++ const char *name, ++ const char *props_title, ++ const char *names_title, ++ unsigned int default_val, ++ unsigned int *val) ++{ ++ int err, n_props, n_names, idx; ++ u32 *props; ++ ++ if (!name) { ++ pr_err("Lookup key inside \"%s\" is mandatory\n", names_title); ++ return -EINVAL; ++ } ++ ++ n_props = fwnode_property_count_u32(fwnode, props_title); ++ if (n_props <= 0) { ++ /* fwnode is NULL, or is missing requested property */ ++ *val = default_val; ++ return 0; ++ } ++ ++ n_names = fwnode_property_string_array_count(fwnode, names_title); ++ if (n_names >= 0 && n_props != n_names) { ++ pr_err("%pfw mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n", ++ fwnode, props_title, names_title, n_props, n_names); ++ return -EINVAL; ++ } ++ ++ idx = fwnode_property_match_string(fwnode, names_title, name); ++ if (idx < 0) ++ idx = fwnode_property_match_string(fwnode, names_title, "default"); ++ /* ++ * If the mode name is missing, it can only mean the specified property ++ * is the default one for all modes, so reject any other property count ++ * than 1. ++ */ ++ if (idx < 0 && n_props != 1) { ++ pr_err("%pfw \"%s \" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n", ++ fwnode, props_title, n_props, name, names_title); ++ return -EINVAL; ++ } ++ ++ if (n_props == 1) { ++ err = fwnode_property_read_u32(fwnode, props_title, val); ++ if (err) ++ return err; ++ ++ return 0; ++ } ++ ++ /* We implicitly know idx >= 0 here */ ++ props = kcalloc(n_props, sizeof(*props), GFP_KERNEL); ++ if (!props) ++ return -ENOMEM; ++ ++ err = fwnode_property_read_u32_array(fwnode, props_title, props, n_props); ++ if (err >= 0) ++ *val = props[idx]; ++ ++ kfree(props); ++ ++ return err; ++} ++ ++static int phy_get_polarity_for_mode(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int supported, ++ unsigned int default_val, ++ const char *polarity_prop, ++ const char *names_prop, ++ unsigned int *val) ++{ ++ int err; ++ ++ err = fwnode_get_u32_prop_for_name(fwnode, mode_name, polarity_prop, ++ names_prop, default_val, val); ++ if (err) ++ return err; ++ ++ if (!(supported & BIT(*val))) { ++ pr_err("%d is not a supported value for %pfw '%s' element '%s'\n", ++ *val, fwnode, polarity_prop, mode_name); ++ err = -EOPNOTSUPP; ++ } ++ ++ return err; ++} ++ ++/** ++ * phy_get_rx_polarity - Get RX polarity for PHY differential lane ++ * @fwnode: Pointer to the PHY's firmware node. ++ * @mode_name: The name of the PHY mode to look up. ++ * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO ++ * @default_val: Default polarity value if property is missing ++ * @val: Pointer to returned polarity. ++ * ++ * Return: zero on success, negative error on failure. ++ */ ++int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int supported, ++ unsigned int default_val, ++ unsigned int *val) ++{ ++ return phy_get_polarity_for_mode(fwnode, mode_name, supported, ++ default_val, "rx-polarity", ++ "rx-polarity-names", val); ++} ++EXPORT_SYMBOL_GPL(phy_get_rx_polarity); ++ ++/** ++ * phy_get_tx_polarity - Get TX polarity for PHY differential lane ++ * @fwnode: Pointer to the PHY's firmware node. ++ * @mode_name: The name of the PHY mode to look up. ++ * @supported: Bit mask of PHY_POL_NORMAL and PHY_POL_INVERT ++ * @default_val: Default polarity value if property is missing ++ * @val: Pointer to returned polarity. ++ * ++ * Return: zero on success, negative error on failure. ++ */ ++int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, unsigned int supported, ++ unsigned int default_val, unsigned int *val) ++{ ++ return phy_get_polarity_for_mode(fwnode, mode_name, supported, ++ default_val, "tx-polarity", ++ "tx-polarity-names", val); ++} ++EXPORT_SYMBOL_GPL(phy_get_tx_polarity); ++ ++/** ++ * phy_get_manual_rx_polarity - Get manual RX polarity for PHY differential lane ++ * @fwnode: Pointer to the PHY's firmware node. ++ * @mode_name: The name of the PHY mode to look up. ++ * @val: Pointer to returned polarity. ++ * ++ * Helper for PHYs which do not support protocols with automatic RX polarity ++ * detection and correction. ++ * ++ * Return: zero on success, negative error on failure. ++ */ ++int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int *val) ++{ ++ return phy_get_rx_polarity(fwnode, mode_name, ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ PHY_POL_NORMAL, val); ++} ++EXPORT_SYMBOL_GPL(phy_get_manual_rx_polarity); ++ ++/** ++ * phy_get_manual_tx_polarity - Get manual TX polarity for PHY differential lane ++ * @fwnode: Pointer to the PHY's firmware node. ++ * @mode_name: The name of the PHY mode to look up. ++ * @val: Pointer to returned polarity. ++ * ++ * Helper for PHYs without any custom default value for the TX polarity. ++ * ++ * Return: zero on success, negative error on failure. ++ */ ++int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int *val) ++{ ++ return phy_get_tx_polarity(fwnode, mode_name, ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ PHY_POL_NORMAL, val); ++} ++EXPORT_SYMBOL_GPL(phy_get_manual_tx_polarity); +--- /dev/null ++++ b/include/linux/phy/phy-common-props.h +@@ -0,0 +1,32 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * phy-common-props.h -- Common properties for generic PHYs ++ * ++ * Copyright 2025 NXP ++ */ ++ ++#ifndef __PHY_COMMON_PROPS_H ++#define __PHY_COMMON_PROPS_H ++ ++#include ++ ++struct fwnode_handle; ++ ++int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int supported, ++ unsigned int default_val, ++ unsigned int *val); ++int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int supported, ++ unsigned int default_val, ++ unsigned int *val); ++int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int *val); ++int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode, ++ const char *mode_name, ++ unsigned int *val); ++ ++#endif /* __PHY_COMMON_PROPS_H */ diff --git a/target/linux/generic/backport-6.18/785-v7.0-06-dt-bindings-net-airoha-en8811h-deprecate-airoha-pnsw.patch b/target/linux/generic/backport-6.18/785-v7.0-06-dt-bindings-net-airoha-en8811h-deprecate-airoha-pnsw.patch new file mode 100644 index 00000000000..2dadefb5490 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-06-dt-bindings-net-airoha-en8811h-deprecate-airoha-pnsw.patch @@ -0,0 +1,62 @@ +From 44f62aa1b1209232cedcfb39097fc1bfbe75bbc7 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 11:12:16 +0200 +Subject: [PATCH 1/2] dt-bindings: net: airoha,en8811h: deprecate + "airoha,pnswap-rx" and "airoha,pnswap-tx" + +Reference the common PHY properties, and update the example to use them. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260119091220.1493761-2-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + .../devicetree/bindings/net/airoha,en8811h.yaml | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/airoha,en8811h.yaml ++++ b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml +@@ -16,6 +16,7 @@ description: + + allOf: + - $ref: ethernet-phy.yaml# ++ - $ref: /schemas/phy/phy-common-props.yaml# + + properties: + compatible: +@@ -30,12 +31,18 @@ properties: + description: + Reverse rx polarity of the SERDES. This is the receiving + side of the lines from the MAC towards the EN881H. ++ This property is deprecated, for details please refer to ++ Documentation/devicetree/bindings/phy/phy-common-props.yaml ++ deprecated: true + + airoha,pnswap-tx: + type: boolean + description: + Reverse tx polarity of SERDES. This is the transmitting + side of the lines from EN8811H towards the MAC. ++ This property is deprecated, for details please refer to ++ Documentation/devicetree/bindings/phy/phy-common-props.yaml ++ deprecated: true + + required: + - reg +@@ -44,6 +51,8 @@ unevaluatedProperties: false + + examples: + - | ++ #include ++ + mdio { + #address-cells = <1>; + #size-cells = <0>; +@@ -51,6 +60,6 @@ examples: + ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.a411"; + reg = <1>; +- airoha,pnswap-rx; ++ rx-polarity = ; + }; + }; diff --git a/target/linux/generic/backport-6.18/785-v7.0-07-net-phy-air_en8811h-deprecate-airoha-pnswap-rx-and-a.patch b/target/linux/generic/backport-6.18/785-v7.0-07-net-phy-air_en8811h-deprecate-airoha-pnswap-rx-and-a.patch new file mode 100644 index 00000000000..6109c44c854 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-07-net-phy-air_en8811h-deprecate-airoha-pnswap-rx-and-a.patch @@ -0,0 +1,108 @@ +From 66d8a334b57e64e43810623b3d88f0ce9745270b Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 11:12:17 +0200 +Subject: [PATCH 2/2] net: phy: air_en8811h: deprecate "airoha,pnswap-rx" and + "airoha,pnswap-tx" + +Prefer the new "rx-polarity" and "tx-polarity" properties, and use the +vendor specific ones as fallback if the standard description doesn't +exist. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Maxime Chevallier +Link: https://patch.msgid.link/20260119091220.1493761-3-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/Kconfig | 1 + + drivers/net/phy/air_en8811h.c | 53 +++++++++++++++++++++++++---------- + 2 files changed, 39 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -98,6 +98,7 @@ config AS21XXX_PHY + + config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" ++ select PHY_COMMON_PROPS + help + Currently supports the Airoha EN8811H PHY. + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -966,11 +967,45 @@ static int en8811h_probe(struct phy_devi + return 0; + } + ++static int en8811h_config_serdes_polarity(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ unsigned int pol, default_pol; ++ u32 pbus_value = 0; ++ int ret; ++ ++ default_pol = PHY_POL_NORMAL; ++ if (device_property_read_bool(dev, "airoha,pnswap-rx")) ++ default_pol = PHY_POL_INVERT; ++ ++ ret = phy_get_rx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ default_pol, &pol); ++ if (ret) ++ return ret; ++ if (pol == PHY_POL_INVERT) ++ pbus_value |= EN8811H_POLARITY_RX_REVERSE; ++ ++ default_pol = PHY_POL_NORMAL; ++ if (device_property_read_bool(dev, "airoha,pnswap-tx")) ++ default_pol = PHY_POL_INVERT; ++ ++ ret = phy_get_tx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ default_pol, &pol); ++ if (ret) ++ return ret; ++ if (pol == PHY_POL_NORMAL) ++ pbus_value |= EN8811H_POLARITY_TX_NORMAL; ++ ++ return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, ++ EN8811H_POLARITY_RX_REVERSE | ++ EN8811H_POLARITY_TX_NORMAL, pbus_value); ++} ++ + static int en8811h_config_init(struct phy_device *phydev) + { + struct en8811h_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- u32 pbus_value; + int ret; + + /* If restart happened in .probe(), no need to restart now */ +@@ -1003,19 +1038,7 @@ static int en8811h_config_init(struct ph + if (ret < 0) + return ret; + +- /* Serdes polarity */ +- pbus_value = 0; +- if (device_property_read_bool(dev, "airoha,pnswap-rx")) +- pbus_value |= EN8811H_POLARITY_RX_REVERSE; +- else +- pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; +- if (device_property_read_bool(dev, "airoha,pnswap-tx")) +- pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; +- else +- pbus_value |= EN8811H_POLARITY_TX_NORMAL; +- ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, +- EN8811H_POLARITY_RX_REVERSE | +- EN8811H_POLARITY_TX_NORMAL, pbus_value); ++ ret = en8811h_config_serdes_polarity(phydev); + if (ret < 0) + return ret; + diff --git a/target/linux/generic/backport-6.18/785-v7.0-08-dt-bindings-net-pcs-mediatek-sgmiisys-deprecate-medi.patch b/target/linux/generic/backport-6.18/785-v7.0-08-dt-bindings-net-pcs-mediatek-sgmiisys-deprecate-medi.patch new file mode 100644 index 00000000000..456e00c8a35 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-08-dt-bindings-net-pcs-mediatek-sgmiisys-deprecate-medi.patch @@ -0,0 +1,40 @@ +From 9f841922ebd0a34c78fc1984dc9abcb346704c58 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 11:12:18 +0200 +Subject: [PATCH 1/3] dt-bindings: net: pcs: mediatek,sgmiisys: deprecate + "mediatek,pnswap" + +Reference the common PHY properties, and update the example to use them. +Note that a PCS subnode exists, and it seems a better container of the +polarity description than the SGMIISYS node that hosts "mediatek,pnswap". +So use that. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20260119091220.1493761-4-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + .../devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml ++++ b/Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml +@@ -39,12 +39,17 @@ properties: + const: 1 + + mediatek,pnswap: +- description: Invert polarity of the SGMII data lanes ++ description: ++ Invert polarity of the SGMII data lanes. ++ This property is deprecated, for details please refer to ++ Documentation/devicetree/bindings/phy/phy-common-props.yaml. + type: boolean ++ deprecated: true + + pcs: + type: object + description: MediaTek LynxI HSGMII PCS ++ $ref: /schemas/phy/phy-common-props.yaml# + properties: + compatible: + const: mediatek,mt7988-sgmii diff --git a/target/linux/generic/backport-6.18/785-v7.0-09-net-pcs-pcs-mtk-lynxi-pass-SGMIISYS-OF-node-to-PCS.patch b/target/linux/generic/backport-6.18/785-v7.0-09-net-pcs-pcs-mtk-lynxi-pass-SGMIISYS-OF-node-to-PCS.patch new file mode 100644 index 00000000000..dc5051c72d0 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-09-net-pcs-pcs-mtk-lynxi-pass-SGMIISYS-OF-node-to-PCS.patch @@ -0,0 +1,160 @@ +From bde1ae2d52ab3599e1c7ca68a90af8407d20f91d Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 11:12:19 +0200 +Subject: [PATCH 2/3] net: pcs: pcs-mtk-lynxi: pass SGMIISYS OF node to PCS + +The Mediatek LynxI PCS is used from the MT7530 DSA driver (where it does +not have an OF presence) and from mtk_eth_soc, where it does +(Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml +informs of a combined clock provider + SGMII PCS "SGMIISYS" syscon +block). + +Currently, mtk_eth_soc parses the SGMIISYS OF node for the +"mediatek,pnswap" property and sets a bit in the "flags" argument of +mtk_pcs_lynxi_create() if set. + +I'd like to deprecate "mediatek,pnswap" in favour of a property which +takes the current phy-mode into consideration. But this is only known at +mtk_pcs_lynxi_config() time, and not known at mtk_pcs_lynxi_create(), +when the SGMIISYS OF node is parsed. + +To achieve that, we must pass the OF node of the PCS, if it exists, to +mtk_pcs_lynxi_create(), and let the PCS take a reference on it and +handle property parsing whenever it wants. + +Use the fwnode API which is more general than OF (in case we ever need +to describe the PCS using some other format). This API should be NULL +tolerant, so add no particular tests for the mt7530 case. + +Signed-off-by: Vladimir Oltean +Link: https://patch.msgid.link/20260119091220.1493761-5-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530-mdio.c | 4 ++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 19 ++++++++----------- + drivers/net/pcs/pcs-mtk-lynxi.c | 15 ++++++++++----- + include/linux/pcs/pcs-mtk-lynxi.h | 5 ++--- + 4 files changed, 22 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -113,8 +113,8 @@ mt7531_create_sgmii(struct mt7530_priv * + ret = PTR_ERR(regmap); + break; + } +- pcs = mtk_pcs_lynxi_create(priv->dev, regmap, +- MT7531_PHYA_CTRL_SIGNAL3, 0); ++ pcs = mtk_pcs_lynxi_create(priv->dev, NULL, regmap, ++ MT7531_PHYA_CTRL_SIGNAL3); + if (!pcs) { + ret = -ENXIO; + break; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -5000,7 +5000,6 @@ static int mtk_sgmii_init(struct mtk_eth + { + struct device_node *np; + struct regmap *regmap; +- u32 flags; + int i; + + for (i = 0; i < MTK_MAX_DEVS; i++) { +@@ -5009,18 +5008,16 @@ static int mtk_sgmii_init(struct mtk_eth + break; + + regmap = syscon_node_to_regmap(np); +- flags = 0; +- if (of_property_read_bool(np, "mediatek,pnswap")) +- flags |= MTK_SGMII_FLAG_PN_SWAP; +- +- of_node_put(np); +- +- if (IS_ERR(regmap)) ++ if (IS_ERR(regmap)) { ++ of_node_put(np); + return PTR_ERR(regmap); ++ } + +- eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap, +- eth->soc->ana_rgc3, +- flags); ++ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, ++ of_fwnode_handle(np), ++ regmap, ++ eth->soc->ana_rgc3); ++ of_node_put(np); + } + + return 0; +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -81,6 +81,7 @@ struct mtk_pcs_lynxi { + phy_interface_t interface; + struct phylink_pcs pcs; + u32 flags; ++ struct fwnode_handle *fwnode; + }; + + static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) +@@ -166,7 +167,7 @@ static int mtk_pcs_lynxi_config(struct p + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + +- if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) ++ if (fwnode_property_read_bool(mpcs->fwnode, "mediatek,pnswap")) + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, + SGMII_PN_SWAP_MASK, + SGMII_PN_SWAP_TX_RX); +@@ -266,8 +267,8 @@ static const struct phylink_pcs_ops mtk_ + }; + + struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, +- struct regmap *regmap, u32 ana_rgc3, +- u32 flags) ++ struct fwnode_handle *fwnode, ++ struct regmap *regmap, u32 ana_rgc3) + { + struct mtk_pcs_lynxi *mpcs; + u32 id, ver; +@@ -301,10 +302,10 @@ struct phylink_pcs *mtk_pcs_lynxi_create + + mpcs->ana_rgc3 = ana_rgc3; + mpcs->regmap = regmap; +- mpcs->flags = flags; + mpcs->pcs.ops = &mtk_pcs_lynxi_ops; + mpcs->pcs.poll = true; + mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->fwnode = fwnode_handle_get(fwnode); + + __set_bit(PHY_INTERFACE_MODE_SGMII, mpcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, mpcs->pcs.supported_interfaces); +@@ -316,10 +317,14 @@ EXPORT_SYMBOL(mtk_pcs_lynxi_create); + + void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs) + { ++ struct mtk_pcs_lynxi *mpcs; ++ + if (!pcs) + return; + +- kfree(pcs_to_mtk_pcs_lynxi(pcs)); ++ mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ fwnode_handle_put(mpcs->fwnode); ++ kfree(mpcs); + } + EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); + +--- a/include/linux/pcs/pcs-mtk-lynxi.h ++++ b/include/linux/pcs/pcs-mtk-lynxi.h +@@ -5,9 +5,8 @@ + #include + #include + +-#define MTK_SGMII_FLAG_PN_SWAP BIT(0) + struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, +- struct regmap *regmap, +- u32 ana_rgc3, u32 flags); ++ struct fwnode_handle *fwnode, ++ struct regmap *regmap, u32 ana_rgc3); + void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); + #endif diff --git a/target/linux/generic/backport-6.18/785-v7.0-10-net-pcs-pcs-mtk-lynxi-deprecate-mediatek-pnswap.patch b/target/linux/generic/backport-6.18/785-v7.0-10-net-pcs-pcs-mtk-lynxi-deprecate-mediatek-pnswap.patch new file mode 100644 index 00000000000..6b372cf27de --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-10-net-pcs-pcs-mtk-lynxi-deprecate-mediatek-pnswap.patch @@ -0,0 +1,123 @@ +From 8871389da15165198c3407584d40e7295bceaca5 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 11:12:20 +0200 +Subject: [PATCH 3/3] net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap" + +Prefer the new "rx-polarity" and "tx-polarity" properties, which in this +case have the advantage that polarity inversion can be specified per +direction (and per protocol, although this isn't useful here). + +We use the vendor specific ones as fallback if the standard description +doesn't exist. + +Daniel, referring to the Mediatek SDK, clarifies that the combined +SGMII_PN_SWAP_TX_RX register field should be split like this: bit 0 is +TX and bit 1 is RX: +https://lore.kernel.org/linux-phy/aSW--slbJWpXK0nv@makrotopia.org/ + +Suggested-by: Daniel Golle +Signed-off-by: Vladimir Oltean +Link: https://patch.msgid.link/20260119091220.1493761-6-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/Kconfig | 1 + + drivers/net/pcs/pcs-mtk-lynxi.c | 50 +++++++++++++++++++++++++++++---- + 2 files changed, 45 insertions(+), 6 deletions(-) + +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -20,6 +20,7 @@ config PCS_LYNX + + config PCS_MTK_LYNXI + tristate ++ select PHY_COMMON_PROPS + select REGMAP + help + This module provides helpers to phylink for managing the LynxI PCS +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -62,8 +63,9 @@ + + /* Register to QPHY wrapper control */ + #define SGMSYS_QPHY_WRAP_CTRL 0xec +-#define SGMII_PN_SWAP_MASK GENMASK(1, 0) +-#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) ++#define SGMII_PN_SWAP_RX BIT(1) ++#define SGMII_PN_SWAP_TX BIT(0) ++ + + /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated + * data +@@ -119,6 +121,42 @@ static void mtk_pcs_lynxi_get_state(stru + FIELD_GET(SGMII_LPA, adv)); + } + ++static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs, ++ phy_interface_t interface) ++{ ++ struct fwnode_handle *fwnode = mpcs->fwnode, *pcs_fwnode; ++ unsigned int pol, default_pol = PHY_POL_NORMAL; ++ unsigned int val = 0; ++ int ret; ++ ++ if (fwnode_property_read_bool(fwnode, "mediatek,pnswap")) ++ default_pol = PHY_POL_INVERT; ++ ++ pcs_fwnode = fwnode_get_named_child_node(fwnode, "pcs"); ++ ++ ret = phy_get_rx_polarity(pcs_fwnode, phy_modes(interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ default_pol, &pol); ++ if (ret) { ++ fwnode_handle_put(pcs_fwnode); ++ return ret; ++ } ++ if (pol == PHY_POL_INVERT) ++ val |= SGMII_PN_SWAP_RX; ++ ++ ret = phy_get_tx_polarity(pcs_fwnode, phy_modes(interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ default_pol, &pol); ++ fwnode_handle_put(pcs_fwnode); ++ if (ret) ++ return ret; ++ if (pol == PHY_POL_INVERT) ++ val |= SGMII_PN_SWAP_TX; ++ ++ return regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, ++ SGMII_PN_SWAP_RX | SGMII_PN_SWAP_TX, val); ++} ++ + static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, +@@ -128,6 +166,7 @@ static int mtk_pcs_lynxi_config(struct p + bool mode_changed = false, changed; + unsigned int rgc3, sgm_mode, bmcr; + int advertise, link_timer; ++ int ret; + + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); +@@ -167,10 +206,9 @@ static int mtk_pcs_lynxi_config(struct p + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + +- if (fwnode_property_read_bool(mpcs->fwnode, "mediatek,pnswap")) +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, +- SGMII_PN_SWAP_MASK, +- SGMII_PN_SWAP_TX_RX); ++ ret = mtk_pcs_config_polarity(mpcs, interface); ++ if (ret) ++ return ret; + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = SGMII_PHY_SPEED_3_125G; diff --git a/target/linux/generic/backport-6.18/785-v7.0-11-phy-enter-drivers-phy-Makefile-even-without-CONFIG_G.patch b/target/linux/generic/backport-6.18/785-v7.0-11-phy-enter-drivers-phy-Makefile-even-without-CONFIG_G.patch new file mode 100644 index 00000000000..3ebff437570 --- /dev/null +++ b/target/linux/generic/backport-6.18/785-v7.0-11-phy-enter-drivers-phy-Makefile-even-without-CONFIG_G.patch @@ -0,0 +1,52 @@ +From 3ddcd24b4d8454b2b9b2d013a0d61986ae8bbbe7 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Fri, 23 Jan 2026 13:06:00 +0200 +Subject: [PATCH] phy: enter drivers/phy/Makefile even without + CONFIG_GENERIC_PHY + +Kconfig option CONFIG_PHY_COMMON_PROPS, which builds +drivers/phy/phy-common-props.c, was intended to be selectable +independently of CONFIG_GENERIC_PHY. Yet it lives in drivers/phy/, which +is entered by the Makefile only if CONFIG_GENERIC_PHY is set. + +Allow the Makefile to enter one level deeper, but stop at drivers/phy/ +if CONFIG_GENERIC_PHY is unselected (i.e. do not enter vendor folders). +The other stuff from drivers/phy/Makefile except for CONFIG_PHY_COMMON_PROPS, +like CONFIG_PHY_NXP_PTN3222, all depends on CONFIG_GENERIC_PHY. + +Fixes: e7556b59ba65 ("phy: add phy_get_rx_polarity() and phy_get_tx_polarity()") +Closes: https://lore.kernel.org/lkml/43ea0202-891d-4582-980b-5cb557b41114@linux.ibm.com/ +Reported-by: Venkat Rao Bagalkote +Debugged-by: Christophe Leroy (CS GROUP) +Signed-off-by: Vladimir Oltean +Reviewed-by: Christophe Leroy (CS GROUP) +Tested-by: Venkat Rao Bagalkote +Link: https://patch.msgid.link/20260123110600.3118561-1-vladimir.oltean@nxp.com +Signed-off-by: Vinod Koul +--- + drivers/Makefile | 2 +- + drivers/phy/Makefile | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -10,7 +10,7 @@ obj-y += cache/ + obj-y += irqchip/ + obj-y += bus/ + +-obj-$(CONFIG_GENERIC_PHY) += phy/ ++obj-y += phy/ + + # GPIO must come after pinctrl as gpios may need to mux pins etc + obj-$(CONFIG_PINCTRL) += pinctrl/ +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -15,7 +15,7 @@ obj-$(CONFIG_PHY_SNPS_EUSB2) += phy-snp + obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o + obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o + obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o +-obj-y += allwinner/ \ ++obj-$(CONFIG_GENERIC_PHY) += allwinner/ \ + amlogic/ \ + broadcom/ \ + cadence/ \ diff --git a/target/linux/generic/hack-6.18/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch b/target/linux/generic/hack-6.18/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch index edbf09126f3..9cacc85ea80 100644 --- a/target/linux/generic/hack-6.18/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch +++ b/target/linux/generic/hack-6.18/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch @@ -37,7 +37,7 @@ Signed-off-by: Bo-Cun Chen .glo_cfg = 0x4604, .rst_idx = 0x4608, .delay_irq = 0x460c, -@@ -4176,6 +4179,56 @@ static void mtk_set_mcr_max_rx(struct mt +@@ -4125,6 +4128,56 @@ static void mtk_set_mcr_max_rx(struct mt mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); } @@ -94,7 +94,7 @@ Signed-off-by: Bo-Cun Chen static void mtk_hw_reset(struct mtk_eth *eth) { u32 val; -@@ -4655,6 +4708,8 @@ static void mtk_pending_work(struct work +@@ -4604,6 +4657,8 @@ static void mtk_pending_work(struct work rtnl_lock(); set_bit(MTK_RESETTING, ð->state); @@ -105,7 +105,7 @@ Signed-off-by: Bo-Cun Chen /* Run again reset preliminary configuration in order to avoid any --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1193,6 +1193,7 @@ struct mtk_reg_map { +@@ -1192,6 +1192,7 @@ struct mtk_reg_map { u32 rx_ptr; /* rx base pointer */ u32 rx_cnt_cfg; /* rx max count configuration */ u32 qcrx_ptr; /* rx cpu pointer */ diff --git a/target/linux/generic/hack-6.18/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.18/735-net-phy-realtek-rtl8261n.patch index e5592d99b55..eafa39b5232 100644 --- a/target/linux/generic/hack-6.18/735-net-phy-realtek-rtl8261n.patch +++ b/target/linux/generic/hack-6.18/735-net-phy-realtek-rtl8261n.patch @@ -7,7 +7,7 @@ There is no upstream driver yet. Merge the RTL SDK driver for now. Signed-off-by: John Crispin --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -458,6 +458,8 @@ config QSEMI_PHY +@@ -459,6 +459,8 @@ config QSEMI_PHY source "drivers/net/phy/realtek/Kconfig" diff --git a/target/linux/generic/hack-6.18/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.18/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch deleted file mode 100644 index 24136dd6b45..00000000000 --- a/target/linux/generic/hack-6.18/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Mon, 27 Mar 2023 18:41:54 +0100 -Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN - -Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually -disabling auto-negotiation, e.g. using ethtool. While a proper fix -using SFP quirks is being discussed upstream, bring a work-around to -restore user experience to what it was before the switch to the -dedicated SGMII PCS driver. - -Signed-off-by: Daniel Golle - ---- a/drivers/net/pcs/pcs-mtk-lynxi.c -+++ b/drivers/net/pcs/pcs-mtk-lynxi.c -@@ -129,15 +129,23 @@ static void mtk_pcs_lynxi_get_state(stru - struct phylink_link_state *state) - { - struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); -- unsigned int bm, adv; -+ unsigned int bm, bmsr, adv; - - /* Read the BMSR and LPA */ - regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); -- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); -+ bmsr = FIELD_GET(SGMII_BMSR, bm); -+ -+ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { -+ state->link = !!(bmsr & BMSR_LSTATUS); -+ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); -+ state->speed = SPEED_2500; -+ state->duplex = DUPLEX_FULL; -+ -+ return; -+ } - -- phylink_mii_c22_pcs_decode_state(state, neg_mode, -- FIELD_GET(SGMII_BMSR, bm), -- FIELD_GET(SGMII_LPA, adv)); -+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); -+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, FIELD_GET(SGMII_LPA, adv)); - } - - static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) -@@ -158,7 +166,7 @@ static int mtk_pcs_lynxi_config(struct p - { - struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); - bool mode_changed = false, changed; -- unsigned int rgc3, sgm_mode, bmcr; -+ unsigned int rgc3, sgm_mode, bmcr = 0; - int advertise, link_timer; - - advertise = phylink_mii_c22_pcs_encode_advertisement(interface, -@@ -181,9 +189,8 @@ static int mtk_pcs_lynxi_config(struct p - if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { - if (interface == PHY_INTERFACE_MODE_SGMII) - sgm_mode |= SGMII_SPEED_DUPLEX_AN; -- bmcr = BMCR_ANENABLE; -- } else { -- bmcr = 0; -+ if (interface != PHY_INTERFACE_MODE_2500BASEX) -+ bmcr = BMCR_ANENABLE; - } - - if (mpcs->interface != interface) { diff --git a/target/linux/generic/pending-6.18/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.18/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch index 4b29cc00100..1c9e5bac214 100644 --- a/target/linux/generic/pending-6.18/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch +++ b/target/linux/generic/pending-6.18/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -5298,6 +5298,8 @@ static int mtk_probe(struct platform_dev +@@ -5295,6 +5295,8 @@ static int mtk_probe(struct platform_dev dev_err(eth->dev, "failed to allocated dummy device\n"); goto err_unreg_netdev; } diff --git a/target/linux/generic/pending-6.18/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch b/target/linux/generic/pending-6.18/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch index 5823744ff44..d7e29833fe1 100644 --- a/target/linux/generic/pending-6.18/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch +++ b/target/linux/generic/pending-6.18/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch @@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -2279,7 +2279,7 @@ int phylink_fwnode_phy_connect(struct ph +@@ -2278,7 +2278,7 @@ int phylink_fwnode_phy_connect(struct ph { struct fwnode_handle *phy_fwnode; struct phy_device *phy_dev; @@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle /* Fixed links and 802.3z are handled without needing a PHY */ if (pl->cfg_link_an_mode == MLO_AN_FIXED || -@@ -2309,6 +2309,25 @@ int phylink_fwnode_phy_connect(struct ph +@@ -2308,6 +2308,25 @@ int phylink_fwnode_phy_connect(struct ph if (pl->config->mac_requires_rxc) flags |= PHY_F_RXC_ALWAYS_ON; diff --git a/target/linux/generic/pending-6.18/707-net-pcs-pcs-mtk-lynxi-fix-bpi-r3-serdes-configuratio.patch b/target/linux/generic/pending-6.18/707-net-pcs-pcs-mtk-lynxi-fix-bpi-r3-serdes-configuratio.patch new file mode 100644 index 00000000000..dd2c60d7233 --- /dev/null +++ b/target/linux/generic/pending-6.18/707-net-pcs-pcs-mtk-lynxi-fix-bpi-r3-serdes-configuratio.patch @@ -0,0 +1,37 @@ +From 7289d42b8fd17713531c0f7e8ae7653d1f2c6f8f Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Thu, 9 Apr 2026 15:33:42 +0200 +Subject: [PATCH] net: pcs: pcs-mtk-lynxi: fix bpi-r3 serdes configuration + +Commit 8871389da151 introduces common pcs dts properties which writes +rx=normal,tx=normal polarity to register SGMSYS_QPHY_WRAP_CTRL of switch. +This is initialized with tx-bit set and so change inverts polarity +compared to before. + +It looks like mt7531 has tx polarity inverted in hardware and set tx-bit +by default to restore the normal polarity. + +Till this patch the register write was only called when mediatek,pnswap +property was set which cannot be done for switch because the fw-node param +was always NULL from switch driver in the mtk_pcs_lynxi_create call. + +Do not configure switch side like it's done before. + +Fixes: 8871389da151 ("net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"") +Signed-off-by: Frank Wunderlich +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struc + unsigned int val = 0; + int ret; + ++ if (!fwnode) ++ return 0; ++ + if (fwnode_property_read_bool(fwnode, "mediatek,pnswap")) + default_pol = PHY_POL_INVERT; + diff --git a/target/linux/generic/pending-6.18/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch b/target/linux/generic/pending-6.18/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch index 34b4e544a2a..d8595e4dfd1 100644 --- a/target/linux/generic/pending-6.18/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch +++ b/target/linux/generic/pending-6.18/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch @@ -291,7 +291,7 @@ Signed-off-by: Felix Fietkau eth->scratch_ring, eth->phy_scratch_ring, true); eth->scratch_ring = NULL; -@@ -5386,6 +5390,9 @@ static void mtk_remove(struct platform_d +@@ -5383,6 +5387,9 @@ static void mtk_remove(struct platform_d mtk_mdio_cleanup(eth); } @@ -301,7 +301,7 @@ Signed-off-by: Felix Fietkau static const struct mtk_soc_data mt2701_data = { .reg_map = &mtk_reg_map, .caps = MT7623_CAPS | MTK_HWLRO, -@@ -5394,14 +5401,14 @@ static const struct mtk_soc_data mt2701_ +@@ -5391,14 +5398,14 @@ static const struct mtk_soc_data mt2701_ .required_pctl = true, .version = 1, .tx = { @@ -318,7 +318,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, .dma_size = MTK_DMA_SIZE(2K), -@@ -5422,14 +5429,14 @@ static const struct mtk_soc_data mt7621_ +@@ -5419,14 +5426,14 @@ static const struct mtk_soc_data mt7621_ .hash_offset = 2, .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, .tx = { @@ -335,7 +335,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, .dma_size = MTK_DMA_SIZE(2K), -@@ -5452,14 +5459,14 @@ static const struct mtk_soc_data mt7622_ +@@ -5449,14 +5456,14 @@ static const struct mtk_soc_data mt7622_ .has_accounting = true, .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, .tx = { @@ -352,7 +352,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, .dma_size = MTK_DMA_SIZE(2K), -@@ -5481,14 +5488,14 @@ static const struct mtk_soc_data mt7623_ +@@ -5478,14 +5485,14 @@ static const struct mtk_soc_data mt7623_ .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, .disable_pll_modes = true, .tx = { @@ -369,7 +369,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, .dma_size = MTK_DMA_SIZE(2K), -@@ -5507,14 +5514,14 @@ static const struct mtk_soc_data mt7629_ +@@ -5504,14 +5511,14 @@ static const struct mtk_soc_data mt7629_ .has_accounting = true, .version = 1, .tx = { @@ -386,7 +386,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, .dma_size = MTK_DMA_SIZE(2K), -@@ -5537,14 +5544,14 @@ static const struct mtk_soc_data mt7981_ +@@ -5534,14 +5541,14 @@ static const struct mtk_soc_data mt7981_ .has_accounting = true, .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE, .tx = { @@ -403,7 +403,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, -@@ -5567,14 +5574,14 @@ static const struct mtk_soc_data mt7986_ +@@ -5564,14 +5571,14 @@ static const struct mtk_soc_data mt7986_ .has_accounting = true, .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE, .tx = { @@ -420,7 +420,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, -@@ -5597,14 +5604,14 @@ static const struct mtk_soc_data mt7988_ +@@ -5594,14 +5601,14 @@ static const struct mtk_soc_data mt7988_ .has_accounting = true, .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE, .tx = { @@ -437,7 +437,7 @@ Signed-off-by: Felix Fietkau .irq_done_mask = MTK_RX_DONE_INT_V2, .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, -@@ -5621,13 +5628,13 @@ static const struct mtk_soc_data rt5350_ +@@ -5618,13 +5625,13 @@ static const struct mtk_soc_data rt5350_ .required_pctl = false, .version = 1, .tx = { diff --git a/target/linux/generic/pending-6.18/737-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch b/target/linux/generic/pending-6.18/737-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch new file mode 100644 index 00000000000..f0a4d5306c3 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch @@ -0,0 +1,103 @@ +From c6f6cb55c3c316f6169e07eacc5ccb214116719a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 15:40:12 +0200 +Subject: [PATCH 1/7] net: phylink: keep and use MAC supported_interfaces in + phylink struct + +Add in phylink struct a copy of supported_interfaces from phylink_config +and make use of that instead of relying on phylink_config value. + +This in preparation for support of PCS handling internally to phylink +where a PCS can be removed or added after the phylink is created and we +need both a reference of the supported_interfaces value from +phylink_config and an internal value that can be updated with the new +PCS info. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -59,6 +59,11 @@ struct phylink { + /* The link configuration settings */ + struct phylink_link_state link_config; + ++ /* What interface are supported by the current link. ++ * Can change on removal or addition of new PCS. ++ */ ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ + /* The current settings */ + phy_interface_t cur_interface; + +@@ -623,7 +628,7 @@ static int phylink_validate_mask(struct + static int phylink_validate(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { +- const unsigned long *interfaces = pl->config->supported_interfaces; ++ const unsigned long *interfaces = pl->supported_interfaces; + + if (state->interface == PHY_INTERFACE_MODE_NA) + return phylink_validate_mask(pl, NULL, supported, state, +@@ -1853,6 +1858,9 @@ struct phylink *phylink_create(struct ph + mutex_init(&pl->state_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); + ++ phy_interface_copy(pl->supported_interfaces, ++ config->supported_interfaces); ++ + pl->config = config; + if (config->type == PHYLINK_NETDEV) { + pl->netdev = to_net_dev(config->dev); +@@ -2011,7 +2019,7 @@ static int phylink_validate_phy(struct p + * those which the host supports. + */ + phy_interface_and(interfaces, phy->possible_interfaces, +- pl->config->supported_interfaces); ++ pl->supported_interfaces); + + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "PHY has no common interfaces\n"); +@@ -2753,12 +2761,12 @@ static phy_interface_t phylink_sfp_selec + return interface; + } + +- if (!test_bit(interface, pl->config->supported_interfaces)) { ++ if (!test_bit(interface, pl->supported_interfaces)) { + phylink_err(pl, + "selection of interface failed, SFP selected %s (%u) but MAC supports %*pbl\n", + phy_modes(interface), interface, + (int)PHY_INTERFACE_MODE_MAX, +- pl->config->supported_interfaces); ++ pl->supported_interfaces); + return PHY_INTERFACE_MODE_NA; + } + +@@ -3686,14 +3694,14 @@ static int phylink_sfp_config_optical(st + + phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n", + (int)PHY_INTERFACE_MODE_MAX, +- pl->config->supported_interfaces, ++ pl->supported_interfaces, + (int)PHY_INTERFACE_MODE_MAX, + pl->sfp_interfaces); + + /* Find the union of the supported interfaces by the PCS/MAC and + * the SFP module. + */ +- phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces, ++ phy_interface_and(pl->sfp_interfaces, pl->supported_interfaces, + pl->sfp_interfaces); + if (phy_interface_empty(pl->sfp_interfaces)) { + phylink_err(pl, "unsupported SFP module: no common interface modes\n"); +@@ -3864,7 +3872,7 @@ static int phylink_sfp_connect_phy(void + + /* Set the PHY's host supported interfaces */ + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, +- pl->config->supported_interfaces); ++ pl->supported_interfaces); + + /* Do the initial configuration */ + return phylink_sfp_config_phy(pl, phy); diff --git a/target/linux/generic/pending-6.18/737-02-net-phylink-introduce-internal-phylink-PCS-handling.patch b/target/linux/generic/pending-6.18/737-02-net-phylink-introduce-internal-phylink-PCS-handling.patch new file mode 100644 index 00000000000..cbb0a507410 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-02-net-phylink-introduce-internal-phylink-PCS-handling.patch @@ -0,0 +1,384 @@ +From d134e22b540226a7404cabb88c86a54857486b4f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 16:03:26 +0200 +Subject: [PATCH 2/7] net: phylink: introduce internal phylink PCS handling + +Introduce internal handling of PCS for phylink. This is an alternative +to .mac_select_pcs that moves the selection logic of the PCS entirely to +phylink with the usage of supported_interface value in the PCS struct. + +MAC should now provide an array of available PCS in phylink_config in +.available_pcs and fill the .num_available_pcs with the number of +elements in the array. MAC should also define a new bitmap, +pcs_interfaces, in phylink_config to define for what interface mode a +dedicated PCS is required. + +On phylink_create() this array is parsed and a linked list of PCS is +created based on the PCS passed in phylink_config. +Also the supported_interface value in phylink struct is updated with the +new supported_interface from the provided PCS. + +On phylink_start() every PCS in phylink PCS list gets attached to the +phylink instance. This is done by setting the phylink value in +phylink_pcs struct to the phylink instance. + +On phylink_stop(), every PCS in phylink PCS list is detached from the +phylink instance. This is done by setting the phylink value in +phylink_pcs struct to NULL. + +On phylink_stop(), every PCS in phylink PCS list is removed from the +list. + +phylink_validate_mac_and_pcs(), phylink_major_config() and +phylink_inband_caps() are updated to support this new implementation +with the PCS list stored in phylink. + +They will make use of phylink_validate_pcs_interface() that will loop +for every PCS in the phylink PCS available list and find one that supports +the passed interface. + +phylink_validate_pcs_interface() apply the same logic of .mac_select_pcs +where if a supported_interface value is not set for the PCS struct, then +it's assumed every interface is supported. + +It's required for a MAC that implement either a .mac_select_pcs or make +use of the PCS list implementation. Implementing both will result in a fail +on MAC/PCS validation. + +phylink value in phylink_pcs struct with this implementation is used to +track from PCS side when it's attached to a phylink instance. PCS driver +will make use of this information to correctly detach from a phylink +instance if needed. + +The .mac_select_pcs implementation is not changed but it's expected that +every MAC driver migrates to the new implementation to later deprecate +and remove .mac_select_pcs. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 149 +++++++++++++++++++++++++++++++++----- + include/linux/phylink.h | 11 +++ + 2 files changed, 141 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -59,6 +59,9 @@ struct phylink { + /* The link configuration settings */ + struct phylink_link_state link_config; + ++ /* List of available PCS */ ++ struct list_head pcs_list; ++ + /* What interface are supported by the current link. + * Can change on removal or addition of new PCS. + */ +@@ -149,6 +152,8 @@ static const phy_interface_t phylink_sfp + + static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); + ++static void phylink_run_resolve(struct phylink *pl); ++ + /** + * phylink_set_port_modes() - set the port type modes in the ethtool mask + * @mask: ethtool link mode mask +@@ -512,22 +517,59 @@ static void phylink_validate_mask_caps(u + linkmode_and(state->advertising, state->advertising, mask); + } + ++static int phylink_validate_pcs_interface(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ /* If PCS define an empty supported_interfaces value, assume ++ * all interface are supported. ++ */ ++ if (phy_interface_empty(pcs->supported_interfaces)) ++ return 0; ++ ++ /* Ensure that this PCS supports the interface mode */ ++ if (!test_bit(interface, pcs->supported_interfaces)) ++ return -EINVAL; ++ ++ return 0; ++} ++ + static int phylink_validate_mac_and_pcs(struct phylink *pl, + unsigned long *supported, + struct phylink_link_state *state) + { +- struct phylink_pcs *pcs = NULL; + unsigned long capabilities; ++ struct phylink_pcs *pcs; ++ bool pcs_found = false; + int ret; + + /* Get the PCS for this interface mode */ + if (pl->mac_ops->mac_select_pcs) { ++ /* Make sure either PCS internal validation or .mac_select_pcs ++ * is used. Return error if both are defined. ++ */ ++ if (!list_empty(&pl->pcs_list)) { ++ phylink_err(pl, "either phylink_pcs_add() or .mac_select_pcs must be used\n"); ++ return -EINVAL; ++ } ++ + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); ++ ++ pcs_found = !!pcs; ++ } else { ++ /* Check every assigned PCS and search for one that supports ++ * the interface. ++ */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, state->interface)) { ++ pcs_found = true; ++ break; ++ } ++ } + } + +- if (pcs) { ++ if (pcs_found) { + /* The PCS, if present, must be setup before phylink_create() + * has been called. If the ops is not initialised, print an + * error and backtrace rather than oopsing the kernel. +@@ -539,13 +581,10 @@ static int phylink_validate_mac_and_pcs( + return -EINVAL; + } + +- /* Ensure that this PCS supports the interface which the MAC +- * returned it for. It is an error for the MAC to return a PCS +- * that does not support the interface mode. +- */ +- if (!phy_interface_empty(pcs->supported_interfaces) && +- !test_bit(state->interface, pcs->supported_interfaces)) { +- phylink_err(pl, "MAC returned PCS which does not support %s\n", ++ /* Recheck PCS to handle legacy way for .mac_select_pcs */ ++ ret = phylink_validate_pcs_interface(pcs, state->interface); ++ if (ret) { ++ phylink_err(pl, "selected PCS does not support %s\n", + phy_modes(state->interface)); + return -EINVAL; + } +@@ -959,12 +998,22 @@ static unsigned int phylink_inband_caps( + phy_interface_t interface) + { + struct phylink_pcs *pcs; ++ bool pcs_found = false; + +- if (!pl->mac_ops->mac_select_pcs) +- return 0; ++ if (pl->mac_ops->mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, ++ interface); ++ pcs_found = !!pcs; ++ } else { ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, interface)) { ++ pcs_found = true; ++ break; ++ } ++ } ++ } + +- pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); +- if (!pcs) ++ if (!pcs_found) + return 0; + + return phylink_pcs_inband_caps(pcs, interface); +@@ -1259,10 +1308,36 @@ static void phylink_major_config(struct + pl->major_config_failed = true; + return; + } ++ /* Find a PCS in available PCS list for the requested interface. ++ * This doesn't overwrite the previous .mac_select_pcs as either ++ * .mac_select_pcs or PCS list implementation are permitted. ++ * ++ * Skip searching if the MAC doesn't require a dedicaed PCS for ++ * the requested interface. ++ */ ++ } else if (test_bit(state->interface, pl->config->pcs_interfaces)) { ++ bool pcs_found = false; ++ ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, ++ state->interface)) { ++ pcs_found = true; ++ break; ++ } ++ } ++ ++ if (!pcs_found) { ++ phylink_err(pl, ++ "couldn't find a PCS for %s\n", ++ phy_modes(state->interface)); + +- pcs_changed = pl->pcs != pcs; ++ pl->major_config_failed = true; ++ return; ++ } + } + ++ pcs_changed = pl->pcs != pcs; ++ + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); + + phylink_dbg(pl, "major config, active %s/%s/%s\n", +@@ -1289,11 +1364,13 @@ static void phylink_major_config(struct + if (pcs_changed) { + phylink_pcs_disable(pl->pcs); + +- if (pl->pcs) +- pl->pcs->phylink = NULL; ++ if (pl->mac_ops->mac_select_pcs) { ++ if (pl->pcs) ++ pl->pcs->phylink = NULL; + +- if (pcs) +- pcs->phylink = pl; ++ if (pcs) ++ pcs->phylink = pl; ++ } + + pl->pcs = pcs; + } +@@ -1840,8 +1917,9 @@ struct phylink *phylink_create(struct ph + phy_interface_t iface, + const struct phylink_mac_ops *mac_ops) + { ++ struct phylink_pcs *pcs; + struct phylink *pl; +- int ret; ++ int i, ret; + + /* Validate the supplied configuration */ + if (phy_interface_empty(config->supported_interfaces)) { +@@ -1857,9 +1935,21 @@ struct phylink *phylink_create(struct ph + mutex_init(&pl->phydev_mutex); + mutex_init(&pl->state_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); ++ INIT_LIST_HEAD(&pl->pcs_list); ++ ++ /* Fill the PCS list with available PCS from phylink config */ ++ for (i = 0; i < config->num_available_pcs; i++) { ++ pcs = config->available_pcs[i]; ++ ++ list_add(&pcs->list, &pl->pcs_list); ++ } + + phy_interface_copy(pl->supported_interfaces, + config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); + + pl->config = config; + if (config->type == PHYLINK_NETDEV) { +@@ -1938,10 +2028,16 @@ EXPORT_SYMBOL_GPL(phylink_create); + */ + void phylink_destroy(struct phylink *pl) + { ++ struct phylink_pcs *pcs, *tmp; ++ + sfp_bus_del_upstream(pl->sfp_bus); + if (pl->link_gpio) + gpiod_put(pl->link_gpio); + ++ /* Remove every PCS from phylink PCS list */ ++ list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) ++ list_del(&pcs->list); ++ + cancel_work_sync(&pl->resolve); + kfree(pl); + } +@@ -2443,6 +2539,7 @@ static irqreturn_t phylink_link_handler( + */ + void phylink_start(struct phylink *pl) + { ++ struct phylink_pcs *pcs; + bool poll = false; + + ASSERT_RTNL(); +@@ -2469,6 +2566,10 @@ void phylink_start(struct phylink *pl) + + pl->pcs_state = PCS_STATE_STARTED; + ++ /* link available PCS to phylink struct */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ pcs->phylink = pl; ++ + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); + + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { +@@ -2513,6 +2614,8 @@ EXPORT_SYMBOL_GPL(phylink_start); + */ + void phylink_stop(struct phylink *pl) + { ++ struct phylink_pcs *pcs; ++ + ASSERT_RTNL(); + + if (pl->sfp_bus) +@@ -2530,6 +2633,14 @@ void phylink_stop(struct phylink *pl) + pl->pcs_state = PCS_STATE_DOWN; + + phylink_pcs_disable(pl->pcs); ++ ++ /* Drop link between phylink and PCS */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ pcs->phylink = NULL; ++ ++ /* Restore original supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); + } + EXPORT_SYMBOL_GPL(phylink_stop); + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -150,12 +150,16 @@ enum phylink_op_type { + * if MAC link is at %MLO_AN_FIXED mode. + * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx + * are supported by the MAC/PCS. ++ * @pcs_interfaces: bitmap describing for which PHY_INTERFACE_MODE_xxx a ++ * dedicated PCS is required. + * @lpi_interfaces: bitmap describing which PHY interface modes can support + * LPI signalling. + * @mac_capabilities: MAC pause/speed/duplex capabilities. + * @lpi_capabilities: MAC speeds which can support LPI signalling + * @lpi_timer_default: Default EEE LPI timer setting. + * @eee_enabled_default: If set, EEE will be enabled by phylink at creation time ++ * @available_pcs: array of available phylink_pcs PCS ++ * @num_available_pcs: num of available phylink_pcs PCS + */ + struct phylink_config { + struct device *dev; +@@ -168,11 +172,15 @@ struct phylink_config { + void (*get_fixed_state)(struct phylink_config *config, + struct phylink_link_state *state); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ DECLARE_PHY_INTERFACE_MASK(pcs_interfaces); + DECLARE_PHY_INTERFACE_MASK(lpi_interfaces); + unsigned long mac_capabilities; + unsigned long lpi_capabilities; + u32 lpi_timer_default; + bool eee_enabled_default; ++ ++ struct phylink_pcs **available_pcs; ++ unsigned int num_available_pcs; + }; + + void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); +@@ -468,6 +476,9 @@ struct phylink_pcs { + struct phylink *phylink; + bool poll; + bool rxc_always_on; ++ ++ /* private: */ ++ struct list_head list; + }; + + /** diff --git a/target/linux/generic/pending-6.18/737-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch b/target/linux/generic/pending-6.18/737-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch new file mode 100644 index 00000000000..e884e70f286 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch @@ -0,0 +1,135 @@ +From 1cb4e56c3ba32ac1bce89dc9c34ef2dbc9b89ad4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 19:10:24 +0200 +Subject: [PATCH 3/7] net: phylink: add phylink_release_pcs() to externally + release a PCS + +Add phylink_release_pcs() to externally release a PCS from a phylink +instance. This can be used to handle case when a single PCS needs to be +removed and the phylink instance needs to be refreshed. + +On calling phylink_release_pcs(), the PCS will be removed from the +phylink internal PCS list and the phylink supported_interfaces value is +reparsed with the remaining PCS interfaces. + +Also a phylink resolve is triggered to handle the PCS removal. + +It's also added to phylink a flag to make phylink resolve reconfigure +the interface mode (even if it didn't change). This is needed to handle +the special case when the current PCS used by phylink is removed and a +major_config is needed to propagae the configuration change. With this +option enabled we also force mac_config even if the PHY link is not up +for the in-band case. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++- + include/linux/phylink.h | 2 ++ + 2 files changed, 58 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -86,6 +86,7 @@ struct phylink { + bool link_failed; + bool suspend_link_up; + bool major_config_failed; ++ bool reconfig_interface; + bool mac_supports_eee_ops; + bool mac_supports_eee; + bool phy_enable_tx_lpi; +@@ -917,6 +918,55 @@ static void phylink_resolve_an_pause(str + } + } + ++/** ++ * phylink_release_pcs - Removes a PCS from the phylink PCS available list ++ * @pcs: a pointer to the phylink_pcs struct to be released ++ * ++ * This function release a PCS from the phylink PCS available list if ++ * actually in use. It also refreshes the supported interfaces of the ++ * phylink instance by copying the supported interfaces from the phylink ++ * conf and merging the supported interfaces of the remaining available PCS ++ * in the list and trigger a resolve. ++ */ ++void phylink_release_pcs(struct phylink_pcs *pcs) ++{ ++ struct phylink *pl; ++ ++ ASSERT_RTNL(); ++ ++ pl = pcs->phylink; ++ if (!pl) ++ return; ++ ++ list_del(&pcs->list); ++ pcs->phylink = NULL; ++ ++ /* Check if we are removing the PCS currently ++ * in use by phylink. If this is the case, ++ * force phylink resolve to reconfigure the interface ++ * mode and set the phylink PCS to NULL. ++ */ ++ if (pl->pcs == pcs) { ++ mutex_lock(&pl->state_mutex); ++ ++ pl->reconfig_interface = true; ++ pl->pcs = NULL; ++ ++ mutex_unlock(&pl->state_mutex); ++ } ++ ++ /* Refresh supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); ++ ++ phylink_run_resolve(pl); ++} ++EXPORT_SYMBOL_GPL(phylink_release_pcs); ++ + static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) + { +@@ -1730,6 +1780,10 @@ static void phylink_resolve(struct work_ + if (phy) + link_state.link &= pl->phy_state.link; + ++ /* Force mac_config if we need to reconfig the interface */ ++ if (pl->reconfig_interface) ++ mac_config = true; ++ + /* Only update if the PHY link is up */ + if (phy && pl->phy_state.link) { + /* If the interface has changed, force a link down +@@ -1763,7 +1817,8 @@ static void phylink_resolve(struct work_ + if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + +- if (mac_config && link_state.interface != pl->link_config.interface) { ++ if ((mac_config && link_state.interface != pl->link_config.interface) || ++ pl->reconfig_interface) { + /* The interface has changed, so force the link down and then + * reconfigure. + */ +@@ -1773,6 +1828,7 @@ static void phylink_resolve(struct work_ + } + phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; ++ pl->reconfig_interface = false; + } + + /* If configuration of the interface failed, force the link down +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -717,6 +717,8 @@ void phylink_disconnect_phy(struct phyli + int phylink_set_fixed_link(struct phylink *, + const struct phylink_link_state *); + ++void phylink_release_pcs(struct phylink_pcs *pcs); ++ + void phylink_mac_change(struct phylink *, bool up); + void phylink_pcs_change(struct phylink_pcs *, bool up); + diff --git a/target/linux/generic/pending-6.18/737-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch b/target/linux/generic/pending-6.18/737-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch new file mode 100644 index 00000000000..676e23c0019 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch @@ -0,0 +1,384 @@ +From a90c644c73bbffd400cd3839fc17ffdfc69ea1e8 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 17 Mar 2025 01:46:53 +0100 +Subject: [PATCH 4/7] net: pcs: implement Firmware node support for PCS driver + +Implement the foundation of Firmware node support for PCS driver. + +To support this, implement a simple Provider API where a PCS driver can +expose multiple PCS with an xlate .get function. + +PCS driver will have to call fwnode_pcs_add_provider() and pass the +firmware node pointer and a xlate function to return the correct PCS for +the passed #pcs-cells. + +This will register the PCS in a global list of providers so that +consumer can access it. + +Consumer will then use fwnode_pcs_get() to get the actual PCS by passing +the firmware node pointer and the index for #pcs-cells. + +For simple implementation where #pcs-cells is 0 and the PCS driver +expose a single PCS, the xlate function fwnode_pcs_simple_get() is +provided. + +For advanced implementation a custom xlate function is required. + +PCS driver on removal should first delete as a provider with +the usage of fwnode_pcs_del_provider() and then call +phylink_release_pcs() on every PCS the driver provides and + +A generic function fwnode_phylink_pcs_parse() is provided for any MAC +driver that will declare PCS in DT (or ACPI). +This function will parse "pcs-handle" property and fill the passed array +with the parsed PCS in availabel_pcs up to the passed num_pcs value. +It's also possible to pass NULL as array to only parse the PCS and +update the num_pcs value with the count of scanned PCS. + +Co-developed-by: Daniel Golle +Signed-off-by: Daniel Golle +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/Kconfig | 7 ++ + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs.c | 201 +++++++++++++++++++++++++++++++ + include/linux/pcs/pcs-provider.h | 41 +++++++ + include/linux/pcs/pcs.h | 56 +++++++++ + 5 files changed, 306 insertions(+) + create mode 100644 drivers/net/pcs/pcs.c + create mode 100644 include/linux/pcs/pcs-provider.h + create mode 100644 include/linux/pcs/pcs.h + +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -5,6 +5,13 @@ + + menu "PCS device drivers" + ++config FWNODE_PCS ++ tristate ++ depends on (ACPI || OF) ++ depends on PHYLINK ++ help ++ Firmware node PCS accessors ++ + config PCS_XPCS + tristate "Synopsys DesignWare Ethernet XPCS" + select PHYLINK +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux PCS drivers + ++obj-$(CONFIG_FWNODE_PCS) += pcs.o + pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o \ + pcs-xpcs-nxp.o pcs-xpcs-wx.o + +--- /dev/null ++++ b/drivers/net/pcs/pcs.c +@@ -0,0 +1,201 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("PCS library"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++struct fwnode_pcs_provider { ++ struct list_head link; ++ ++ struct fwnode_handle *fwnode; ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data); ++ ++ void *data; ++}; ++ ++static LIST_HEAD(fwnode_pcs_providers); ++static DEFINE_MUTEX(fwnode_pcs_mutex); ++ ++struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, ++ void *data) ++{ ++ return data; ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_simple_get); ++ ++int fwnode_pcs_add_provider(struct fwnode_handle *fwnode, ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data), ++ void *data) ++{ ++ struct fwnode_pcs_provider *pp; ++ ++ if (!fwnode) ++ return 0; ++ ++ pp = kzalloc(sizeof(*pp), GFP_KERNEL); ++ if (!pp) ++ return -ENOMEM; ++ ++ pp->fwnode = fwnode_handle_get(fwnode); ++ pp->data = data; ++ pp->get = get; ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_add(&pp->link, &fwnode_pcs_providers); ++ mutex_unlock(&fwnode_pcs_mutex); ++ pr_debug("Added pcs provider from %pfwf\n", fwnode); ++ ++ fwnode_dev_initialized(fwnode, true); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider); ++ ++void fwnode_pcs_del_provider(struct fwnode_handle *fwnode) ++{ ++ struct fwnode_pcs_provider *pp; ++ ++ if (!fwnode) ++ return; ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_for_each_entry(pp, &fwnode_pcs_providers, link) { ++ if (pp->fwnode == fwnode) { ++ list_del(&pp->link); ++ fwnode_dev_initialized(pp->fwnode, false); ++ fwnode_handle_put(pp->fwnode); ++ kfree(pp); ++ break; ++ } ++ } ++ mutex_unlock(&fwnode_pcs_mutex); ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_del_provider); ++ ++static int fwnode_parse_pcsspec(const struct fwnode_handle *fwnode, int index, ++ const char *name, ++ struct fwnode_reference_args *out_args) ++{ ++ int ret = -ENOENT; ++ ++ if (!fwnode) ++ return -ENOENT; ++ ++ if (name) ++ index = fwnode_property_match_string(fwnode, "pcs-names", ++ name); ++ ++ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle", ++ "#pcs-cells", ++ -1, index, out_args); ++ if (ret || (name && index < 0)) ++ return ret; ++ ++ return 0; ++} ++ ++static struct phylink_pcs * ++fwnode_pcs_get_from_pcsspec(struct fwnode_reference_args *pcsspec) ++{ ++ struct fwnode_pcs_provider *provider; ++ struct phylink_pcs *pcs = ERR_PTR(-EPROBE_DEFER); ++ ++ if (!pcsspec) ++ return ERR_PTR(-EINVAL); ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_for_each_entry(provider, &fwnode_pcs_providers, link) { ++ if (provider->fwnode == pcsspec->fwnode) { ++ pcs = provider->get(pcsspec, provider->data); ++ if (!IS_ERR(pcs)) ++ break; ++ } ++ } ++ mutex_unlock(&fwnode_pcs_mutex); ++ ++ return pcs; ++} ++ ++static struct phylink_pcs *__fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index, const char *con_id) ++{ ++ struct fwnode_reference_args pcsspec; ++ struct phylink_pcs *pcs; ++ int ret; ++ ++ ret = fwnode_parse_pcsspec(fwnode, index, con_id, &pcsspec); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ pcs = fwnode_pcs_get_from_pcsspec(&pcsspec); ++ fwnode_handle_put(pcsspec.fwnode); ++ ++ return pcs; ++} ++ ++struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, int index) ++{ ++ return __fwnode_pcs_get(fwnode, index, NULL); ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_get); ++ ++static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode, ++ unsigned int *num_pcs) ++{ ++ struct fwnode_reference_args out_args; ++ int index = 0; ++ int ret; ++ ++ while (true) { ++ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle", ++ "#pcs-cells", ++ -1, index, &out_args); ++ /* We expect to reach an -ENOENT error while counting */ ++ if (ret) ++ break; ++ ++ fwnode_handle_put(out_args.fwnode); ++ index++; ++ } ++ ++ /* Update num_pcs with parsed PCS */ ++ *num_pcs = index; ++ ++ /* Return error if we didn't found any PCS */ ++ return index > 0 ? 0 : -ENOENT; ++} ++ ++int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs) ++{ ++ int i; ++ ++ if (!fwnode_property_present(fwnode, "pcs-handle")) ++ return -ENODEV; ++ ++ /* With available_pcs NULL, only count the PCS */ ++ if (!available_pcs) ++ return fwnode_phylink_pcs_count(fwnode, num_pcs); ++ ++ for (i = 0; i < *num_pcs; i++) { ++ struct phylink_pcs *pcs; ++ ++ pcs = fwnode_pcs_get(fwnode, i); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ ++ available_pcs[i] = pcs; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_parse); +--- /dev/null ++++ b/include/linux/pcs/pcs-provider.h +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#ifndef __LINUX_PCS_PROVIDER_H ++#define __LINUX_PCS_PROVIDER_H ++ ++/** ++ * fwnode_pcs_simple_get - Simple xlate function to retrieve PCS ++ * @pcsspec: reference arguments ++ * @data: Context data (assumed assigned to the single PCS) ++ * ++ * Returns the PCS. (pointed by data) ++ */ ++struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, ++ void *data); ++ ++/** ++ * fwnode_pcs_add_provider - Registers a new PCS provider ++ * @np: Firmware node ++ * @get: xlate function to retrieve the PCS ++ * @data: Context data ++ * ++ * Register and add a new PCS to the global providers list ++ * for the firmware node. A function to get the PCS from ++ * firmware node with the use fwnode reference arguments. ++ * To the get function is also passed the interface type ++ * requested for the PHY. PCS driver will use the passed ++ * interface to understand if the PCS can support it or not. ++ * ++ * Returns 0 on success or -ENOMEM on allocation failure. ++ */ ++int fwnode_pcs_add_provider(struct fwnode_handle *fwnode, ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data), ++ void *data); ++ ++/** ++ * fwnode_pcs_del_provider - Removes a PCS provider ++ * @fwnode: Firmware node ++ */ ++void fwnode_pcs_del_provider(struct fwnode_handle *fwnode); ++ ++#endif /* __LINUX_PCS_PROVIDER_H */ +--- /dev/null ++++ b/include/linux/pcs/pcs.h +@@ -0,0 +1,56 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#ifndef __LINUX_PCS_H ++#define __LINUX_PCS_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_FWNODE_PCS) ++/** ++ * fwnode_pcs_get - Retrieves a PCS from a firmware node ++ * @fwnode: firmware node ++ * @index: index fwnode PCS handle in firmware node ++ * ++ * Get a PCS from the firmware node at index. ++ * ++ * Returns a pointer to the phylink_pcs or a negative ++ * error pointer. Can return -EPROBE_DEFER if the PCS is not ++ * present in global providers list (either due to driver ++ * still needs to be probed or it failed to probe/removed) ++ */ ++struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index); ++ ++/** ++ * fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider ++ * @fwnode: firmware node ++ * @available_pcs: pointer to preallocated array of PCS ++ * @num_pcs: where to store count of parsed PCS ++ * ++ * Generic helper function to fill available_pcs array with PCS parsed ++ * from a "pcs-handle" fwnode property defined in firmware node up to ++ * passed num_pcs. ++ * ++ * If available_pcs is NULL, num_pcs is updated with the count of the ++ * parsed PCS. ++ * ++ * Returns 0 or a negative error. ++ */ ++int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs); ++#else ++static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index) ++{ ++ return ERR_PTR(-ENOENT); ++} ++ ++static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif /* __LINUX_PCS_H */ diff --git a/target/linux/generic/pending-6.18/737-05-net-phylink-support-late-PCS-provider-attach.patch b/target/linux/generic/pending-6.18/737-05-net-phylink-support-late-PCS-provider-attach.patch new file mode 100644 index 00000000000..9e8f9721abd --- /dev/null +++ b/target/linux/generic/pending-6.18/737-05-net-phylink-support-late-PCS-provider-attach.patch @@ -0,0 +1,254 @@ +From 684e49a015f2c5ae95ba968bb21ffc8fc36a2c7f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 6 Apr 2025 03:28:22 +0200 +Subject: [PATCH 5/7] net: phylink: support late PCS provider attach + +Add support in phylink for late PCS provider attach to a phylink +instance. This works by creating a global notifier for the PCS provider +and making each phylink instance that makes use of fwnode subscribe to +this notifier. + +The PCS notifier will emit the event FWNODE_PCS_PROVIDER_ADD every time +a new PCS provider is added. + +phylink will then react to this event and will call the new function +fwnode_phylink_pcs_get_from_fwnode() that will check if the PCS fwnode +provided by the event is present in the phy-handle property of the +phylink instance. + +If a related PCS is found, then such PCS is added to the phylink +instance PCS list. + +Then we link the PCS to the phylink instance if it's not disable and we +refresh the supported interfaces of the phylink instance. + +Finally we check if we are in a major_config_failed scenario and trigger +an interface reconfiguration in the next phylink resolve. + +If link was previously torn down due to removal of PCS, the link will be +established again as the PCS came back and is not available to phylink. + +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/pcs.c | 34 +++++++++++++++++++++++++ + drivers/net/phy/phylink.c | 52 +++++++++++++++++++++++++++++++++++++++ + include/linux/pcs/pcs.h | 48 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 134 insertions(+) + +--- a/drivers/net/pcs/pcs.c ++++ b/drivers/net/pcs/pcs.c +@@ -22,6 +22,13 @@ struct fwnode_pcs_provider { + + static LIST_HEAD(fwnode_pcs_providers); + static DEFINE_MUTEX(fwnode_pcs_mutex); ++static BLOCKING_NOTIFIER_HEAD(fwnode_pcs_notify_list); ++ ++int register_fwnode_pcs_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&fwnode_pcs_notify_list, nb); ++} ++EXPORT_SYMBOL_GPL(register_fwnode_pcs_notifier); + + struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, + void *data) +@@ -55,6 +62,10 @@ int fwnode_pcs_add_provider(struct fwnod + + fwnode_dev_initialized(fwnode, true); + ++ blocking_notifier_call_chain(&fwnode_pcs_notify_list, ++ FWNODE_PCS_PROVIDER_ADD, ++ fwnode); ++ + return 0; + } + EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider); +@@ -147,6 +158,29 @@ struct phylink_pcs *fwnode_pcs_get(struc + } + EXPORT_SYMBOL_GPL(fwnode_pcs_get); + ++struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode) ++{ ++ struct fwnode_reference_args pcsspec; ++ int i = 0; ++ int ret; ++ ++ while (true) { ++ ret = fwnode_parse_pcsspec(fwnode, i, NULL, &pcsspec); ++ if (ret) ++ break; ++ ++ if (pcsspec.fwnode == pcs_fwnode) ++ break; ++ ++ i++; ++ } ++ ++ return fwnode_pcs_get(fwnode, i); ++} ++EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_get_from_fwnode); ++ + static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode, + unsigned int *num_pcs) + { +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -61,6 +62,7 @@ struct phylink { + + /* List of available PCS */ + struct list_head pcs_list; ++ struct notifier_block fwnode_pcs_nb; + + /* What interface are supported by the current link. + * Can change on removal or addition of new PCS. +@@ -1952,6 +1954,51 @@ int phylink_set_fixed_link(struct phylin + } + EXPORT_SYMBOL_GPL(phylink_set_fixed_link); + ++static int pcs_provider_notify(struct notifier_block *self, ++ unsigned long val, void *data) ++{ ++ struct phylink *pl = container_of(self, struct phylink, fwnode_pcs_nb); ++ struct fwnode_handle *pcs_fwnode = data; ++ struct phylink_pcs *pcs; ++ ++ /* Check if the just added PCS provider is ++ * in the phylink instance phy-handle property ++ */ ++ pcs = fwnode_phylink_pcs_get_from_fwnode(dev_fwnode(pl->config->dev), ++ pcs_fwnode); ++ if (IS_ERR(pcs)) ++ return NOTIFY_DONE; ++ ++ /* Add the PCS */ ++ rtnl_lock(); ++ ++ list_add(&pcs->list, &pl->pcs_list); ++ ++ /* Link phylink if we are started */ ++ if (!pl->phylink_disable_state) ++ pcs->phylink = pl; ++ ++ /* Refresh supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); ++ ++ mutex_lock(&pl->state_mutex); ++ /* Force an interface reconfig if major config fail */ ++ if (pl->major_config_failed) ++ pl->reconfig_interface = true; ++ mutex_unlock(&pl->state_mutex); ++ ++ rtnl_unlock(); ++ ++ phylink_run_resolve(pl); ++ ++ return NOTIFY_OK; ++} ++ + /** + * phylink_create() - create a phylink instance + * @config: a pointer to the target &struct phylink_config +@@ -2007,6 +2054,11 @@ struct phylink *phylink_create(struct ph + pl->supported_interfaces, + pcs->supported_interfaces); + ++ if (!phy_interface_empty(config->pcs_interfaces)) { ++ pl->fwnode_pcs_nb.notifier_call = pcs_provider_notify; ++ register_fwnode_pcs_notifier(&pl->fwnode_pcs_nb); ++ } ++ + pl->config = config; + if (config->type == PHYLINK_NETDEV) { + pl->netdev = to_net_dev(config->dev); +--- a/include/linux/pcs/pcs.h ++++ b/include/linux/pcs/pcs.h +@@ -4,8 +4,25 @@ + + #include + ++enum fwnode_pcs_notify_event { ++ FWNODE_PCS_PROVIDER_ADD, ++}; ++ + #if IS_ENABLED(CONFIG_FWNODE_PCS) + /** ++ * register_fwnode_pcs_notifier - Register a notifier block for fwnode ++ * PCS events ++ * @nb: pointer to the notifier block ++ * ++ * Registers a notifier block to the fwnode_pcs_notify_list blocking ++ * notifier chain. This allows phylink instance to subscribe for ++ * PCS provider events. ++ * ++ * Returns 0 or a negative error. ++ */ ++int register_fwnode_pcs_notifier(struct notifier_block *nb); ++ ++/** + * fwnode_pcs_get - Retrieves a PCS from a firmware node + * @fwnode: firmware node + * @index: index fwnode PCS handle in firmware node +@@ -21,6 +38,25 @@ struct phylink_pcs *fwnode_pcs_get(struc + int index); + + /** ++ * fwnode_phylink_pcs_get_from_fwnode - Retrieves the PCS provided ++ * by the firmware node from a ++ * firmware node ++ * @fwnode: firmware node ++ * @pcs_fwnode: PCS firmware node ++ * ++ * Parse 'pcs-handle' in 'fwnode' and get the PCS that match ++ * 'pcs_fwnode' firmware node. ++ * ++ * Returns a pointer to the phylink_pcs or a negative ++ * error pointer. Can return -EPROBE_DEFER if the PCS is not ++ * present in global providers list (either due to driver ++ * still needs to be probed or it failed to probe/removed) ++ */ ++struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode); ++ ++/** + * fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider + * @fwnode: firmware node + * @available_pcs: pointer to preallocated array of PCS +@@ -39,11 +75,23 @@ int fwnode_phylink_pcs_parse(struct fwno + struct phylink_pcs **available_pcs, + unsigned int *num_pcs); + #else ++static inline int register_fwnode_pcs_notifier(struct notifier_block *nb) ++{ ++ return -EOPNOTSUPP; ++} ++ + static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, + int index) + { + return ERR_PTR(-ENOENT); + } ++ ++static inline struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode) ++{ ++ return ERR_PTR(-ENOENT); ++} + + static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, + struct phylink_pcs **available_pcs, diff --git a/target/linux/generic/pending-6.18/737-06-dt-bindings-net-ethernet-controller-permit-to-define.patch b/target/linux/generic/pending-6.18/737-06-dt-bindings-net-ethernet-controller-permit-to-define.patch new file mode 100644 index 00000000000..ac4154b8579 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-06-dt-bindings-net-ethernet-controller-permit-to-define.patch @@ -0,0 +1,29 @@ +From c5d151dccce7deb62620a7b16418c0d6d6a59720 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 17 Mar 2025 23:07:45 +0100 +Subject: [PATCH 6/7] dt-bindings: net: ethernet-controller: permit to define + multiple PCS + +Drop the limitation of a single PCS in pcs-handle property. Multiple PCS +can be defined for an ethrnet-controller node to support various PHY +interface mode type. + +It's very common for SoCs to have a dedicated PCS for SGMII mode and one +for USXGMII mode. + +Signed-off-by: Christian Marangi +--- + Documentation/devicetree/bindings/net/ethernet-controller.yaml | 2 -- + 1 file changed, 2 deletions(-) + +--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml ++++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml +@@ -85,8 +85,6 @@ properties: + + pcs-handle: + $ref: /schemas/types.yaml#/definitions/phandle-array +- items: +- maxItems: 1 + description: + Specifies a reference to a node representing a PCS PHY device on a MDIO + bus to link with an external PHY (phy-handle) if exists. diff --git a/target/linux/generic/pending-6.18/737-07-net-phylink-add-.pcs_link_down-PCS-OP.patch b/target/linux/generic/pending-6.18/737-07-net-phylink-add-.pcs_link_down-PCS-OP.patch new file mode 100644 index 00000000000..b707508f07c --- /dev/null +++ b/target/linux/generic/pending-6.18/737-07-net-phylink-add-.pcs_link_down-PCS-OP.patch @@ -0,0 +1,64 @@ +From 4b1dde131a237455e41985fdc95306cd2f1b8a0a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 9 May 2025 16:36:22 +0200 +Subject: [PATCH 7/7] net: phylink: add .pcs_link_down PCS OP + +Permit for PCS driver to define specific operation to torn down the link +between the MAC and the PCS. + +This might be needed for some PCS that reset counter or require special +reset to correctly work if the link needs to be restored later. + +On phylink_link_down() call, the additional phylink_pcs_link_down() will +be called before .mac_link_down to torn down the link. + +PCS driver will need to define .pcs_link_down to make use of this. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 8 ++++++++ + include/linux/phylink.h | 2 ++ + 2 files changed, 10 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1031,6 +1031,12 @@ static void phylink_pcs_link_up(struct p + pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); + } + ++static void phylink_pcs_link_down(struct phylink_pcs *pcs) ++{ ++ if (pcs && pcs->ops->pcs_link_down) ++ pcs->ops->pcs_link_down(pcs); ++} ++ + static void phylink_pcs_disable_eee(struct phylink_pcs *pcs) + { + if (pcs && pcs->ops->pcs_disable_eee) +@@ -1723,6 +1729,8 @@ static void phylink_link_down(struct phy + + phylink_deactivate_lpi(pl); + ++ phylink_pcs_link_down(pl->pcs); ++ + pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode, + pl->cur_interface); + phylink_info(pl, "Link is Down\n"); +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -494,6 +494,7 @@ struct phylink_pcs { + * @pcs_an_restart: restart 802.3z BaseX autonegotiation. + * @pcs_link_up: program the PCS for the resolved link configuration + * (where necessary). ++ * @pcs_link_down: torn down link between MAC and PCS. + * @pcs_disable_eee: optional notification to PCS that EEE has been disabled + * at the MAC. + * @pcs_enable_eee: optional notification to PCS that EEE will be enabled at +@@ -521,6 +522,7 @@ struct phylink_pcs_ops { + void (*pcs_an_restart)(struct phylink_pcs *pcs); + void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, int duplex); ++ void (*pcs_link_down)(struct phylink_pcs *pcs); + void (*pcs_disable_eee)(struct phylink_pcs *pcs); + void (*pcs_enable_eee)(struct phylink_pcs *pcs); + int (*pcs_pre_init)(struct phylink_pcs *pcs); diff --git a/target/linux/generic/pending-6.18/737-08-net-ethernet-mtk_eth_soc-improve-probe-deferal.patch b/target/linux/generic/pending-6.18/737-08-net-ethernet-mtk_eth_soc-improve-probe-deferal.patch new file mode 100644 index 00000000000..ec920fa2178 --- /dev/null +++ b/target/linux/generic/pending-6.18/737-08-net-ethernet-mtk_eth_soc-improve-probe-deferal.patch @@ -0,0 +1,94 @@ +From 035efe8d5a0b5247f0d570968503260984ad9d38 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 25 Apr 2025 18:22:47 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: improve probe deferal + +In case an NVMEM provider for a MAC address returns -EPROBE_DEFER the +driver currently bails out only after already generating random addresses +for preceding netdevs and loudly warns about that. +This results in the warning being displayed multiple times, each time +with a different random MAC addresses. +Improve this by first checking if all MAC address providing NVMEM devices +are ready before resorting to random addresses. + +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 ++++++++++++++++----- + 1 file changed, 42 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -5005,17 +5005,6 @@ static int mtk_add_mac(struct mtk_eth *e + mac->hw = eth; + mac->of_node = np; + +- err = of_get_ethdev_address(mac->of_node, eth->netdev[id]); +- if (err == -EPROBE_DEFER) +- return err; +- +- if (err) { +- /* If the mac address is invalid, use random mac address */ +- eth_hw_addr_random(eth->netdev[id]); +- dev_err(eth->dev, "generated random MAC address %pM\n", +- eth->netdev[id]->dev_addr); +- } +- + memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); + mac->hwlro_ip_cnt = 0; + +@@ -5156,6 +5145,26 @@ free_netdev: + return err; + } + ++static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only) ++{ ++ int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]); ++ ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ if (test_defer_only) ++ return 0; ++ ++ if (err) { ++ /* If the mac address is invalid, use random mac address */ ++ eth_hw_addr_random(eth->netdev[i]); ++ dev_err(eth->dev, "generated random MAC address %pM\n", ++ eth->netdev[i]); ++ } ++ ++ return 0; ++} ++ + void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) + { + struct net_device *dev, *tmp; +@@ -5415,6 +5424,28 @@ static int mtk_probe(struct platform_dev + } + } + ++ /* check if all MAC address providers are available and return ++ * -EPROBE_DEFER in case at least one of them is not ready ++ */ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, true); ++ if (err) ++ goto err_deinit_hw; ++ } ++ ++ /* now actually assign MAC addresses to netdevs */ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, false); ++ if (err) ++ goto err_deinit_hw; ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { + err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_SHARED], + mtk_handle_irq, 0, diff --git a/target/linux/generic/pending-6.18/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.18/737-09-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch similarity index 56% rename from target/linux/generic/pending-6.18/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch rename to target/linux/generic/pending-6.18/737-09-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch index 5175d451e80..ec562595849 100644 --- a/target/linux/generic/pending-6.18/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch +++ b/target/linux/generic/pending-6.18/737-09-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -1,28 +1,21 @@ -From d5e337e7aecc2e1cc9e96768062610adb95f8f72 Mon Sep 17 00:00:00 2001 +From 0be12d497dbe2ea783e805f0375869cb573cd52a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 12 Dec 2023 03:51:14 +0000 -Subject: [PATCH] net: ethernet: mtk_eth_soc: add paths and SerDes modes for - MT7988 +Subject: [PATCH 4/4] net: ethernet: mtk_eth_soc: add paths and SerDes modes + for MT7988 -MT7988 comes with a new 10G/USXGMII SerDes PCS to connect external PHYs or -transceivers in USXGMII, 10GBase-R and 5GBase-R interface modes in addition -to the existing 2500Base-X, 1000Base-X and Cisco SGMII modes supported by -the existing LynxI PCS. +MT7988 comes with two SerDes interfaces to connect external PHYs or +transceivers in USXGMII, 10GBase-R, 5GBase-R, 2500Base-X, 1000Base-X and +Cisco SGMII interface modes. -Implement support for configuring for the new paths to the 10G SerDes -interfaces. - -Add USXGMII PCS driver for 10GBase-R, 5GBase-R and USXGMII mode, and -setup the new PHYA on MT7988 to access the also still existing old -LynxI PCS for 1000Base-X, 2500Base-X and Cisco SGMII PCS interface -modes. +Implement support for configuring for the new paths to SerDes interfaces. Signed-off-by: Daniel Golle --- - drivers/net/ethernet/mediatek/mtk_eth_path.c | 122 +++++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 292 +++++++++++++++++-- - drivers/net/ethernet/mediatek/mtk_eth_soc.h | 107 ++++++- - 3 files changed, 470 insertions(+), 51 deletions(-) + drivers/net/ethernet/mediatek/mtk_eth_path.c | 81 ++++++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 165 +++++++++++++++---- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 49 +++++- + 3 files changed, 252 insertions(+), 43 deletions(-) --- a/drivers/net/ethernet/mediatek/mtk_eth_path.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c @@ -120,7 +113,7 @@ Signed-off-by: Daniel Golle }, }; -@@ -278,12 +338,26 @@ out: +@@ -278,12 +338,25 @@ out: return err; } @@ -135,7 +128,6 @@ Signed-off-by: Daniel Golle + /* Setup proper MUXes along the path */ + return mtk_eth_mux_setup(eth, path); +} -+ + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) { @@ -155,21 +147,33 @@ Signed-off-by: Daniel Golle #include #include #include -+#include ++#include +#include #include #include #include -@@ -523,6 +525,30 @@ static void mtk_setup_bridge_switch(stru +@@ -523,23 +525,28 @@ static void mtk_setup_bridge_switch(stru MTK_GSW_CFG); } +-static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, +- phy_interface_t interface) +static bool mtk_check_gmac23_idle(struct mtk_mac *mac) -+{ + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); +- struct mtk_eth *eth = mac->hw; +- unsigned int sid; + u32 mac_fsm, gdm_fsm; -+ + +- if (interface == PHY_INTERFACE_MODE_SGMII || +- phy_interface_mode_is_8023z(interface)) { +- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +- 0 : mac->id; + mac_fsm = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id)); -+ + +- return eth->sgmii_pcs[sid]; +- } + switch (mac->id) { + case MTK_GMAC2_ID: + gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM2_FSM); @@ -180,50 +184,17 @@ Signed-off-by: Daniel Golle + default: + return true; + }; -+ + +- return NULL; + if ((mac_fsm & 0xFFFF0000) == 0x01010000 && + (gdm_fsm & 0xFFFF0000) == 0x00000000) + return true; + + return false; -+} -+ - static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) - { -@@ -531,6 +557,21 @@ static struct phylink_pcs *mtk_mac_selec - struct mtk_eth *eth = mac->hw; - unsigned int sid; - -+ if (mtk_is_netsys_v3_or_greater(eth)) { -+ switch (interface) { -+ case PHY_INTERFACE_MODE_1000BASEX: -+ case PHY_INTERFACE_MODE_2500BASEX: -+ case PHY_INTERFACE_MODE_SGMII: -+ return mac->sgmii_pcs; -+ case PHY_INTERFACE_MODE_5GBASER: -+ case PHY_INTERFACE_MODE_10GBASER: -+ case PHY_INTERFACE_MODE_USXGMII: -+ return mac->usxgmii_pcs; -+ default: -+ return NULL; -+ } -+ } -+ - if (interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(interface)) { - sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? -@@ -559,6 +600,9 @@ static int mtk_mac_prepare(struct phylin - MTK_XGMAC_FORCE_MODE(mac->id), MTK_XGMAC_STS(mac->id)); - } - -+ if (mac->pextp && mac->interface != iface) -+ phy_reset(mac->pextp); -+ - return 0; } -@@ -602,6 +646,15 @@ static void mtk_mac_config(struct phylin + static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode, +@@ -602,6 +609,15 @@ static void mtk_mac_config(struct phylin goto init_err; } break; @@ -239,7 +210,7 @@ Signed-off-by: Daniel Golle case PHY_INTERFACE_MODE_INTERNAL: if (mac->id == MTK_GMAC2_ID && MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) { -@@ -655,8 +708,6 @@ static void mtk_mac_config(struct phylin +@@ -655,8 +671,6 @@ static void mtk_mac_config(struct phylin val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); val |= SYSCFG0_GE_MODE(ge_mode, mac->id); regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); @@ -248,7 +219,7 @@ Signed-off-by: Daniel Golle } /* SGMII */ -@@ -673,21 +724,40 @@ static void mtk_mac_config(struct phylin +@@ -673,9 +687,12 @@ static void mtk_mac_config(struct phylin /* Save the syscfg0 value for mac_finish */ mac->syscfg0 = val; @@ -263,32 +234,22 @@ Signed-off-by: Daniel Golle return; } - /* Setup gmac */ -- if (mtk_interface_mode_is_xgmii(eth, state->interface)) { -- mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); -- mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); -+ if (mtk_is_netsys_v3_or_greater(eth)) { -+ if (mtk_interface_mode_is_xgmii(eth, state->interface)) { -+ mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); -+ mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); - -- if (mac->id == MTK_GMAC1_ID) -- mtk_setup_bridge_switch(eth); -+ if (mac->id == MTK_GMAC1_ID) -+ mtk_setup_bridge_switch(eth); -+ } else { -+ mtk_w32(eth, 0, MTK_GDMA_EG_CTRL(mac->id)); -+ -+ /* FIXME: In current hardware design, we have to reset FE -+ * when swtiching XGDM to GDM. Therefore, here trigger an SER -+ * to let GDM go back to the initial state. -+ */ -+ if ((mtk_interface_mode_is_xgmii(eth, mac->interface) || -+ mac->interface == PHY_INTERFACE_MODE_NA) && -+ !mtk_check_gmac23_idle(mac) && -+ !test_bit(MTK_RESETTING, ð->state)) -+ schedule_work(ð->pending_work); -+ } +@@ -686,8 +703,22 @@ static void mtk_mac_config(struct phylin + + if (mac->id == MTK_GMAC1_ID) + mtk_setup_bridge_switch(eth); ++ } else if (mtk_is_netsys_v3_or_greater(eth)) { ++ mtk_w32(eth, 0, MTK_GDMA_EG_CTRL(mac->id)); ++ ++ /* FIXME: In current hardware design, we have to reset FE ++ * when swtiching XGDM to GDM. Therefore, here trigger an SER ++ * to let GDM go back to the initial state. ++ */ ++ if ((mtk_interface_mode_is_xgmii(eth, mac->interface) || ++ mac->interface == PHY_INTERFACE_MODE_NA) && ++ !mtk_check_gmac23_idle(mac) && ++ !test_bit(MTK_RESETTING, ð->state)) ++ schedule_work(ð->pending_work); } + mac->interface = state->interface; @@ -296,110 +257,109 @@ Signed-off-by: Daniel Golle return; err_phy: -@@ -708,6 +778,10 @@ static int mtk_mac_finish(struct phylink - struct mtk_eth *eth = mac->hw; - u32 mcr_cur, mcr_new; - -+ /* Setup PMA/PMD */ -+ if (mac->pextp) -+ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); -+ - /* Enable SGMII */ - if (interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(interface)) -@@ -903,6 +977,10 @@ static void mtk_mac_link_up(struct phyli - else - mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, - tx_pause, rx_pause); -+ -+ /* Repeat pextp setup to tune link */ -+ if (mac->pextp) -+ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); - } - - static void mtk_mac_disable_tx_lpi(struct phylink_config *config) -@@ -3765,6 +3843,9 @@ static int mtk_open(struct net_device *d - - ppe_num = eth->soc->ppe_num; - -+ if (mac->pextp) -+ phy_power_on(mac->pextp); -+ - err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); - if (err) { - netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, -@@ -3912,6 +3993,9 @@ static int mtk_stop(struct net_device *d - for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) - mtk_ppe_stop(eth->ppe[i]); - -+ if (mac->pextp) -+ phy_power_off(mac->pextp); -+ - return 0; - } - -@@ -4968,6 +5052,7 @@ static const struct net_device_ops mtk_n - static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) - { - const __be32 *_id = of_get_property(np, "reg", NULL); -+ struct device_node *pcs_np; +@@ -738,6 +769,9 @@ static void mtk_mac_link_down(struct phy + mtk_m32(mac->hw, + MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK, 0, + MTK_MAC_MCR(mac->id)); ++ if (mtk_is_netsys_v3_or_greater(mac->hw)) ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, ++ MTK_XGMAC_STS(mac->id)); + } else if (mac->id != MTK_GMAC1_ID) { + /* XGMAC except for built-in switch */ + mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, +@@ -956,7 +990,6 @@ static int mtk_mac_enable_tx_lpi(struct + + static const struct phylink_mac_ops mtk_phylink_ops = { + .mac_prepare = mtk_mac_prepare, +- .mac_select_pcs = mtk_mac_select_pcs, + .mac_config = mtk_mac_config, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, +@@ -4971,7 +5004,8 @@ static int mtk_add_mac(struct mtk_eth *e phy_interface_t phy_mode; struct phylink *phylink; struct mtk_mac *mac; -@@ -5004,16 +5089,44 @@ static int mtk_add_mac(struct mtk_eth *e - mac->id = id; - mac->hw = eth; - mac->of_node = np; -+ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 0); -+ if (pcs_np) { -+ mac->sgmii_pcs = mtk_pcs_lynxi_get(eth->dev, pcs_np); -+ if (IS_ERR(mac->sgmii_pcs)) { -+ if (PTR_ERR(mac->sgmii_pcs) != -EPROBE_DEFER) -+ dev_err(eth->dev, -+ "cannot select SGMII PCS, error %ld\n", -+ PTR_ERR(mac->sgmii_pcs)); +- int id, err; ++ int id, err, count; ++ unsigned int sid; + int txqs = 1; + u32 val; + +@@ -5042,6 +5076,7 @@ static int mtk_add_mac(struct mtk_eth *e + mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD | + MAC_2500FD; + mac->phylink_config.lpi_timer_default = 1000; ++ mac->phylink_config.num_available_pcs = 0; + + /* MT7623 gmac0 is now missing its speed-specific PLL configuration + * in its .mac_config method (since state->speed is not valid there. +@@ -5072,13 +5107,62 @@ static int mtk_add_mac(struct mtk_eth *e + + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { + __set_bit(PHY_INTERFACE_MODE_SGMII, +- mac->phylink_config.supported_interfaces); ++ mac->phylink_config.pcs_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, +- mac->phylink_config.supported_interfaces); ++ mac->phylink_config.pcs_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, +- mac->phylink_config.supported_interfaces); ++ mac->phylink_config.pcs_interfaces); ++ ++ if (mtk_is_netsys_v3_or_greater(mac->hw)) { ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, ++ mac->phylink_config.pcs_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ mac->phylink_config.pcs_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ mac->phylink_config.pcs_interfaces); ++ ++ err = fwnode_phylink_pcs_parse(of_fwnode_handle(np), NULL, &count); ++ if (err == -ENODEV) { ++ err = 0; ++ goto no_pcs; ++ } + -+ err = PTR_ERR(mac->sgmii_pcs); -+ goto free_netdev; -+ } -+ } - -- err = of_get_ethdev_address(mac->of_node, eth->netdev[id]); -- if (err == -EPROBE_DEFER) -- return err; -+ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 1); -+ if (pcs_np) { -+ mac->usxgmii_pcs = mtk_usxgmii_pcs_get(eth->dev, pcs_np); -+ if (IS_ERR(mac->usxgmii_pcs)) { -+ if (PTR_ERR(mac->usxgmii_pcs) != -EPROBE_DEFER) -+ dev_err(eth->dev, -+ "cannot select USXGMII PCS, error %ld\n", -+ PTR_ERR(mac->usxgmii_pcs)); ++ if (count > 2) ++ err = -ENOMEM; + -+ err = PTR_ERR(mac->usxgmii_pcs); -+ goto free_netdev; -+ } -+ } - -- if (err) { -- /* If the mac address is invalid, use random mac address */ -- eth_hw_addr_random(eth->netdev[id]); -- dev_err(eth->dev, "generated random MAC address %pM\n", -- eth->netdev[id]->dev_addr); -+ if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) { -+ mac->pextp = devm_of_phy_optional_get(eth->dev, mac->of_node, NULL); -+ if (IS_ERR(mac->pextp)) { -+ if (PTR_ERR(mac->pextp) != -EPROBE_DEFER) -+ dev_err(eth->dev, "cannot get PHY, error %ld\n", -+ PTR_ERR(mac->pextp)); ++ if (err) ++ goto free_netdev; ++ ++ err = fwnode_phylink_pcs_parse(of_fwnode_handle(np), mac->available_pcs, &count); ++ if (err) ++ goto free_netdev; + -+ err = PTR_ERR(mac->pextp); -+ goto free_netdev; ++ mac->phylink_config.available_pcs = mac->available_pcs; ++ mac->phylink_config.num_available_pcs = count; ++ } else { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) { ++ /* single LynxI PCS used by either GMAC */ ++ if (!test_bit(phy_mode, eth->sgmii_pcs[0]->supported_interfaces)) ++ goto no_pcs; ++ if (eth->shared_sgmii_used) { ++ err = -EBUSY; ++ goto free_netdev; ++ } ++ sid = 0; ++ eth->shared_sgmii_used = true; ++ } else { ++ sid = id; ++ } ++ mac->phylink_config.available_pcs = ð->sgmii_pcs[sid]; ++ mac->phylink_config.num_available_pcs = 1; + } ++ ++ phy_interface_or(mac->phylink_config.supported_interfaces, ++ mac->phylink_config.supported_interfaces, ++ mac->phylink_config.pcs_interfaces); } - memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); -@@ -5099,8 +5212,21 @@ static int mtk_add_mac(struct mtk_eth *e ++no_pcs: + if (mtk_is_netsys_v3_or_greater(mac->hw) && + MTK_HAS_CAPS(mac->hw->soc->caps, MTK_ESW) && + id == MTK_GMAC1_ID) { +@@ -5088,18 +5172,16 @@ static int mtk_add_mac(struct mtk_eth *e phy_interface_zero(mac->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_INTERNAL, mac->phylink_config.supported_interfaces); @@ -413,42 +373,37 @@ Signed-off-by: Daniel Golle + mac->phylink_config.supported_interfaces); } -+ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && -+ id == MTK_GMAC2_ID) -+ __set_bit(PHY_INTERFACE_MODE_INTERNAL, -+ mac->phylink_config.supported_interfaces); -+ - phylink = phylink_create(&mac->phylink_config, - of_fwnode_handle(mac->of_node), - phy_mode, &mtk_phylink_ops); -@@ -5156,6 +5282,26 @@ free_netdev: - return err; - } - -+static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only) -+{ -+ int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]); -+ -+ if (err == -EPROBE_DEFER) -+ return err; -+ -+ if (test_defer_only) -+ return 0; -+ -+ if (err) { -+ /* If the mac address is invalid, use random mac address */ -+ eth_hw_addr_random(eth->netdev[i]); -+ dev_err(eth->dev, "generated random MAC address %pM\n", -+ eth->netdev[i]); +- phylink = phylink_create(&mac->phylink_config, +- of_fwnode_handle(mac->of_node), +- phy_mode, &mtk_phylink_ops); +- if (IS_ERR(phylink)) { +- err = PTR_ERR(phylink); +- goto free_netdev; +- } +- +- mac->phylink = phylink; +- + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && + id == MTK_GMAC2_ID) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, +@@ -5122,6 +5204,16 @@ static int mtk_add_mac(struct mtk_eth *e + eth->netdev[id]->irq = eth->irq[MTK_FE_IRQ_SHARED]; + eth->netdev[id]->dev.of_node = np; + ++ phylink = phylink_create(&mac->phylink_config, ++ of_fwnode_handle(mac->of_node), ++ phy_mode, &mtk_phylink_ops); ++ if (IS_ERR(phylink)) { ++ err = PTR_ERR(phylink); ++ goto free_netdev; + } + -+ return 0; -+} ++ mac->phylink = phylink; + - void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) - { - struct net_device *dev, *tmp; -@@ -5309,7 +5455,8 @@ static int mtk_probe(struct platform_dev + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + else +@@ -5315,7 +5407,8 @@ static int mtk_probe(struct platform_dev regmap_write(cci, 0, 3); } @@ -458,54 +413,9 @@ Signed-off-by: Daniel Golle err = mtk_sgmii_init(eth); if (err) -@@ -5418,6 +5565,24 @@ static int mtk_probe(struct platform_dev - } - } - -+ for (i = 0; i < MTK_MAX_DEVS; i++) { -+ if (!eth->netdev[i]) -+ continue; -+ -+ err = mtk_mac_assign_address(eth, i, true); -+ if (err) -+ goto err_deinit_hw; -+ } -+ -+ for (i = 0; i < MTK_MAX_DEVS; i++) { -+ if (!eth->netdev[i]) -+ continue; -+ -+ err = mtk_mac_assign_address(eth, i, false); -+ if (err) -+ goto err_deinit_hw; -+ } -+ - if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { - err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_SHARED], - mtk_handle_irq, 0, -@@ -5528,6 +5693,11 @@ static void mtk_remove(struct platform_d - mtk_stop(eth->netdev[i]); - mac = netdev_priv(eth->netdev[i]); - phylink_disconnect_phy(mac->phylink); -+ if (mac->sgmii_pcs) -+ mtk_pcs_lynxi_put(mac->sgmii_pcs); -+ -+ if (mac->usxgmii_pcs) -+ mtk_usxgmii_pcs_put(mac->usxgmii_pcs); - } - - mtk_wed_exit(); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -570,6 +571,7 @@ +@@ -570,6 +570,7 @@ #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) #define SYSCFG0_SGMII_GMAC1_V2 BIT(9) #define SYSCFG0_SGMII_GMAC2_V2 BIT(8) @@ -513,7 +423,7 @@ Signed-off-by: Daniel Golle /* ethernet subsystem clock register */ -@@ -611,6 +613,7 @@ +@@ -611,6 +612,7 @@ #define TOP_MISC_NETSYS_PCS_MUX 0x0 #define NETSYS_PCS_MUX_MASK GENMASK(1, 0) #define MUX_G2_USXGMII_SEL BIT(1) @@ -521,7 +431,7 @@ Signed-off-by: Daniel Golle #define USB_PHY_SWITCH_REG 0x218 #define QPHY_SEL_MASK GENMASK(1, 0) -@@ -636,6 +639,8 @@ +@@ -636,6 +638,8 @@ #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c) #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110) @@ -530,7 +440,7 @@ Signed-off-by: Daniel Golle #define MTK_FE_CDM1_FSM 0x220 #define MTK_FE_CDM2_FSM 0x224 #define MTK_FE_CDM3_FSM 0x238 -@@ -644,6 +649,11 @@ +@@ -644,6 +648,11 @@ #define MTK_FE_CDM6_FSM 0x328 #define MTK_FE_GDM1_FSM 0x228 #define MTK_FE_GDM2_FSM 0x22C @@ -542,7 +452,7 @@ Signed-off-by: Daniel Golle #define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) -@@ -972,6 +982,7 @@ enum mkt_eth_capabilities { +@@ -972,6 +981,7 @@ enum mkt_eth_capabilities { MTK_RGMII_BIT = 0, MTK_TRGMII_BIT, MTK_SGMII_BIT, @@ -550,7 +460,7 @@ Signed-off-by: Daniel Golle MTK_2P5GPHY_BIT, MTK_ESW_BIT, MTK_GEPHY_BIT, -@@ -996,6 +1007,8 @@ enum mkt_eth_capabilities { +@@ -996,6 +1006,8 @@ enum mkt_eth_capabilities { MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT, MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, @@ -559,7 +469,7 @@ Signed-off-by: Daniel Golle /* PATH BITS */ MTK_ETH_PATH_GMAC1_RGMII_BIT, -@@ -1005,13 +1018,18 @@ enum mkt_eth_capabilities { +@@ -1005,13 +1017,18 @@ enum mkt_eth_capabilities { MTK_ETH_PATH_GMAC2_SGMII_BIT, MTK_ETH_PATH_GMAC2_2P5GPHY_BIT, MTK_ETH_PATH_GMAC2_GEPHY_BIT, @@ -567,7 +477,7 @@ Signed-off-by: Daniel Golle MTK_ETH_PATH_GDM1_ESW_BIT, + MTK_ETH_PATH_GMAC1_USXGMII_BIT, + MTK_ETH_PATH_GMAC2_USXGMII_BIT, -+ MTK_ETH_PATH_GMAC3_USXGMII_BIT ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT, }; /* Supported hardware group on SoCs */ @@ -578,7 +488,7 @@ Signed-off-by: Daniel Golle #define MTK_2P5GPHY BIT_ULL(MTK_2P5GPHY_BIT) #define MTK_ESW BIT_ULL(MTK_ESW_BIT) #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) -@@ -1041,6 +1059,10 @@ enum mkt_eth_capabilities { +@@ -1041,6 +1058,10 @@ enum mkt_eth_capabilities { BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) @@ -589,7 +499,7 @@ Signed-off-by: Daniel Golle /* Supported path present on SoCs */ #define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) -@@ -1050,7 +1072,11 @@ enum mkt_eth_capabilities { +@@ -1050,7 +1071,11 @@ enum mkt_eth_capabilities { #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) #define MTK_ETH_PATH_GMAC2_2P5GPHY BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT) #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) @@ -601,7 +511,7 @@ Signed-off-by: Daniel Golle #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) -@@ -1059,7 +1085,11 @@ enum mkt_eth_capabilities { +@@ -1059,7 +1084,11 @@ enum mkt_eth_capabilities { #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) #define MTK_GMAC2_2P5GPHY (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY) @@ -613,7 +523,7 @@ Signed-off-by: Daniel Golle /* MUXes present on SoCs */ /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ -@@ -1086,6 +1116,12 @@ enum mkt_eth_capabilities { +@@ -1086,6 +1115,12 @@ enum mkt_eth_capabilities { #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) @@ -626,7 +536,7 @@ Signed-off-by: Daniel Golle #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) #define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ -@@ -1117,9 +1153,12 @@ enum mkt_eth_capabilities { +@@ -1117,9 +1152,12 @@ enum mkt_eth_capabilities { MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ MTK_RSTCTRL_PPE1 | MTK_SRAM) @@ -642,17 +552,24 @@ Signed-off-by: Daniel Golle struct mtk_tx_dma_desc_info { dma_addr_t addr; -@@ -1366,6 +1405,9 @@ struct mtk_mac { +@@ -1300,6 +1338,7 @@ struct mtk_eth { + struct regmap *ethsys; + struct regmap *infra; + struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS]; ++ bool shared_sgmii_used; + struct regmap *pctl; + bool hwlro; + refcount_t dma_refcnt; +@@ -1366,6 +1405,8 @@ struct mtk_mac { struct device_node *of_node; struct phylink *phylink; struct phylink_config phylink_config; -+ struct phylink_pcs *sgmii_pcs; -+ struct phylink_pcs *usxgmii_pcs; -+ struct phy *pextp; ++ struct phylink_pcs *current_pcs; ++ struct phylink_pcs *available_pcs[2]; struct mtk_eth *hw; struct mtk_hw_stats *hw_stats; __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; -@@ -1517,6 +1559,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk +@@ -1517,6 +1558,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id); int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); diff --git a/target/linux/generic/pending-6.18/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch b/target/linux/generic/pending-6.18/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch index c479c80a53e..5850e41899f 100644 --- a/target/linux/generic/pending-6.18/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch +++ b/target/linux/generic/pending-6.18/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -5731,7 +5731,7 @@ static const struct mtk_soc_data mt2701_ +@@ -5682,7 +5682,7 @@ static const struct mtk_soc_data mt2701_ DESC_SIZE(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, @@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, -@@ -5759,7 +5759,7 @@ static const struct mtk_soc_data mt7621_ +@@ -5710,7 +5710,7 @@ static const struct mtk_soc_data mt7621_ DESC_SIZE(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, @@ -48,7 +48,7 @@ Signed-off-by: Felix Fietkau .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, -@@ -5789,7 +5789,7 @@ static const struct mtk_soc_data mt7622_ +@@ -5740,7 +5740,7 @@ static const struct mtk_soc_data mt7622_ DESC_SIZE(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, @@ -57,7 +57,7 @@ Signed-off-by: Felix Fietkau .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, -@@ -5818,7 +5818,7 @@ static const struct mtk_soc_data mt7623_ +@@ -5769,7 +5769,7 @@ static const struct mtk_soc_data mt7623_ DESC_SIZE(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, @@ -66,7 +66,7 @@ Signed-off-by: Felix Fietkau .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, -@@ -5844,7 +5844,7 @@ static const struct mtk_soc_data mt7629_ +@@ -5795,7 +5795,7 @@ static const struct mtk_soc_data mt7629_ DESC_SIZE(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, @@ -75,7 +75,7 @@ Signed-off-by: Felix Fietkau .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, -@@ -5876,7 +5876,7 @@ static const struct mtk_soc_data mt7981_ +@@ -5827,7 +5827,7 @@ static const struct mtk_soc_data mt7981_ .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, @@ -84,7 +84,7 @@ Signed-off-by: Felix Fietkau }, }; -@@ -5906,7 +5906,7 @@ static const struct mtk_soc_data mt7986_ +@@ -5857,7 +5857,7 @@ static const struct mtk_soc_data mt7986_ .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, @@ -93,7 +93,7 @@ Signed-off-by: Felix Fietkau }, }; -@@ -5959,7 +5959,7 @@ static const struct mtk_soc_data rt5350_ +@@ -5910,7 +5910,7 @@ static const struct mtk_soc_data rt5350_ .dma_l4_valid = RX_DMA_L4_VALID_PDMA, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, diff --git a/target/linux/generic/pending-6.18/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch b/target/linux/generic/pending-6.18/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch index 80b3a2c3376..3dc8c8f6f94 100644 --- a/target/linux/generic/pending-6.18/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch +++ b/target/linux/generic/pending-6.18/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch @@ -25,7 +25,7 @@ Signed-off-by: Felix Fietkau help --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4861,6 +4861,7 @@ static int mtk_get_sset_count(struct net +@@ -4810,6 +4810,7 @@ static int mtk_get_sset_count(struct net static void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data) { @@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau struct page_pool_stats stats = {}; int i; -@@ -4873,6 +4874,7 @@ static void mtk_ethtool_pp_stats(struct +@@ -4822,6 +4823,7 @@ static void mtk_ethtool_pp_stats(struct page_pool_get_stats(ring->page_pool, &stats); } page_pool_ethtool_stats_get(data, &stats); diff --git a/target/linux/generic/pending-6.18/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch b/target/linux/generic/pending-6.18/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch deleted file mode 100644 index 1f1c40b1d91..00000000000 --- a/target/linux/generic/pending-6.18/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch +++ /dev/null @@ -1,136 +0,0 @@ -From patchwork Thu Feb 1 21:52:20 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Daniel Golle -X-Patchwork-Id: 13541842 -Date: Thu, 1 Feb 2024 21:52:20 +0000 -From: Daniel Golle -To: Bc-bocun Chen , - Steven Liu , - John Crispin , - Chunfeng Yun , - Vinod Koul , - Kishon Vijay Abraham I , - Rob Herring , - Krzysztof Kozlowski , - Conor Dooley , - Daniel Golle , - Qingfang Deng , - SkyLake Huang , - Matthias Brugger , - AngeloGioacchino Del Regno , - Philipp Zabel , - linux-arm-kernel@lists.infradead.org, - linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, - devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, - netdev@vger.kernel.org -Subject: [PATCH 1/2] dt-bindings: phy: mediatek,xfi-tphy: add new bindings -Message-ID: - <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> -MIME-Version: 1.0 -Content-Disposition: inline -List-Id: Linux Phy Mailing list - -Add bindings for the MediaTek XFI T-PHY Ethernet SerDes PHY found in the -MediaTek MT7988 SoC which can operate at various interfaces modes: - -via USXGMII PCS: - * USXGMII - * 10GBase-R - * 5GBase-R - -via LynxI SGMII PCS: - * 2500Base-X - * 1000Base-X - * Cisco SGMII (MAC side) - -Signed-off-by: Daniel Golle ---- - .../bindings/phy/mediatek,xfi-tphy.yaml | 80 +++++++++++++++++++ - 1 file changed, 80 insertions(+) - create mode 100644 Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml -@@ -0,0 +1,80 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/phy/mediatek,xfi-tphy.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: MediaTek XFI T-PHY -+ -+maintainers: -+ - Daniel Golle -+ -+description: -+ The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes -+ used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in -+ MediaTek's 10G-capabale SoCs. -+ -+properties: -+ $nodename: -+ pattern: "^phy@[0-9a-f]+$" -+ -+ compatible: -+ const: mediatek,mt7988-xfi-tphy -+ -+ reg: -+ maxItems: 1 -+ -+ clocks: -+ items: -+ - description: XFI PHY clock -+ - description: XFI register clock -+ -+ clock-names: -+ items: -+ - const: xfipll -+ - const: topxtal -+ -+ resets: -+ items: -+ - description: PEXTP reset -+ -+ mediatek,usxgmii-performance-errata: -+ $ref: /schemas/types.yaml#/definitions/flag -+ description: -+ One instance of the T-PHY on MT7988 suffers from a performance -+ problem in 10GBase-R mode which needs a work-around in the driver. -+ The work-around is enabled using this flag. -+ -+ "#phy-cells": -+ const: 0 -+ -+required: -+ - compatible -+ - reg -+ - clocks -+ - clock-names -+ - resets -+ - "#phy-cells" -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ soc { -+ #address-cells = <2>; -+ #size-cells = <2>; -+ -+ phy@11f20000 { -+ compatible = "mediatek,mt7988-xfi-tphy"; -+ reg = <0 0x11f20000 0 0x10000>; -+ clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>, -+ <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>; -+ clock-names = "xfipll", "topxtal"; -+ resets = <&watchdog 14>; -+ mediatek,usxgmii-performance-errata; -+ #phy-cells = <0>; -+ }; -+ }; -+ -+... diff --git a/target/linux/generic/pending-6.18/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch b/target/linux/generic/pending-6.18/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch index 8ac059ef5e5..7093797af7c 100644 --- a/target/linux/generic/pending-6.18/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch +++ b/target/linux/generic/pending-6.18/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch @@ -1,16 +1,15 @@ -From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001 +From e11b399cedf08e0ca3046c856708daffb6f7888a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 12 Dec 2023 03:47:18 +0000 -Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988 +Subject: [PATCH] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988 -Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which +Introduce a full platform MFD driver for the LynxI (H)SGMII PCS which is going to initially be used for the MT7988 SoC. Signed-off-by: Daniel Golle --- - drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++-- - include/linux/pcs/pcs-mtk-lynxi.h | 11 ++ - 2 files changed, 227 insertions(+), 11 deletions(-) + drivers/net/pcs/pcs-mtk-lynxi.c | 290 +++++++++++++++++++++++++++++--- + 1 file changed, 262 insertions(+), 28 deletions(-) --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c @@ -22,7 +21,7 @@ Signed-off-by: Daniel Golle * * Author: Sean Wang * Author: Alexander Couzens -@@ -8,11 +8,17 @@ +@@ -8,12 +8,22 @@ * */ @@ -31,28 +30,40 @@ Signed-off-by: Daniel Golle +#include +#include #include ++#include +#include #include + #include ++#include ++#include #include +#include #include +#include ++#include /* SGMII subsystem config registers */ /* BMCR (low 16) BMSR (high 16) */ -@@ -65,6 +71,8 @@ - #define SGMII_PN_SWAP_MASK GENMASK(1, 0) - #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) +@@ -67,6 +77,8 @@ + #define SGMII_PN_SWAP_TX BIT(0) + +#define MTK_NETSYS_V3_AMA_RGC3 0x128 + /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated * data * @regmap: The register map pointing at the range used to setup -@@ -74,15 +82,29 @@ +@@ -74,16 +86,37 @@ + * @dev: Pointer to device owning the PCS + * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap * @interface: Currently configured interface mode ++ * @neg_mode: Currently configured negotiation mode ++ * @advertising: Currently configured advertisement * @pcs: Phylink PCS structure * @flags: Flags indicating hardware properties ++ * @last_get_state: Jiffies at last pcs_get_state call ++ * @link_poll: Delayed work for link polling when phylink does ++ * not poll the PCS + * @rstc: Reset controller + * @sgmii_sel: SGMII Register Clock + * @sgmii_rx: SGMII RX Clock @@ -64,23 +75,47 @@ Signed-off-by: Daniel Golle + struct device *dev; u32 ana_rgc3; phy_interface_t interface; ++ unsigned int neg_mode; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); struct phylink_pcs pcs; u32 flags; + struct fwnode_handle *fwnode; ++ unsigned long last_get_state; ++ struct delayed_work link_poll; + struct reset_control *rstc; + struct clk *sgmii_sel; + struct clk *sgmii_rx; + struct clk *sgmii_tx; ++ struct phy *xfi_tphy; + struct list_head node; }; -+static LIST_HEAD(mtk_pcs_lynxi_instances); -+static DEFINE_MUTEX(instance_mutex); -+ static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) +@@ -105,22 +138,6 @@ static unsigned int mtk_pcs_lynxi_inband + } + } + +-static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, +- unsigned int neg_mode, +- struct phylink_link_state *state) +-{ +- struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +- unsigned int bm, adv; +- +- /* Read the BMSR and LPA */ +- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); +- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); +- +- phylink_mii_c22_pcs_decode_state(state, neg_mode, +- FIELD_GET(SGMII_BMSR, bm), +- FIELD_GET(SGMII_LPA, adv)); +-} +- + static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs, + phy_interface_t interface) { - return container_of(pcs, struct mtk_pcs_lynxi, pcs); -@@ -118,6 +140,17 @@ static void mtk_pcs_lynxi_get_state(stru - FIELD_GET(SGMII_LPA, adv)); +@@ -160,6 +177,17 @@ static int mtk_pcs_config_polarity(struc + SGMII_PN_SWAP_RX | SGMII_PN_SWAP_TX, val); } +static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) @@ -97,18 +132,127 @@ Signed-off-by: Daniel Golle static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, -@@ -163,6 +196,7 @@ static int mtk_pcs_lynxi_config(struct p +@@ -171,8 +199,13 @@ static int mtk_pcs_lynxi_config(struct p + int advertise, link_timer; + int ret; + ++ mpcs->neg_mode = neg_mode; ++ ++ if (advertising) ++ linkmode_copy(mpcs->advertising, advertising); ++ + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, +- advertising); ++ mpcs->advertising); + if (advertise < 0) + return advertise; + +@@ -206,6 +239,10 @@ static int mtk_pcs_lynxi_config(struct p SGMII_PHYA_PWD); /* Reset SGMII PCS state */ ++ if (mpcs->xfi_tphy) ++ phy_reset(mpcs->xfi_tphy); ++ + mtk_sgmii_reset(mpcs); regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, SGMII_SW_RESET); -@@ -249,10 +283,29 @@ static void mtk_pcs_lynxi_link_up(struct +@@ -255,9 +292,41 @@ static int mtk_pcs_lynxi_config(struct p + usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + ++ /* Setup PMA/PMD */ ++ if (mpcs->xfi_tphy) ++ phy_set_mode_ext(mpcs->xfi_tphy, PHY_MODE_ETHERNET, interface); ++ + return changed || mode_changed; + } + ++static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, ++ unsigned int neg_mode, ++ struct phylink_link_state *state) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int bm, adv; ++ ++ /* Read the BMSR and LPA */ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ ++ phylink_mii_c22_pcs_decode_state(state, neg_mode, ++ FIELD_GET(SGMII_BMSR, bm), ++ FIELD_GET(SGMII_LPA, adv)); ++ ++ mpcs->last_get_state = jiffies; ++ ++ /* The XFI T-PHY's analog calibration may settle at a bad ++ * operating point after hard reset, leaving the TX output ++ * non-functional. Re-initialize to get another chance. ++ */ ++ if (!state->link && mpcs->xfi_tphy) { ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mtk_pcs_lynxi_config(pcs, mpcs->neg_mode, ++ state->interface, NULL, false); ++ } ++} ++ + static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +@@ -273,6 +342,16 @@ static void mtk_pcs_lynxi_link_up(struct + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + unsigned int sgm_mode; + ++ /* In force mode the PCS provides no link status feedback, ++ * so re-init the T-PHY here as the only chance to recover ++ * from a bad analog calibration. In-band modes are handled ++ * by pcs_get_state and the link_poll worker instead. ++ */ ++ if (mpcs->xfi_tphy && neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mtk_pcs_lynxi_config(pcs, neg_mode, interface, NULL, false); ++ } ++ + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { + /* Force the speed and duplex setting */ + if (speed == SPEED_10) +@@ -291,11 +370,71 @@ static void mtk_pcs_lynxi_link_up(struct } } ++/* Fallback link poll worker for cases where phylink does not poll the ++ * PCS (e.g. when an SFP module has a PHY and phylink uses MLO_AN_PHY). ++ * Defers to pcs_get_state when phylink IS polling the PCS. ++ */ ++static void mtk_pcs_lynxi_link_poll(struct work_struct *work) ++{ ++ struct mtk_pcs_lynxi *mpcs = container_of(work, ++ struct mtk_pcs_lynxi, ++ link_poll.work); ++ phy_interface_t interface; ++ unsigned int bm; ++ ++ /* pcs_get_state handles re-init when phylink polls the PCS */ ++ if (time_is_after_jiffies(mpcs->last_get_state + 2 * HZ)) ++ goto reschedule; ++ ++ interface = mpcs->interface; ++ if (interface == PHY_INTERFACE_MODE_NA) ++ goto reschedule; ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ if (FIELD_GET(SGMII_BMSR, bm) & BMSR_LSTATUS) ++ goto reschedule; ++ ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mtk_pcs_lynxi_config(&mpcs->pcs, mpcs->neg_mode, ++ interface, NULL, false); ++ ++reschedule: ++ schedule_delayed_work(&mpcs->link_poll, HZ); ++} ++ +static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); @@ -118,6 +262,11 @@ Signed-off-by: Daniel Golle + clk_prepare_enable(mpcs->sgmii_tx); + } + ++ if (mpcs->xfi_tphy) { ++ phy_power_on(mpcs->xfi_tphy); ++ schedule_delayed_work(&mpcs->link_poll, HZ); ++ } ++ + return 0; +} + @@ -133,9 +282,14 @@ Signed-off-by: Daniel Golle + } + mpcs->interface = PHY_INTERFACE_MODE_NA; ++ if (mpcs->xfi_tphy) { ++ cancel_delayed_work_sync(&mpcs->link_poll); ++ phy_power_off(mpcs->xfi_tphy); ++ } } -@@ -263,11 +316,12 @@ static const struct phylink_pcs_ops mtk_ + static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { +@@ -305,11 +444,13 @@ static const struct phylink_pcs_ops mtk_ .pcs_an_restart = mtk_pcs_lynxi_restart_an, .pcs_link_up = mtk_pcs_lynxi_link_up, .pcs_disable = mtk_pcs_lynxi_disable, @@ -143,15 +297,16 @@ Signed-off-by: Daniel Golle }; -struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, -- struct regmap *regmap, u32 ana_rgc3, -- u32 flags) -+static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap, -+ u32 ana_rgc3, u32 flags, +- struct fwnode_handle *fwnode, +- struct regmap *regmap, u32 ana_rgc3) ++static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, ++ struct fwnode_handle *fwnode, ++ struct regmap *regmap, u32 ana_rgc3, + struct mtk_pcs_lynxi *prealloc) { struct mtk_pcs_lynxi *mpcs; u32 id, ver; -@@ -275,29 +329,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create +@@ -317,29 +458,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); if (ret < 0) @@ -192,24 +347,32 @@ Signed-off-by: Daniel Golle mpcs->ana_rgc3 = ana_rgc3; mpcs->regmap = regmap; -@@ -311,6 +369,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create +@@ -347,12 +492,20 @@ struct phylink_pcs *mtk_pcs_lynxi_create + mpcs->pcs.poll = true; + mpcs->interface = PHY_INTERFACE_MODE_NA; + mpcs->fwnode = fwnode_handle_get(fwnode); ++ INIT_DELAYED_WORK(&mpcs->link_poll, mtk_pcs_lynxi_link_poll); + + __set_bit(PHY_INTERFACE_MODE_SGMII, mpcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, mpcs->pcs.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_2500BASEX, mpcs->pcs.supported_interfaces); return &mpcs->pcs; +}; + +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, -+ struct regmap *regmap, u32 ana_rgc3, -+ u32 flags) ++ struct fwnode_handle *fwnode, ++ struct regmap *regmap, u32 ana_rgc3) +{ -+ return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL); ++ return mtk_pcs_lynxi_init(dev, fwnode, regmap, ana_rgc3, NULL); } EXPORT_SYMBOL(mtk_pcs_lynxi_create); -@@ -323,5 +388,142 @@ void mtk_pcs_lynxi_destroy(struct phylin +@@ -369,5 +522,86 @@ void mtk_pcs_lynxi_destroy(struct phylin } EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); ++#ifdef CONFIG_FWNODE_PCS +static int mtk_pcs_lynxi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; @@ -217,7 +380,6 @@ Signed-off-by: Daniel Golle + struct mtk_pcs_lynxi *mpcs; + struct phylink_pcs *pcs; + struct regmap *regmap; -+ u32 flags = 0; + + mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); + if (!mpcs) @@ -228,9 +390,6 @@ Signed-off-by: Daniel Golle + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + -+ if (of_property_read_bool(np->parent, "mediatek,pnswap")) -+ flags |= MTK_SGMII_FLAG_PN_SWAP; -+ + mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); + if (IS_ERR(mpcs->rstc)) + return PTR_ERR(mpcs->rstc); @@ -248,8 +407,13 @@ Signed-off-by: Daniel Golle + if (IS_ERR(mpcs->sgmii_tx)) + return PTR_ERR(mpcs->sgmii_tx); + -+ pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev), -+ flags, mpcs); ++ mpcs->xfi_tphy = devm_of_phy_optional_get(mpcs->dev, np, NULL); ++ if (IS_ERR(mpcs->xfi_tphy)) ++ return PTR_ERR(mpcs->xfi_tphy); ++ ++ pcs = mtk_pcs_lynxi_init(dev, of_fwnode_handle(np), regmap, ++ (uintptr_t)of_device_get_match_data(dev), ++ mpcs); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + @@ -257,27 +421,19 @@ Signed-off-by: Daniel Golle + + platform_set_drvdata(pdev, mpcs); + -+ mutex_lock(&instance_mutex); -+ list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances); -+ mutex_unlock(&instance_mutex); -+ -+ return 0; ++ return fwnode_pcs_add_provider(of_fwnode_handle(np), fwnode_pcs_simple_get, &mpcs->pcs); +} + +static void mtk_pcs_lynxi_remove(struct platform_device *pdev) +{ -+ struct device *dev = &pdev->dev; -+ struct mtk_pcs_lynxi *cur, *tmp; -+ -+ mutex_lock(&instance_mutex); -+ list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node) -+ if (cur->dev == dev) { -+ list_del(&cur->node); -+ kfree(cur); -+ break; -+ } -+ mutex_unlock(&instance_mutex); -+} ++ struct mtk_pcs_lynxi *mpcs = platform_get_drvdata(pdev); ++ ++ fwnode_pcs_del_provider(dev_fwnode(&pdev->dev)); ++ ++ rtnl_lock(); ++ phylink_release_pcs(&mpcs->pcs); ++ rtnl_unlock(); ++}; + +static const struct of_device_id mtk_pcs_lynxi_of_match[] = { + { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 }, @@ -285,85 +441,17 @@ Signed-off-by: Daniel Golle +}; +MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match); + -+struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) -+{ -+ struct platform_device *pdev; -+ struct mtk_pcs_lynxi *mpcs; -+ -+ if (!np) -+ return NULL; -+ -+ if (!of_device_is_available(np)) -+ return ERR_PTR(-ENODEV); -+ -+ if (!of_match_node(mtk_pcs_lynxi_of_match, np)) -+ return ERR_PTR(-EINVAL); -+ -+ pdev = of_find_device_by_node(np); -+ if (!pdev || !platform_get_drvdata(pdev)) { -+ if (pdev) -+ put_device(&pdev->dev); -+ return ERR_PTR(-EPROBE_DEFER); -+ } -+ -+ mpcs = platform_get_drvdata(pdev); -+ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); -+ -+ return &mpcs->pcs; -+} -+EXPORT_SYMBOL(mtk_pcs_lynxi_get); -+ -+void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) -+{ -+ struct mtk_pcs_lynxi *cur, *mpcs = NULL; -+ -+ if (!pcs) -+ return; -+ -+ mutex_lock(&instance_mutex); -+ list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node) -+ if (pcs == &cur->pcs) { -+ mpcs = cur; -+ break; -+ } -+ mutex_unlock(&instance_mutex); -+ -+ if (WARN_ON(!mpcs)) -+ return; -+ -+ put_device(mpcs->dev); -+} -+EXPORT_SYMBOL(mtk_pcs_lynxi_put); -+ +static struct platform_driver mtk_pcs_lynxi_driver = { + .driver = { + .name = "mtk-pcs-lynxi", -+ .suppress_bind_attrs = true, + .of_match_table = mtk_pcs_lynxi_of_match, + }, + .probe = mtk_pcs_lynxi_probe, + .remove = mtk_pcs_lynxi_remove, +}; +module_platform_driver(mtk_pcs_lynxi_driver); ++#endif + +MODULE_AUTHOR("Daniel Golle "); MODULE_DESCRIPTION("MediaTek SGMII library for LynxI"); MODULE_LICENSE("GPL"); ---- a/include/linux/pcs/pcs-mtk-lynxi.h -+++ b/include/linux/pcs/pcs-mtk-lynxi.h -@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create - struct regmap *regmap, - u32 ana_rgc3, u32 flags); - void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); -+ -+#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI) -+struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np); -+void mtk_pcs_lynxi_put(struct phylink_pcs *pcs); -+#else -+static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) -+{ -+ return NULL; -+} -+static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { } -+#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */ - #endif diff --git a/target/linux/generic/pending-6.18/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch b/target/linux/generic/pending-6.18/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch index 215bd2ca2ef..46694c85343 100644 --- a/target/linux/generic/pending-6.18/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch +++ b/target/linux/generic/pending-6.18/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch @@ -1,7 +1,7 @@ -From 7d88d79c0f65b27a92754d7547f7af098b3de67b Mon Sep 17 00:00:00 2001 +From 029c59b9e92776ed1b8f586055d5813132e32f47 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 12 Dec 2023 03:47:31 +0000 -Subject: [PATCH 4/5] dt-bindings: net: pcs: add bindings for MediaTek USXGMII +Subject: [PATCH 2/4] dt-bindings: net: pcs: add bindings for MediaTek USXGMII PCS MediaTek's USXGMII can be found in the MT7988 SoC. We need to access diff --git a/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch index 686c0293a1a..83336476d48 100644 --- a/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch +++ b/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch @@ -1,21 +1,21 @@ -From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001 +From c33cd256420ed08ffbedf39971882acc60dc184e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 12 Dec 2023 03:47:47 +0000 -Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS +Subject: [PATCH 3/4] net: pcs: add driver for MediaTek USXGMII PCS Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting USXGMII, 10GBase-R and 5GBase-R interface modes. Signed-off-by: Daniel Golle --- - MAINTAINERS | 2 + - drivers/net/pcs/Kconfig | 11 + - drivers/net/pcs/Makefile | 1 + - drivers/net/pcs/pcs-mtk-usxgmii.c | 456 ++++++++++++++++++++++++++++ - include/linux/pcs/pcs-mtk-usxgmii.h | 27 ++ - 5 files changed, 497 insertions(+) + MAINTAINERS | 2 + + drivers/net/pcs/Kconfig | 12 + + drivers/net/pcs/Kconfig.orig | 55 ++++ + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs-mtk-usxgmii.c | 440 ++++++++++++++++++++++++++++++ + 5 files changed, 510 insertions(+) + create mode 100644 drivers/net/pcs/Kconfig.orig create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c - create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h --- a/MAINTAINERS +++ b/MAINTAINERS @@ -31,34 +31,94 @@ Signed-off-by: Daniel Golle M: Daniel Golle --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig -@@ -25,6 +25,17 @@ config PCS_MTK_LYNXI +@@ -33,6 +33,18 @@ config PCS_MTK_LYNXI This module provides helpers to phylink for managing the LynxI PCS which is part of MediaTek's SoC and Ethernet switch ICs. +config PCS_MTK_USXGMII + tristate "MediaTek USXGMII PCS" ++ select FWNODE_PCS + select PCS_MTK_LYNXI -+ select PHY_MTK_PEXTP + select PHYLINK ++ imply PHY_MTK_PEXTP + help + This module provides a driver for MediaTek's USXGMII PCS supporting + 10GBase-R, 5GBase-R and USXGMII interface modes. + 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same -+ differential pairs via an embedded LynxI PHY. ++ differential pairs via an embedded LynxI PCS. + config PCS_RZN1_MIIC tristate "Renesas RZ/N1, RZ/N2H, RZ/T2H MII converter" depends on OF +--- /dev/null ++++ b/drivers/net/pcs/Kconfig.orig +@@ -0,0 +1,55 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# PCS Layer Configuration ++# ++ ++menu "PCS device drivers" ++ ++config OF_PCS ++ tristate ++ depends on OF ++ depends on PHYLINK ++ help ++ OpenFirmware PCS accessors ++ ++config PCS_XPCS ++ tristate "Synopsys DesignWare Ethernet XPCS" ++ select PHYLINK ++ help ++ This module provides a driver and helper functions for Synopsys ++ DesignWare XPCS controllers. ++ ++config PCS_LYNX ++ tristate ++ help ++ This module provides helpers to phylink for managing the Lynx PCS ++ which is part of the Layerscape and QorIQ Ethernet SERDES. ++ ++config PCS_MTK_LYNXI ++ tristate ++ select REGMAP ++ help ++ This module provides helpers to phylink for managing the LynxI PCS ++ which is part of MediaTek's SoC and Ethernet switch ICs. ++ ++config PCS_MTK_USXGMII ++ tristate "MediaTek USXGMII PCS" ++ select OF_PCS ++ select PCS_MTK_LYNXI ++ select PHY_MTK_PEXTP ++ select PHYLINK ++ help ++ This module provides a driver for MediaTek's USXGMII PCS supporting ++ 10GBase-R, 5GBase-R and USXGMII interface modes. ++ 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same ++ differential pairs via an embedded LynxI PHY. ++ ++config PCS_RZN1_MIIC ++ tristate "Renesas RZ/N1 MII converter" ++ depends on OF && (ARCH_RZN1 || COMPILE_TEST) ++ help ++ This module provides a driver for the MII converter that is available ++ on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in ++ pass-through mode for MII. ++ ++endmenu --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile -@@ -8,3 +8,4 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o +@@ -8,4 +8,5 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs. + obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o - obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o +obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o + obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o --- /dev/null +++ b/drivers/net/pcs/pcs-mtk-usxgmii.c -@@ -0,0 +1,453 @@ +@@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. @@ -74,8 +134,11 @@ Signed-off-by: Daniel Golle +#include +#include +#include -+#include ++#include ++#include ++#include +#include ++#include + +/* USXGMII subsystem config registers */ +/* Register to control speed */ @@ -134,14 +197,12 @@ Signed-off-by: Daniel Golle + void __iomem *base; + struct clk *clk; + struct reset_control *reset; ++ struct phy *xfi_tphy; + phy_interface_t interface; + unsigned int neg_mode; + struct list_head node; +}; + -+static LIST_HEAD(mtk_usxgmii_pcs_instances); -+static DEFINE_MUTEX(instance_mutex); -+ +static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg) +{ + return ioread32(mpcs->base + reg); @@ -214,6 +275,7 @@ Signed-off-by: Daniel Golle + mode_changed = true; + } + ++ phy_reset(mpcs->xfi_tphy); + mtk_usxgmii_reset(mpcs); + + /* Setup USXGMII AN ctrl */ @@ -258,6 +320,9 @@ Signed-off-by: Daniel Golle + if (an_ctrl & USXGMII_AN_ENABLE) + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0); + ++ /* Setup PMA/PMD */ ++ phy_set_mode_ext(mpcs->xfi_tphy, PHY_MODE_ETHERNET, interface); ++ + return mode_changed; +} + @@ -328,7 +393,8 @@ Signed-off-by: Daniel Golle + phylink_decode_usxgmii_word(state, lpa); +} + -+static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ unsigned int neg_mode, + struct phylink_link_state *state) +{ + struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); @@ -368,10 +434,23 @@ Signed-off-by: Daniel Golle + phy_interface_t interface, + int speed, int duplex) +{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ + /* Reconfiguring USXGMII to ensure the quality of the RX signal + * after the line side link up. + */ + mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false); ++ phy_reset(mpcs->xfi_tphy); ++ phy_set_mode_ext(mpcs->xfi_tphy, PHY_MODE_ETHERNET, interface); ++} ++ ++static int mtk_usxgmii_pcs_enable(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ phy_power_on(mpcs->xfi_tphy); ++ ++ return 0; +} + +static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs) @@ -380,13 +459,31 @@ Signed-off-by: Daniel Golle + + mpcs->interface = PHY_INTERFACE_MODE_NA; + mpcs->neg_mode = -1; ++ ++ phy_power_off(mpcs->xfi_tphy); ++} ++ ++static unsigned int mtk_usxgmii_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return LINK_INBAND_ENABLE; ++ ++ default: ++ return 0; ++ } +} + +static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_inband_caps = mtk_usxgmii_pcs_inband_caps, + .pcs_config = mtk_usxgmii_pcs_config, + .pcs_get_state = mtk_usxgmii_pcs_get_state, + .pcs_an_restart = mtk_usxgmii_pcs_restart_an, + .pcs_link_up = mtk_usxgmii_pcs_link_up, ++ .pcs_enable = mtk_usxgmii_pcs_enable, + .pcs_disable = mtk_usxgmii_pcs_disable, +}; + @@ -409,10 +506,18 @@ Signed-off-by: Daniel Golle + mpcs->interface = PHY_INTERFACE_MODE_NA; + mpcs->neg_mode = -1; + ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, mpcs->pcs.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, mpcs->pcs.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, mpcs->pcs.supported_interfaces); ++ + mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL); + if (IS_ERR(mpcs->clk)) + return PTR_ERR(mpcs->clk); + ++ mpcs->xfi_tphy = devm_of_phy_get(mpcs->dev, dev->of_node, NULL); ++ if (IS_ERR(mpcs->xfi_tphy)) ++ return PTR_ERR(mpcs->xfi_tphy); ++ + mpcs->reset = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(mpcs->reset)) + return PTR_ERR(mpcs->reset); @@ -421,26 +526,19 @@ Signed-off-by: Daniel Golle + + platform_set_drvdata(pdev, mpcs); + -+ mutex_lock(&instance_mutex); -+ list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances); -+ mutex_unlock(&instance_mutex); -+ -+ return 0; ++ return fwnode_pcs_add_provider(dev_fwnode(dev), fwnode_pcs_simple_get, &mpcs->pcs); +} + +static void mtk_usxgmii_remove(struct platform_device *pdev) +{ -+ struct device *dev = &pdev->dev; -+ struct mtk_usxgmii_pcs *cur, *tmp; -+ -+ mutex_lock(&instance_mutex); -+ list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node) -+ if (cur->dev == dev) { -+ list_del(&cur->node); -+ break; -+ } -+ mutex_unlock(&instance_mutex); -+} ++ struct mtk_usxgmii_pcs *mpcs = platform_get_drvdata(pdev); ++ ++ fwnode_pcs_del_provider(dev_fwnode(&pdev->dev)); ++ ++ rtnl_lock(); ++ phylink_release_pcs(&mpcs->pcs); ++ rtnl_unlock(); ++}; + +static const struct of_device_id mtk_usxgmii_of_mtable[] = { + { .compatible = "mediatek,mt7988-usxgmiisys" }, @@ -448,60 +546,9 @@ Signed-off-by: Daniel Golle +}; +MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable); + -+struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) -+{ -+ struct platform_device *pdev; -+ struct mtk_usxgmii_pcs *mpcs; -+ -+ if (!np) -+ return NULL; -+ -+ if (!of_device_is_available(np)) -+ return ERR_PTR(-ENODEV); -+ -+ if (!of_match_node(mtk_usxgmii_of_mtable, np)) -+ return ERR_PTR(-EINVAL); -+ -+ pdev = of_find_device_by_node(np); -+ if (!pdev || !platform_get_drvdata(pdev)) { -+ if (pdev) -+ put_device(&pdev->dev); -+ return ERR_PTR(-EPROBE_DEFER); -+ } -+ -+ mpcs = platform_get_drvdata(pdev); -+ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); -+ -+ return &mpcs->pcs; -+} -+EXPORT_SYMBOL(mtk_usxgmii_pcs_get); -+ -+void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) -+{ -+ struct mtk_usxgmii_pcs *cur, *mpcs = NULL; -+ -+ if (!pcs) -+ return; -+ -+ mutex_lock(&instance_mutex); -+ list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node) -+ if (pcs == &cur->pcs) { -+ mpcs = cur; -+ break; -+ } -+ mutex_unlock(&instance_mutex); -+ -+ if (WARN_ON(!mpcs)) -+ return; -+ -+ put_device(mpcs->dev); -+} -+EXPORT_SYMBOL(mtk_usxgmii_pcs_put); -+ +static struct platform_driver mtk_usxgmii_driver = { + .driver = { -+ .name = "mtk_usxgmii", -+ .suppress_bind_attrs = true, ++ .name = "mtk-pcs-usxgmii", + .of_match_table = mtk_usxgmii_of_mtable, + }, + .probe = mtk_usxgmii_probe, @@ -512,33 +559,3 @@ Signed-off-by: Daniel Golle +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek USXGMII PCS driver"); +MODULE_AUTHOR("Daniel Golle "); ---- /dev/null -+++ b/include/linux/pcs/pcs-mtk-usxgmii.h -@@ -0,0 +1,27 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+#ifndef __LINUX_PCS_MTK_USXGMII_H -+#define __LINUX_PCS_MTK_USXGMII_H -+ -+#include -+ -+/** -+ * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance -+ * @np: Pointer to device node indentifying a MediaTek USXGMII PCS -+ * @mode: Ethernet PHY interface mode -+ * -+ * Return PCS identified by a device node and the PHY interface mode in use -+ * -+ * Return: Pointer to phylink PCS instance of NULL -+ */ -+#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII) -+struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np); -+void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs); -+#else -+static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) -+{ -+ return NULL; -+} -+static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { } -+#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */ -+ -+#endif /* __LINUX_PCS_MTK_USXGMII_H */ diff --git a/target/linux/generic/pending-6.18/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.18/741-net-phy-broadcom-update-dependency-condition.patch index 03e70b64938..d6a89a17b4d 100644 --- a/target/linux/generic/pending-6.18/741-net-phy-broadcom-update-dependency-condition.patch +++ b/target/linux/generic/pending-6.18/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -156,7 +156,7 @@ config BROADCOM_PHY +@@ -157,7 +157,7 @@ config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING diff --git a/target/linux/mediatek/dts/mt7987.dtsi b/target/linux/mediatek/dts/mt7987.dtsi index e5a534903e7..b0b883f6f42 100644 --- a/target/linux/mediatek/dts/mt7987.dtsi +++ b/target/linux/mediatek/dts/mt7987.dtsi @@ -185,6 +185,7 @@ <&sgmiisys0 CLK_SGM0_TX_EN>, <&sgmiisys0 CLK_SGM0_RX_EN>; clock-names = "sgmii_sel", "sgmii_tx", "sgmii_rx"; + #pcs-cells = <0>; }; }; @@ -204,6 +205,7 @@ <&sgmiisys1 CLK_SGM1_TX_EN>, <&sgmiisys1 CLK_SGM1_RX_EN>; clock-names = "sgmii_sel", "sgmii_tx", "sgmii_rx"; + #pcs-cells = <0>; }; }; diff --git a/target/linux/mediatek/patches-6.18/186-arm64-dts-mt7988a-complete-dtsi.patch b/target/linux/mediatek/patches-6.18/186-arm64-dts-mt7988a-complete-dtsi.patch index ca5402b75c4..32f8b679468 100644 --- a/target/linux/mediatek/patches-6.18/186-arm64-dts-mt7988a-complete-dtsi.patch +++ b/target/linux/mediatek/patches-6.18/186-arm64-dts-mt7988a-complete-dtsi.patch @@ -28,7 +28,7 @@ Work-in-progress patch to complete mt7988a.dtsi pcie0_pins: pcie0-pins { mux { function = "pcie"; -@@ -277,6 +284,60 @@ +@@ -277,6 +284,64 @@ status = "disabled"; }; @@ -47,6 +47,7 @@ Work-in-progress patch to complete mt7988a.dtsi + <&sgmiisys0 CLK_SGM0_TX_EN>, + <&sgmiisys0 CLK_SGM0_RX_EN>; + clock-names = "sgmii_sel", "sgmii_tx", "sgmii_rx"; ++ phys = <&xfi_tphy0>; + #pcs-cells = <0>; + }; + }; @@ -66,6 +67,7 @@ Work-in-progress patch to complete mt7988a.dtsi + <&sgmiisys1 CLK_SGM1_TX_EN>, + <&sgmiisys1 CLK_SGM1_RX_EN>; + clock-names = "sgmii_sel", "sgmii_tx", "sgmii_rx"; ++ phys = <&xfi_tphy1>; + #pcs-cells = <0>; + }; + }; @@ -75,6 +77,7 @@ Work-in-progress patch to complete mt7988a.dtsi + reg = <0 0x10080000 0 0x1000>; + resets = <&watchdog 12>; + clocks = <&topckgen CLK_TOP_USXGMII_SBUS_0_SEL>; ++ phys = <&xfi_tphy0>; + #pcs-cells = <0>; + }; + @@ -83,13 +86,14 @@ Work-in-progress patch to complete mt7988a.dtsi + reg = <0 0x10081000 0 0x1000>; + resets = <&watchdog 13>; + clocks = <&topckgen CLK_TOP_USXGMII_SBUS_1_SEL>; ++ phys = <&xfi_tphy1>; + #pcs-cells = <0>; + }; + mcusys: mcusys@100e0000 { compatible = "mediatek,mt7988-mcusys", "syscon"; reg = <0 0x100e0000 0 0x1000>; -@@ -318,6 +379,32 @@ +@@ -318,6 +383,32 @@ status = "disabled"; }; @@ -122,7 +126,7 @@ Work-in-progress patch to complete mt7988a.dtsi i2c0: i2c@11003000 { compatible = "mediatek,mt7981-i2c"; reg = <0 0x11003000 0 0x1000>, -@@ -424,7 +511,7 @@ +@@ -424,7 +515,7 @@ <0 0x0f0f0018 0 0x20>; }; @@ -131,7 +135,7 @@ Work-in-progress patch to complete mt7988a.dtsi compatible = "mediatek,mt7988-xhci", "mediatek,mtk-xhci"; reg = <0 0x11190000 0 0x2e00>, <0 0x11193e00 0 0x0100>; -@@ -458,6 +545,35 @@ +@@ -458,6 +549,35 @@ status = "disabled"; }; @@ -167,7 +171,7 @@ Work-in-progress patch to complete mt7988a.dtsi mmc0: mmc@11230000 { compatible = "mediatek,mt7988-mmc"; reg = <0 0x11230000 0 0x1000>, -@@ -720,6 +836,10 @@ +@@ -720,6 +840,10 @@ #address-cells = <1>; #size-cells = <1>; @@ -178,12 +182,11 @@ Work-in-progress patch to complete mt7988a.dtsi lvts_calibration: calib@918 { reg = <0x918 0x28>; }; -@@ -984,12 +1104,16 @@ +@@ -984,12 +1108,14 @@ gmac1: mac@1 { compatible = "mediatek,eth-mac"; reg = <1>; + pcs-handle = <&sgmiipcs1>, <&usxgmiisys1>; -+ phys = <&xfi_tphy1>; status = "disabled"; }; @@ -191,11 +194,10 @@ Work-in-progress patch to complete mt7988a.dtsi compatible = "mediatek,eth-mac"; reg = <2>; + pcs-handle = <&sgmiipcs0>, <&usxgmiisys0>; -+ phys = <&xfi_tphy0>; status = "disabled"; }; -@@ -1001,6 +1125,21 @@ +@@ -1001,6 +1127,21 @@ int_2p5g_phy: ethernet-phy@15 { compatible = "ethernet-phy-ieee802.3-c45"; reg = <15>; @@ -217,7 +219,7 @@ Work-in-progress patch to complete mt7988a.dtsi }; }; }; -@@ -1012,6 +1151,17 @@ +@@ -1012,6 +1153,17 @@ #size-cells = <1>; ranges = <0 0x15400000 0 0x200000>; }; diff --git a/target/linux/mediatek/patches-6.18/198-dts-mt7988a-enable-wed.patch b/target/linux/mediatek/patches-6.18/198-dts-mt7988a-enable-wed.patch index 8468adbb435..88f49c0e709 100644 --- a/target/linux/mediatek/patches-6.18/198-dts-mt7988a-enable-wed.patch +++ b/target/linux/mediatek/patches-6.18/198-dts-mt7988a-enable-wed.patch @@ -43,7 +43,7 @@ Signed-off-by: Daniel Golle }; soc { -@@ -866,6 +892,50 @@ +@@ -870,6 +896,50 @@ reg = <0 0x15000000 0 0x1000>; #clock-cells = <1>; #reset-cells = <1>; @@ -94,7 +94,7 @@ Signed-off-by: Daniel Golle }; switch: switch@15020000 { -@@ -1087,6 +1157,7 @@ +@@ -1091,6 +1161,7 @@ #size-cells = <0>; mediatek,ethsys = <ðsys>; mediatek,infracfg = <&topmisc>; @@ -102,7 +102,7 @@ Signed-off-by: Daniel Golle gmac0: mac@0 { compatible = "mediatek,eth-mac"; -@@ -1152,6 +1223,72 @@ +@@ -1154,6 +1225,72 @@ ranges = <0 0x15400000 0 0x200000>; }; diff --git a/target/linux/mediatek/patches-6.18/500-gsw-rtl8367s-mt7622-support.patch b/target/linux/mediatek/patches-6.18/500-gsw-rtl8367s-mt7622-support.patch index ed560c21c77..ceffc0d1149 100644 --- a/target/linux/mediatek/patches-6.18/500-gsw-rtl8367s-mt7622-support.patch +++ b/target/linux/mediatek/patches-6.18/500-gsw-rtl8367s-mt7622-support.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -470,6 +470,12 @@ config ROCKCHIP_PHY +@@ -471,6 +471,12 @@ config ROCKCHIP_PHY help Currently supports the integrated Ethernet PHY. diff --git a/target/linux/mediatek/patches-6.18/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.18/734-net-phy-add-Airoha-EN8801SC-PHY.patch index aba9c8926f0..f3f631a9329 100644 --- a/target/linux/mediatek/patches-6.18/734-net-phy-add-Airoha-EN8801SC-PHY.patch +++ b/target/linux/mediatek/patches-6.18/734-net-phy-add-Airoha-EN8801SC-PHY.patch @@ -25,7 +25,7 @@ Signed-off-by: Robert Marko + config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" - help + select PHY_COMMON_PROPS --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -44,6 +44,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/target/linux/mediatek/patches-6.18/736-net-pcs-mtk_usxgmii-add-polarity-control.patch b/target/linux/mediatek/patches-6.18/736-net-pcs-mtk_usxgmii-add-polarity-control.patch index 3b0b06bc108..092cd607e20 100644 --- a/target/linux/mediatek/patches-6.18/736-net-pcs-mtk_usxgmii-add-polarity-control.patch +++ b/target/linux/mediatek/patches-6.18/736-net-pcs-mtk_usxgmii-add-polarity-control.patch @@ -9,7 +9,7 @@ Signed-off-by: John Crispin --- --- a/drivers/net/pcs/pcs-mtk-usxgmii.c +++ b/drivers/net/pcs/pcs-mtk-usxgmii.c -@@ -52,6 +52,12 @@ +@@ -55,6 +55,12 @@ #define USXGMII_LPA GENMASK(15, 0) #define USXGMII_LPA_LATCH BIT(31) @@ -22,16 +22,16 @@ Signed-off-by: John Crispin /* Register to read PCS link status */ #define RG_PCS_RX_STATUS0 0x904 #define RG_PCS_RX_STATUS_UPDATE BIT(16) -@@ -74,6 +80,7 @@ struct mtk_usxgmii_pcs { - struct clk *clk; +@@ -78,6 +84,7 @@ struct mtk_usxgmii_pcs { struct reset_control *reset; + struct phy *xfi_tphy; phy_interface_t interface; + unsigned int polarity; unsigned int neg_mode; struct list_head node; }; -@@ -155,6 +162,10 @@ static int mtk_usxgmii_pcs_config(struct - +@@ -157,6 +164,10 @@ static int mtk_usxgmii_pcs_config(struct + phy_reset(mpcs->xfi_tphy); mtk_usxgmii_reset(mpcs); + /* Configure the interface polarity */ @@ -41,7 +41,7 @@ Signed-off-by: John Crispin /* Setup USXGMII AN ctrl */ mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, -@@ -332,6 +343,7 @@ static const struct phylink_pcs_ops mtk_ +@@ -369,6 +380,7 @@ static const struct phylink_pcs_ops mtk_ static int mtk_usxgmii_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -49,7 +49,7 @@ Signed-off-by: John Crispin struct mtk_usxgmii_pcs *mpcs; mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); -@@ -342,6 +354,13 @@ static int mtk_usxgmii_probe(struct plat +@@ -379,6 +391,13 @@ static int mtk_usxgmii_probe(struct plat if (IS_ERR(mpcs->base)) return PTR_ERR(mpcs->base); diff --git a/target/linux/mediatek/patches-6.18/737-net-dsa-add-Airoha-AN8855.patch b/target/linux/mediatek/patches-6.18/737-net-dsa-add-Airoha-AN8855.patch index 325ea352230..affbd025e41 100644 --- a/target/linux/mediatek/patches-6.18/737-net-dsa-add-Airoha-AN8855.patch +++ b/target/linux/mediatek/patches-6.18/737-net-dsa-add-Airoha-AN8855.patch @@ -263,7 +263,7 @@ Christian Marangi (9): + config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" - help + select PHY_COMMON_PROPS --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -45,6 +45,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/target/linux/mediatek/patches-6.18/740-net-pcs-mtk_lynxi-add-mt7987-support.patch b/target/linux/mediatek/patches-6.18/740-net-pcs-mtk_lynxi-add-mt7987-support.patch index dffa05c00d9..009b80ab623 100644 --- a/target/linux/mediatek/patches-6.18/740-net-pcs-mtk_lynxi-add-mt7987-support.patch +++ b/target/linux/mediatek/patches-6.18/740-net-pcs-mtk_lynxi-add-mt7987-support.patch @@ -10,9 +10,9 @@ Signed-off-by: Bo-Cun Chen --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c -@@ -416,9 +416,12 @@ static int mtk_pcs_lynxi_probe(struct pl - if (of_property_read_bool(np->parent, "mediatek,pnswap")) - flags |= MTK_SGMII_FLAG_PN_SWAP; +@@ -540,9 +540,12 @@ static int mtk_pcs_lynxi_probe(struct pl + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); - if (IS_ERR(mpcs->rstc)) @@ -26,8 +26,8 @@ Signed-off-by: Bo-Cun Chen reset_control_deassert(mpcs->rstc); mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel"); -@@ -465,6 +468,7 @@ static void mtk_pcs_lynxi_remove(struct - } +@@ -586,6 +589,7 @@ static void mtk_pcs_lynxi_remove(struct + }; static const struct of_device_id mtk_pcs_lynxi_of_match[] = { + { .compatible = "mediatek,mt7987-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 }, diff --git a/target/linux/mediatek/patches-6.18/741-net-pcs-mtk-lynxi-add-phya-tx-rx-clock-path.patch b/target/linux/mediatek/patches-6.18/741-net-pcs-mtk-lynxi-add-phya-tx-rx-clock-path.patch index 67f2ddc496c..bd0159f3149 100644 --- a/target/linux/mediatek/patches-6.18/741-net-pcs-mtk-lynxi-add-phya-tx-rx-clock-path.patch +++ b/target/linux/mediatek/patches-6.18/741-net-pcs-mtk-lynxi-add-phya-tx-rx-clock-path.patch @@ -11,7 +11,7 @@ Signed-off-by: Bo-Cun Chen --- --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c -@@ -25,6 +25,7 @@ +@@ -30,6 +30,7 @@ #define SGMSYS_PCS_CONTROL_1 0x0 #define SGMII_BMCR GENMASK(15, 0) #define SGMII_BMSR GENMASK(31, 16) @@ -19,7 +19,7 @@ Signed-off-by: Bo-Cun Chen #define SGMSYS_PCS_DEVICE_ID 0x4 #define SGMII_LYNXI_DEV_ID 0x4d544950 -@@ -52,6 +53,8 @@ +@@ -57,6 +58,8 @@ #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) #define SGMII_DUPLEX_HALF BIT(4) #define SGMII_REMOTE_FAULT_DIS BIT(8) @@ -28,17 +28,25 @@ Signed-off-by: Bo-Cun Chen /* Register to reset SGMII design */ #define SGMSYS_RESERVED_0 0x34 -@@ -166,7 +169,7 @@ static int mtk_pcs_lynxi_config(struct p +@@ -76,6 +79,7 @@ + #define SGMII_PN_SWAP_RX BIT(1) + #define SGMII_PN_SWAP_TX BIT(0) + ++#define MTK_SGMII_FLAG_PHYA_TRX_CK BIT(2) + + #define MTK_NETSYS_V3_AMA_RGC3 0x128 + +@@ -195,7 +199,7 @@ static int mtk_pcs_lynxi_config(struct p { struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); bool mode_changed = false, changed; -- unsigned int rgc3, sgm_mode, bmcr = 0; -+ unsigned int rgc3, sgm_mode, bmcr = 0, trxbuf_thr = 0x3112; +- unsigned int rgc3, sgm_mode, bmcr; ++ unsigned int rgc3, sgm_mode, bmcr, trxbuf_thr = 0x3112; int advertise, link_timer; + int ret; - advertise = phylink_mii_c22_pcs_encode_advertisement(interface, -@@ -193,6 +196,12 @@ static int mtk_pcs_lynxi_config(struct p - bmcr = BMCR_ANENABLE; +@@ -229,6 +233,12 @@ static int mtk_pcs_lynxi_config(struct p + bmcr = 0; } + /* Configure SGMII PCS clock source */ @@ -50,7 +58,7 @@ Signed-off-by: Bo-Cun Chen if (mpcs->interface != interface) { link_timer = phylink_get_link_timer_ns(interface); if (link_timer < 0) -@@ -235,12 +244,14 @@ static int mtk_pcs_lynxi_config(struct p +@@ -273,12 +283,14 @@ static int mtk_pcs_lynxi_config(struct p /* Update the sgmsys mode register */ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, @@ -67,23 +75,30 @@ Signed-off-by: Bo-Cun Chen /* Release PHYA power down state * Only removing bit SGMII_PHYA_PWD isn't enough. -@@ -416,6 +427,9 @@ static int mtk_pcs_lynxi_probe(struct pl - if (of_property_read_bool(np->parent, "mediatek,pnswap")) - flags |= MTK_SGMII_FLAG_PN_SWAP; +@@ -453,7 +465,7 @@ static struct phylink_pcs *mtk_pcs_lynxi + struct mtk_pcs_lynxi *prealloc) + { + struct mtk_pcs_lynxi *mpcs; +- u32 id, ver; ++ u32 id, ver, flags = 0; + int ret; -+ if (of_property_read_bool(np->parent, "mediatek,phya_trx_ck")) + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); +@@ -475,6 +487,9 @@ static struct phylink_pcs *mtk_pcs_lynxi + return ERR_PTR(-ENODEV); + } + ++ if (fwnode_device_is_compatible(fwnode, "mediatek,mt7987-sgmii")) + flags |= MTK_SGMII_FLAG_PHYA_TRX_CK; + - if (of_parse_phandle(np->parent, "resets", 0)) { - mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); - if (IS_ERR(mpcs->rstc)) ---- a/include/linux/pcs/pcs-mtk-lynxi.h -+++ b/include/linux/pcs/pcs-mtk-lynxi.h -@@ -6,6 +6,7 @@ - #include + dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, + ver); + +@@ -491,6 +506,7 @@ static struct phylink_pcs *mtk_pcs_lynxi + mpcs->pcs.ops = &mtk_pcs_lynxi_ops; + mpcs->pcs.poll = true; + mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->flags = flags; + mpcs->fwnode = fwnode_handle_get(fwnode); + INIT_DELAYED_WORK(&mpcs->link_poll, mtk_pcs_lynxi_link_poll); - #define MTK_SGMII_FLAG_PN_SWAP BIT(0) -+#define MTK_SGMII_FLAG_PHYA_TRX_CK BIT(2) - struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, - struct regmap *regmap, - u32 ana_rgc3, u32 flags); diff --git a/target/linux/mediatek/patches-6.18/750-net-ethernet-mtk_eth_soc-add-mt7987-support.patch b/target/linux/mediatek/patches-6.18/750-net-ethernet-mtk_eth_soc-add-mt7987-support.patch index caa1c8c77eb..9e04e5af6e3 100644 --- a/target/linux/mediatek/patches-6.18/750-net-ethernet-mtk_eth_soc-add-mt7987-support.patch +++ b/target/linux/mediatek/patches-6.18/750-net-ethernet-mtk_eth_soc-add-mt7987-support.patch @@ -46,7 +46,7 @@ Signed-off-by: Bo-Cun Chen .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY, --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -834,10 +834,16 @@ static void mtk_set_queue_speed(struct m +@@ -794,10 +794,16 @@ static void mtk_set_queue_speed(struct m return; val = MTK_QTX_SCH_MIN_RATE_EN | @@ -66,7 +66,7 @@ Signed-off-by: Bo-Cun Chen if (mtk_is_netsys_v1(eth)) val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; -@@ -864,6 +870,30 @@ static void mtk_set_queue_speed(struct m +@@ -824,6 +830,30 @@ static void mtk_set_queue_speed(struct m default: break; } @@ -97,7 +97,7 @@ Signed-off-by: Bo-Cun Chen } else { switch (speed) { case SPEED_10: -@@ -944,7 +974,7 @@ static void mtk_xgdm_mac_link_up(struct +@@ -904,7 +934,7 @@ static void mtk_xgdm_mac_link_up(struct return; /* Eliminate the interference(before link-up) caused by PHY noise */ @@ -106,7 +106,7 @@ Signed-off-by: Bo-Cun Chen mdelay(20); mtk_m32(mac->hw, XMAC_GLB_CNTCLR, XMAC_GLB_CNTCLR, MTK_XMAC_CNT_CTRL(mac->id)); -@@ -2982,10 +3012,16 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2937,10 +2967,16 @@ static int mtk_tx_alloc(struct mtk_eth * mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs); val = MTK_QTX_SCH_MIN_RATE_EN | @@ -126,7 +126,7 @@ Signed-off-by: Bo-Cun Chen if (mtk_is_netsys_v1(eth)) val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); -@@ -5967,6 +6003,36 @@ static const struct mtk_soc_data mt7986_ +@@ -5918,6 +5954,36 @@ static const struct mtk_soc_data mt7986_ }, }; @@ -163,7 +163,7 @@ Signed-off-by: Bo-Cun Chen static const struct mtk_soc_data mt7988_data = { .reg_map = &mt7988_reg_map, .ana_rgc3 = 0x128, -@@ -6028,6 +6094,7 @@ const struct of_device_id of_mtk_match[] +@@ -5979,6 +6045,7 @@ const struct of_device_id of_mtk_match[] { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data }, { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data }, { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data }, @@ -173,7 +173,7 @@ Signed-off-by: Bo-Cun Chen {}, --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -264,6 +264,13 @@ +@@ -263,6 +263,13 @@ #define MTK_QTX_SCH_MAX_RATE_MAN GENMASK(10, 4) #define MTK_QTX_SCH_MAX_RATE_EXP GENMASK(3, 0) @@ -187,7 +187,7 @@ Signed-off-by: Bo-Cun Chen /* QDMA TX Scheduler Rate Control Register */ #define MTK_QDMA_TX_SCH_MAX_WFQ BIT(15) -@@ -539,9 +546,23 @@ +@@ -538,9 +545,23 @@ #define XMAC_MCR_FORCE_RX_FC BIT(4) /* XFI Mac logic reset registers */ @@ -212,7 +212,7 @@ Signed-off-by: Bo-Cun Chen /* XFI Mac count global control */ #define MTK_XMAC_CNT_CTRL(x) (MTK_XMAC_BASE(x) + 0x100) #define XMAC_GLB_CNTCLR BIT(0) -@@ -842,6 +863,17 @@ enum mtk_clks_map { +@@ -841,6 +862,17 @@ enum mtk_clks_map { BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \ BIT_ULL(MTK_CLK_SGMII2_CDR_FB)) @@ -230,7 +230,7 @@ Signed-off-by: Bo-Cun Chen #define MT7988_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_ESW) | \ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \ BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \ -@@ -998,12 +1030,14 @@ enum mkt_eth_capabilities { +@@ -997,12 +1029,14 @@ enum mkt_eth_capabilities { MTK_RSTCTRL_PPE2_BIT, MTK_U3_COPHY_V2_BIT, MTK_SRAM_BIT, @@ -246,7 +246,7 @@ Signed-off-by: Bo-Cun Chen MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT, MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, -@@ -1045,14 +1079,16 @@ enum mkt_eth_capabilities { +@@ -1044,14 +1078,16 @@ enum mkt_eth_capabilities { #define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT) #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT) #define MTK_SRAM BIT_ULL(MTK_SRAM_BIT) @@ -265,7 +265,7 @@ Signed-off-by: Bo-Cun Chen #define MTK_ETH_MUX_GMAC2_TO_2P5GPHY \ BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT) #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ -@@ -1084,12 +1120,13 @@ enum mkt_eth_capabilities { +@@ -1083,12 +1119,13 @@ enum mkt_eth_capabilities { #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) @@ -283,7 +283,7 @@ Signed-off-by: Bo-Cun Chen /* MUXes present on SoCs */ /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ -@@ -1099,9 +1136,9 @@ enum mkt_eth_capabilities { +@@ -1098,9 +1135,9 @@ enum mkt_eth_capabilities { #define MTK_MUX_GMAC2_GMAC0_TO_GEPHY \ (MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY | MTK_MUX | MTK_INFRA) @@ -296,7 +296,7 @@ Signed-off-by: Bo-Cun Chen /* 2: GMAC1 -> SGMII, 3: GMAC2 -> SGMII */ #define MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ -@@ -1141,18 +1178,24 @@ enum mkt_eth_capabilities { +@@ -1140,18 +1177,24 @@ enum mkt_eth_capabilities { #define MT7629_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW | \ MTK_MUX_GMAC2_GMAC0_TO_GEPHY | \ diff --git a/target/linux/mediatek/patches-6.18/751-net-ethernet-mtk_eth_soc-revise-hardware-configuration-for-mt7987.patch b/target/linux/mediatek/patches-6.18/751-net-ethernet-mtk_eth_soc-revise-hardware-configuration-for-mt7987.patch index 3f3a7bb3507..6ca9beb49c1 100644 --- a/target/linux/mediatek/patches-6.18/751-net-ethernet-mtk_eth_soc-revise-hardware-configuration-for-mt7987.patch +++ b/target/linux/mediatek/patches-6.18/751-net-ethernet-mtk_eth_soc-revise-hardware-configuration-for-mt7987.patch @@ -15,7 +15,7 @@ Signed-off-by: Bo-Cun Chen --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4568,27 +4568,40 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -4517,27 +4517,40 @@ static int mtk_hw_init(struct mtk_eth *e mtk_w32(eth, PSE_DUMMY_WORK_GDM(1) | PSE_DUMMY_WORK_GDM(2) | PSE_DUMMY_WORK_GDM(3) | DUMMY_PAGE_THR, PSE_DUMY_REQ); diff --git a/target/linux/mediatek/patches-6.18/965-dts-mt7988a-add-trng-support.patch b/target/linux/mediatek/patches-6.18/965-dts-mt7988a-add-trng-support.patch index f08e9e59975..8789f187c47 100644 --- a/target/linux/mediatek/patches-6.18/965-dts-mt7988a-add-trng-support.patch +++ b/target/linux/mediatek/patches-6.18/965-dts-mt7988a-add-trng-support.patch @@ -11,7 +11,7 @@ Signed-off-by: Marcos Alano --- --- a/arch/arm64/boot/dts/mediatek/mt7988a.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7988a.dtsi -@@ -1332,4 +1332,8 @@ +@@ -1334,4 +1334,8 @@ , ; }; diff --git a/target/linux/realtek/patches-6.18/714-net-phy-sfp-add-support-for-SMBus.patch b/target/linux/realtek/patches-6.18/714-net-phy-sfp-add-support-for-SMBus.patch index 590345be343..23e17606517 100644 --- a/target/linux/realtek/patches-6.18/714-net-phy-sfp-add-support-for-SMBus.patch +++ b/target/linux/realtek/patches-6.18/714-net-phy-sfp-add-support-for-SMBus.patch @@ -10,7 +10,7 @@ Signed-off-by: Antoine Tenart --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c -@@ -826,6 +826,29 @@ static int sfp_i2c_mdiobus_create(struct +@@ -845,6 +845,29 @@ static int sfp_i2c_mdiobus_create(struct return 0; } @@ -40,7 +40,7 @@ Signed-off-by: Antoine Tenart static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) { mdiobus_unregister(sfp->i2c_mii); -@@ -2000,9 +2023,15 @@ static void sfp_sm_fault(struct sfp *sfp +@@ -2019,9 +2042,15 @@ static void sfp_sm_fault(struct sfp *sfp static int sfp_sm_add_mdio_bus(struct sfp *sfp) { diff --git a/target/linux/realtek/patches-6.18/730-add-pcs-rtl-otto.patch b/target/linux/realtek/patches-6.18/730-add-pcs-rtl-otto.patch index b32f2a1bb3b..6fee8268847 100644 --- a/target/linux/realtek/patches-6.18/730-add-pcs-rtl-otto.patch +++ b/target/linux/realtek/patches-6.18/730-add-pcs-rtl-otto.patch @@ -11,9 +11,9 @@ Signed-off-by: Markus Stockhausen --- --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig -@@ -36,6 +36,14 @@ config PCS_MTK_USXGMII +@@ -45,6 +45,14 @@ config PCS_MTK_USXGMII 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same - differential pairs via an embedded LynxI PHY. + differential pairs via an embedded LynxI PCS. +config PCS_RTL_OTTO + tristate "Realtek Otto SerDes PCS" @@ -28,10 +28,9 @@ Signed-off-by: Markus Stockhausen depends on OF --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile -@@ -7,5 +7,6 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs. - obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o +@@ -9,4 +9,5 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o + obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o +obj-$(CONFIG_PCS_RTL_OTTO) += pcs-rtl-otto.o obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o - obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o diff --git a/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch b/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch index 5452551d8f7..c6674ba06bb 100644 --- a/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch +++ b/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch @@ -25,7 +25,7 @@ Signed-off-by: Jonas Jelonek --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c -@@ -785,21 +785,29 @@ static int sfp_smbus_byte_write(struct s +@@ -804,21 +804,29 @@ static int sfp_smbus_byte_write(struct s static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) { diff --git a/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch b/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch index 6391f569090..8d42b5cff17 100644 --- a/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch +++ b/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch @@ -42,7 +42,7 @@ Signed-off-by: Jonas Jelonek #include #include "sfp.h" -@@ -734,50 +735,101 @@ static int sfp_i2c_write(struct sfp *sfp +@@ -753,50 +754,101 @@ static int sfp_i2c_write(struct sfp *sfp return ret == ARRAY_SIZE(msgs) ? len : 0; } @@ -164,7 +164,7 @@ Signed-off-by: Jonas Jelonek } return data - (u8 *)buf; -@@ -786,17 +838,32 @@ static int sfp_smbus_byte_write(struct s +@@ -805,17 +857,32 @@ static int sfp_smbus_byte_write(struct s static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) { size_t max_block_size;