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 74731f069a..5fab4e3365 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 0000000000..65f0496b75 --- /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 0000000000..d1cd6f1712 --- /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 0000000000..c1f397fc7a --- /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 0000000000..543e718c6f --- /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 0000000000..fd74b41cd6 --- /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 0000000000..70f9a55959 --- /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 0000000000..9679527758 --- /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 0000000000..2dadefb549 --- /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 0000000000..6109c44c85 --- /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 0000000000..456e00c8a3 --- /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 0000000000..dc5051c72d --- /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 0000000000..6b372cf27d --- /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 0000000000..3ebff43757 --- /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 edbf09126f..9cacc85ea8 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 e5592d99b5..eafa39b523 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 24136dd6b4..0000000000 --- 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 4b29cc0010..1c9e5bac21 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 5823744ff4..d7e29833fe 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 0000000000..dd2c60d723 --- /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 34b4e544a2..d8595e4dfd 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 0000000000..f0a4d5306c --- /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 0000000000..cbb0a50741 --- /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 0000000000..e884e70f28 --- /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 0000000000..676e23c001 --- /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 0000000000..9e8f9721ab --- /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 0000000000..ac4154b857 --- /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 0000000000..b707508f07 --- /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 0000000000..ec920fa217 --- /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 5175d451e8..ec56259584 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)); +@@ -686,8 +703,22 @@ static void mtk_mac_config(struct phylin -- 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)); + 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); -+ } ++ /* 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; +@@ -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 -+ /* 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; + 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)); -+ -+ err = PTR_ERR(mac->sgmii_pcs); -+ goto free_netdev; -+ } -+ } +- int id, err; ++ int id, err, count; ++ unsigned int sid; + int txqs = 1; + u32 val; -- 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)); -+ -+ err = PTR_ERR(mac->usxgmii_pcs); -+ goto free_netdev; -+ } -+ } +@@ -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; -- 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)); + /* 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); + -+ err = PTR_ERR(mac->pextp); -+ goto free_netdev; ++ 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; ++ } ++ ++ if (count > 2) ++ err = -ENOMEM; ++ ++ if (err) ++ goto free_netdev; ++ ++ err = fwnode_phylink_pcs_parse(of_fwnode_handle(np), mac->available_pcs, &count); ++ if (err) ++ 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; - } +- 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; -+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; + } + -+ 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 c479c80a53..5850e41899 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 80b3a2c337..3dc8c8f6f9 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 1f1c40b1d9..0000000000 --- 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 8ac059ef5e..7093797af7 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; ++ struct mtk_pcs_lynxi *mpcs = platform_get_drvdata(pdev); + -+ 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); -+} ++ 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 215bd2ca2e..46694c8534 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 686c0293a1..83336476d4 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,12 +31,65 @@ 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 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 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 @@ -46,19 +99,26 @@ Signed-off-by: Daniel Golle + 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, RZ/N2H, RZ/T2H MII converter" - depends on OF ++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; ++ struct mtk_usxgmii_pcs *mpcs = platform_get_drvdata(pdev); + -+ 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); -+} ++ 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 03e70b6493..d6a89a17b4 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 e5a534903e..b0b883f6f4 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 ca5402b75c..32f8b67946 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 8468adbb43..88f49c0e70 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 ed560c21c7..ceffc0d114 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 aba9c8926f..f3f631a932 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 3b0b06bc10..092cd607e2 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 325ea35223..affbd025e4 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 dffa05c00d..009b80ab62 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 67f2ddc496..bd0159f314 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 caa1c8c77e..9e04e5af6e 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 3f3a7bb350..6ca9beb49c 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 f08e9e5997..8789f187c4 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 590345be34..23e1760651 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 b32f2a1bb3..6fee826884 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 5452551d8f..c6674ba06b 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 6391f56909..8d42b5cff1 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;