From 53d14ca9a6daa5edee979a8b2d053acc269cd73c Mon Sep 17 00:00:00 2001
From: Josef Schlehofer <josef.schlehofer@nic.cz>
Date: Tue, 29 Nov 2022 11:10:09 +0100
Subject: [PATCH] patches/openwrt: 5.15: generic pending patches: rebase

---
 .../0003-Generic-pending-patches-5.15.patch   | 10847 ++++++++++------
 1 file changed, 6870 insertions(+), 3977 deletions(-)

diff --git a/patches/openwrt/a-new-kernel-5.15/0003-Generic-pending-patches-5.15.patch b/patches/openwrt/a-new-kernel-5.15/0003-Generic-pending-patches-5.15.patch
index 2a31394ea..b85c89d88 100644
--- a/patches/openwrt/a-new-kernel-5.15/0003-Generic-pending-patches-5.15.patch
+++ b/patches/openwrt/a-new-kernel-5.15/0003-Generic-pending-patches-5.15.patch
@@ -1,13 +1,13 @@
-From 86b7905251dc6b34461f91fc32be6775fc7bf165 Mon Sep 17 00:00:00 2001
+From dde8512fc111ac1c1f5df602f86c4810651c109a Mon Sep 17 00:00:00 2001
 From: Josef Schlehofer <pepe.schlehofer@gmail.com>
-Date: Mon, 4 Jul 2022 14:20:22 +0200
+Date: Tue, 29 Nov 2022 10:55:43 +0100
 Subject: [PATCH] Generic pending 5.15 patches
 
 ---
- ...terrupt-provider-address-cells-check.patch |   28 +
  ...include-asm-rwonce.h-for-kernel-code.patch |   29 +
- ...-Use-stddefs.h-instead-of-compiler.h.patch |   26 +
+ ...-Use-stddefs.h-instead-of-compiler.h.patch |   22 +
  ...s-negative-stack-offsets-on-stack-tr.patch |   57 +
+ .../103-kbuild-export-SUBARCH.patch           |   21 +
  ...e_mem_map-with-ARCH_PFN_OFFSET-calcu.patch |   82 +
  ...0-add-linux-spidev-compatible-si3210.patch |   18 +
  ...ame2-and-add-RENAME_WHITEOUT-support.patch |   81 +
@@ -26,13 +26,12 @@ Subject: [PATCH] Generic pending 5.15 patches
  .../pending-5.15/305-mips_module_reloc.patch  |  370 ++
  .../307-mips_highmem_offset.patch             |   19 +
  .../pending-5.15/308-mips32r2_tune.patch      |   22 +
- ...CPU-option-reporting-to-proc-cpuinfo.patch |  136 +
  .../310-arm_module_unresolved_weak_sym.patch  |   22 +
- ...t-command-line-parameters-from-users.patch |  283 +
+ ...t-command-line-parameters-from-users.patch |  282 +
  .../332-arc-add-OWRTDTB-section.patch         |   84 +
  ...able-unaligned-access-in-kernel-mode.patch |   24 +
  ...ernel-XZ-compression-option-on-PPC_8.patch |   25 +
- .../400-mtd-mtdsplit-support.patch            |  339 ++
+ .../400-mtd-mtdsplit-support.patch            |  328 ++
  ...support-for-minor-aligned-partitions.patch |  245 +
  .../pending-5.15/420-mtd-redboot_space.patch  |   41 +
  ...30-mtd-add-myloader-partition-parser.patch |  229 +
@@ -45,23 +44,22 @@ Subject: [PATCH] Generic pending 5.15 patches
  .../476-mtd-spi-nor-add-eon-en25q128.patch    |   18 +
  .../479-mtd-spi-nor-add-xtx-xt25f128b.patch   |   79 +
  ...r-add-support-for-Gigadevice-GD25D05.patch |   22 +
- .../482-mtd-spi-nor-add-gd25q512.patch        |   25 +
- ...spinand-add-support-for-xtx-xt26g0xa.patch |  178 +
- .../484-mtd-spi-nor-add-esmt-f25l16pa.patch   |   27 +
- .../485-mtd-spi-nor-add-xmc-xm25qh128c.patch  |   28 +
+ .../482-mtd-spi-nor-add-gd25q512.patch        |   21 +
+ .../484-mtd-spi-nor-add-esmt-f25l16pa.patch   |   23 +
+ .../485-mtd-spi-nor-add-xmc-xm25qh128c.patch  |   24 +
  ...nand-add-support-for-ESMT-F50x1G41LB.patch |  143 +
+ ...nd-Add-support-for-Etron-EM73D044VCx.patch |  168 +
  ...mtd-device-named-ubi-or-data-on-boot.patch |   97 +
  ...to-create-ubiblock-device-for-rootfs.patch |   69 +
  ...ting-ubi0-rootfs-in-init-do_mounts.c.patch |   53 +
  ...ROOT_DEV-to-ubiblock-rootfs-if-unset.patch |   34 +
  .../494-mtd-ubi-add-EOF-marker-support.patch  |   60 +
- ...-mtd-core-add-get_mtd_device_by_node.patch |   75 +
  ...-add-bindings-for-mtd-concat-devices.patch |   52 +
  ...cat-add-dt-driver-for-concat-devices.patch |  216 +
  ...i-nor-locking-support-for-MX25L6405D.patch |   34 +
  ...i-nor-disable-16-bit-sr-for-macronix.patch |   30 +
- .../500-fs_cdrom_dependencies.patch           |   62 +
- .../530-jffs2_make_lzma_available.patch       | 5180 +++++++++++++++++
+ .../500-fs_cdrom_dependencies.patch           |   52 +
+ .../530-jffs2_make_lzma_available.patch       | 4581 +++++++++++++++++
  .../pending-5.15/532-jffs2_eofdetect.patch    |   65 +
  .../600-netfilter_conntrack_flush.patch       |   88 +
  ...etfilter_match_bypass_default_checks.patch |  110 +
@@ -76,11 +74,43 @@ Subject: [PATCH] Generic pending 5.15 patches
  ...nes-for-_POLICY_FAILED-until-all-cod.patch |   50 +
  ...T-skip-GRO-for-foreign-MAC-addresses.patch |  149 +
  ...et-add-mac-address-increment-support.patch |   89 +
- ...83-of_net-add-mac-address-to-of-tree.patch |   59 +
+ ...83-of_net-add-mac-address-to-of-tree.patch |   55 +
+ ...t-do-mac-address-increment-only-once.patch |   31 +
+ ...ow_offload-handle-netdevice-events-f.patch |  106 +
+ ...er-nf_flow_table-add-missing-locking.patch |   39 +
  ...net-mtk_eth_soc-enable-threaded-NAPI.patch |   41 +
  ...detach-callback-to-struct-phy_driver.patch |   38 +
  ...a-tag_mtk-add-padding-for-tx-packets.patch |   29 +
  ...d-knob-for-filtering-rx-tx-BPDU-pack.patch |  174 +
+ ...rtl8221-allow-to-configure-SERDES-mo.patch |  101 +
+ ...e-all-MACs-are-powered-down-before-r.patch |   28 +
+ ...-net-mtk_sgmii-implement-mtk_pcs_ops.patch |   46 +
+ ..._sgmii-fix-powering-up-the-SGMII-phy.patch |   39 +
+ ...sure-the-SGMII-PHY-is-powered-down-o.patch |   65 +
+ ...k_pcs_setup_mode_an-don-t-rely-on-re.patch |   31 +
+ ...t-the-speed-according-to-the-phy-int.patch |   47 +
+ .../729-net-mtk_eth_soc-improve-comment.patch |   22 +
+ ...enable-PCS-polling-to-allow-SFP-work.patch |   23 +
+ ...iatek-ppe-add-support-for-flow-accou.patch |  409 ++
+ ..._eth_soc-account-for-vlan-in-rx-head.patch |   22 +
+ ..._eth_soc-increase-tx-ring-side-for-Q.patch |  143 +
+ ..._eth_soc-avoid-port_mg-assignment-on.patch |   52 +
+ ..._eth_soc-implement-multi-queue-suppo.patch |  654 +++
+ ...t-dsa-tag_mtk-assign-per-port-queues.patch |   20 +
+ ...iatek-ppe-assign-per-port-queues-for.patch |   93 +
+ ..._eth_soc-compile-out-netsys-v2-code-.patch |   28 +
+ ...ort-for-DSA-rx-offloading-via-metada.patch |   72 +
+ ..._eth_soc-fix-VLAN-rx-hardware-accele.patch |  192 +
+ ..._eth_soc-work-around-issue-with-send.patch |   78 +
+ ...rnet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch |   21 +
+ ..._eth_soc-drop-packets-to-WDMA-if-the.patch |   37 +
+ ..._eth_soc-fix-flow_offload-related-re.patch |   52 +
+ ..._eth_soc-drop-generic-vlan-rx-offloa.patch |  181 +
+ ...et-mtk_wed-introduce-wed-mcu-support.patch |  591 +++
+ ...net-mtk_wed-introduce-wed-wo-support.patch |  737 +++
+ ..._wed-rename-tx_wdma-array-in-rx_wdma.patch |   79 +
+ ...mtk_wed-add-configure-wed-wo-support.patch | 1521 ++++++
+ ...ethernet-mtk_wed-add-rx-mib-counters.patch |  149 +
  ...equest-assisted-learning-on-CPU-port.patch |   27 +
  ...-missing-linux-if_ether.h-for-ETH_AL.patch |   61 +
  ...ice-struct-copy-its-DMA-params-to-th.patch |   73 +
@@ -90,20 +120,13 @@ Subject: [PATCH] Generic pending 5.15 patches
  ...problem-with-platfom-data-in-w1-gpio.patch |   26 +
  .../pending-5.15/834-ledtrig-libata.patch     |  149 +
  ...40-hwrng-bcm2835-set-quality-to-1000.patch |   26 +
- ..._wwan-add-ZTE-MF286D-modem-19d2-1485.patch |   59 +
  ...e-main-irq_chip-structure-a-static-d.patch |  102 +
- ...mvebu-a3700-comphy-Remove-port-from-.patch |  217 +
- ...mvebu-a3700-comphy-Add-native-kernel.patch | 1564 +++++
- ...l-armada-37xx-Add-xtal-clock-to-comp.patch |   31 +
- ...mvebu-Make-SATA-PHY-optional-for-Arm.patch |   61 +
- ...xhci-mvebu-make-USB-3.0-PHY-optional.patch |  163 +
- ...ark-Fix-initialization-with-old-Marv.patch |   36 +
  .../pending-5.15/920-mangle_bootargs.patch    |   71 +
- 95 files changed, 14636 insertions(+)
- create mode 100644 target/linux/generic/pending-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch
+ 118 files changed, 17345 insertions(+)
  create mode 100644 target/linux/generic/pending-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
  create mode 100644 target/linux/generic/pending-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch
  create mode 100644 target/linux/generic/pending-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
+ create mode 100644 target/linux/generic/pending-5.15/103-kbuild-export-SUBARCH.patch
  create mode 100644 target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
  create mode 100644 target/linux/generic/pending-5.15/130-add-linux-spidev-compatible-si3210.patch
  create mode 100644 target/linux/generic/pending-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch
@@ -122,7 +145,6 @@ Subject: [PATCH] Generic pending 5.15 patches
  create mode 100644 target/linux/generic/pending-5.15/305-mips_module_reloc.patch
  create mode 100644 target/linux/generic/pending-5.15/307-mips_highmem_offset.patch
  create mode 100644 target/linux/generic/pending-5.15/308-mips32r2_tune.patch
- create mode 100644 target/linux/generic/pending-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch
  create mode 100644 target/linux/generic/pending-5.15/310-arm_module_unresolved_weak_sym.patch
  create mode 100644 target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
  create mode 100644 target/linux/generic/pending-5.15/332-arc-add-OWRTDTB-section.patch
@@ -142,16 +164,15 @@ Subject: [PATCH] Generic pending 5.15 patches
  create mode 100644 target/linux/generic/pending-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch
  create mode 100644 target/linux/generic/pending-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch
  create mode 100644 target/linux/generic/pending-5.15/482-mtd-spi-nor-add-gd25q512.patch
- create mode 100644 target/linux/generic/pending-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch
  create mode 100644 target/linux/generic/pending-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch
  create mode 100644 target/linux/generic/pending-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
  create mode 100644 target/linux/generic/pending-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch
+ create mode 100644 target/linux/generic/pending-5.15/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
  create mode 100644 target/linux/generic/pending-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
  create mode 100644 target/linux/generic/pending-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch
  create mode 100644 target/linux/generic/pending-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch
  create mode 100644 target/linux/generic/pending-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch
  create mode 100644 target/linux/generic/pending-5.15/494-mtd-ubi-add-EOF-marker-support.patch
- create mode 100644 target/linux/generic/pending-5.15/495-mtd-core-add-get_mtd_device_by_node.patch
  create mode 100644 target/linux/generic/pending-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
  create mode 100644 target/linux/generic/pending-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
  create mode 100644 target/linux/generic/pending-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch
@@ -173,10 +194,42 @@ Subject: [PATCH] Generic pending 5.15 patches
  create mode 100644 target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
  create mode 100644 target/linux/generic/pending-5.15/682-of_net-add-mac-address-increment-support.patch
  create mode 100644 target/linux/generic/pending-5.15/683-of_net-add-mac-address-to-of-tree.patch
+ create mode 100644 target/linux/generic/pending-5.15/684-of_net-do-mac-address-increment-only-once.patch
+ create mode 100644 target/linux/generic/pending-5.15/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
+ create mode 100644 target/linux/generic/pending-5.15/701-netfilter-nf_flow_table-add-missing-locking.patch
  create mode 100644 target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
  create mode 100644 target/linux/generic/pending-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch
  create mode 100644 target/linux/generic/pending-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
  create mode 100644 target/linux/generic/pending-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch
+ create mode 100644 target/linux/generic/pending-5.15/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch
+ create mode 100644 target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch
+ create mode 100644 target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch
+ create mode 100644 target/linux/generic/pending-5.15/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch
+ create mode 100644 target/linux/generic/pending-5.15/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch
+ create mode 100644 target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch
+ create mode 100644 target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch
+ create mode 100644 target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch
+ create mode 100644 target/linux/generic/pending-5.15/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch
+ create mode 100644 target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-02-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-03-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-04-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-05-net-dsa-tag_mtk-assign-per-port-queues.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-06-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-07-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-08-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-09-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-10-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-11-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-12-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-13-net-ethernet-mtk_eth_soc-fix-flow_offload-related-re.patch
+ create mode 100644 target/linux/generic/pending-5.15/732-14-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch
+ create mode 100644 target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch
+ create mode 100644 target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_wed-introduce-wed-wo-support.patch
+ create mode 100644 target/linux/generic/pending-5.15/733-03-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch
+ create mode 100644 target/linux/generic/pending-5.15/733-04-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch
+ create mode 100644 target/linux/generic/pending-5.15/733-05-net-ethernet-mtk_wed-add-rx-mib-counters.patch
  create mode 100644 target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
  create mode 100644 target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
  create mode 100644 target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
@@ -186,50 +239,9 @@ Subject: [PATCH] Generic pending 5.15 patches
  create mode 100644 target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
  create mode 100644 target/linux/generic/pending-5.15/834-ledtrig-libata.patch
  create mode 100644 target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
- create mode 100644 target/linux/generic/pending-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
  create mode 100644 target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
- create mode 100644 target/linux/generic/pending-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
  create mode 100644 target/linux/generic/pending-5.15/920-mangle_bootargs.patch
 
-diff --git a/target/linux/generic/pending-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch b/target/linux/generic/pending-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch
-new file mode 100644
-index 0000000000..75f63728ec
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch
-@@ -0,0 +1,28 @@
-+From d8d1a9a77863a8c7031ae82a1d461aa78eb72a7b Mon Sep 17 00:00:00 2001
-+From: Rob Herring <robh@kernel.org>
-+Date: Mon, 11 Oct 2021 14:12:43 -0500
-+Subject: [PATCH] checks: Drop interrupt provider '#address-cells' check
-+
-+'#address-cells' is only needed when parsing 'interrupt-map' properties, so
-+remove it from the common interrupt-provider test.
-+
-+Cc: Andre Przywara <andre.przywara@arm.com>
-+Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
-+Signed-off-by: Rob Herring <robh@kernel.org>
-+Message-Id: <20211011191245.1009682-3-robh@kernel.org>
-+Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
-+---
-+--- a/scripts/dtc/checks.c
-++++ b/scripts/dtc/checks.c
-+@@ -1569,11 +1569,6 @@ static void check_interrupt_provider(str
-+ 	if (!prop)
-+ 		FAIL(c, dti, node,
-+ 		     "Missing #interrupt-cells in interrupt provider");
-+-
-+-	prop = get_property(node, "#address-cells");
-+-	if (!prop)
-+-		FAIL(c, dti, node,
-+-		     "Missing #address-cells in interrupt provider");
-+ }
-+ WARNING(interrupt_provider, check_interrupt_provider, NULL);
-+ 
 diff --git a/target/linux/generic/pending-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
 new file mode 100644
 index 0000000000..22f52c1d46
@@ -267,10 +279,10 @@ index 0000000000..22f52c1d46
 + #endif /* __LINUX_COMPILER_H */
 diff --git a/target/linux/generic/pending-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch b/target/linux/generic/pending-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch
 new file mode 100644
-index 0000000000..eefbe4b543
+index 0000000000..55388fb58a
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch
-@@ -0,0 +1,26 @@
+@@ -0,0 +1,22 @@
 +From d3c5b26768dbe990c4e1bd79e420c11ce7491d51 Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 11:36:00 +0200
@@ -282,8 +294,6 @@ index 0000000000..eefbe4b543
 + include/uapi/linux/swab.h | 2 +-
 + 1 file changed, 1 insertion(+), 1 deletion(-)
 +
-+diff --git a/include/uapi/linux/swab.h b/include/uapi/linux/swab.h
-+index 7272f85d6d6a..3736f2fe1541 100644
 +--- a/include/uapi/linux/swab.h
 ++++ b/include/uapi/linux/swab.h
 +@@ -3,7 +3,7 @@
@@ -295,8 +305,6 @@ index 0000000000..eefbe4b543
 + #include <asm/bitsperlong.h>
 + #include <asm/swab.h>
 + 
-+-- 
-+
 diff --git a/target/linux/generic/pending-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
 new file mode 100644
 index 0000000000..95a9656d26
@@ -360,9 +368,36 @@ index 0000000000..95a9656d26
 + 		*frame_size = -ip->i_format.simmediate;
 + 		return 1;
 + 	}
+diff --git a/target/linux/generic/pending-5.15/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-5.15/103-kbuild-export-SUBARCH.patch
+new file mode 100644
+index 0000000000..d378dabdda
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/103-kbuild-export-SUBARCH.patch
+@@ -0,0 +1,21 @@
++From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sun, 9 Jul 2017 00:26:53 +0200
++Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++ Makefile | 4 ++--
++ 1 file changed, 2 insertions(+), 2 deletions(-)
++
++--- a/Makefile
+++++ b/Makefile
++@@ -526,7 +526,7 @@ KBUILD_LDFLAGS_MODULE :=
++ KBUILD_LDFLAGS :=
++ CLANG_FLAGS :=
++ 
++-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
+++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
++ export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
++ export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
++ export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
 diff --git a/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
 new file mode 100644
-index 0000000000..30c70a6be9
+index 0000000000..1ba95d3ffa
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
 @@ -0,0 +1,82 @@
@@ -439,7 +474,7 @@ index 0000000000..30c70a6be9
 +
 +--- a/mm/page_alloc.c
 ++++ b/mm/page_alloc.c
-+@@ -7552,7 +7552,7 @@ static void __init alloc_node_mem_map(st
++@@ -7602,7 +7602,7 @@ static void __init alloc_node_mem_map(st
 + 	if (pgdat == NODE_DATA(0)) {
 + 		mem_map = NODE_DATA(0)->node_mem_map;
 + 		if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
@@ -893,7 +928,7 @@ index 0000000000..7e9d0e66c0
 + exit:
 diff --git a/target/linux/generic/pending-5.15/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-5.15/203-kallsyms_uncompressed.patch
 new file mode 100644
-index 0000000000..ea407d6db9
+index 0000000000..b525976fc9
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/203-kallsyms_uncompressed.patch
 @@ -0,0 +1,119 @@
@@ -1005,7 +1040,7 @@ index 0000000000..ea407d6db9
 + 		}
 +--- a/scripts/link-vmlinux.sh
 ++++ b/scripts/link-vmlinux.sh
-+@@ -263,6 +263,10 @@ kallsyms()
++@@ -257,6 +257,10 @@ kallsyms()
 + 		kallsymopt="${kallsymopt} --base-relative"
 + 	fi
 + 
@@ -1665,148 +1700,6 @@ index 0000000000..ef92a5dfb6
 + cflags-$(CONFIG_CPU_MIPS32_R5)	+= -march=mips32r5 -Wa,--trap -modd-spreg
 + cflags-$(CONFIG_CPU_MIPS32_R6)	+= -march=mips32r6 -Wa,--trap -modd-spreg
 + cflags-$(CONFIG_CPU_MIPS64_R1)	+= -march=mips64 -Wa,--trap
-diff --git a/target/linux/generic/pending-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch b/target/linux/generic/pending-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch
-new file mode 100644
-index 0000000000..318c0b1b25
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch
-@@ -0,0 +1,136 @@
-+From 87ec87c2ad615c1a177cd08ef5fa29fc739f6e50 Mon Sep 17 00:00:00 2001
-+From: Hauke Mehrtens <hauke@hauke-m.de>
-+Date: Sun, 23 Dec 2018 18:06:53 +0100
-+Subject: [PATCH] MIPS: Add CPU option reporting to /proc/cpuinfo
-+
-+Many MIPS CPUs have optional CPU features which are not activates for
-+all CPU cores. Print the CPU options which are implemented in the core
-+in /proc/cpuinfo. This makes it possible to see what features are
-+supported and which are not supported. This should cover all standard
-+MIPS extensions, before it only printed information about the main MIPS
-+ASEs.
-+
-+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
-+---
-+ arch/mips/kernel/proc.c | 116 ++++++++++++++++++++++++++++++++++++++++
-+ 1 file changed, 116 insertions(+)
-+
-+--- a/arch/mips/kernel/proc.c
-++++ b/arch/mips/kernel/proc.c
-+@@ -138,6 +138,116 @@ static int show_cpuinfo(struct seq_file
-+ 		seq_printf(m, "micromips kernel\t: %s\n",
-+ 		      (read_c0_config3() & MIPS_CONF3_ISA_OE) ?  "yes" : "no");
-+ 	}
-++
-++	seq_printf(m, "Options implemented\t:");
-++	if (cpu_has_tlb)
-++		seq_printf(m, "%s", " tlb");
-++	if (cpu_has_ftlb)
-++		seq_printf(m, "%s", " ftlb");
-++	if (cpu_has_tlbinv)
-++		seq_printf(m, "%s", " tlbinv");
-++	if (cpu_has_segments)
-++		seq_printf(m, "%s", " segments");
-++	if (cpu_has_rixiex)
-++		seq_printf(m, "%s", " rixiex");
-++	if (cpu_has_ldpte)
-++		seq_printf(m, "%s", " ldpte");
-++	if (cpu_has_maar)
-++		seq_printf(m, "%s", " maar");
-++	if (cpu_has_rw_llb)
-++		seq_printf(m, "%s", " rw_llb");
-++	if (cpu_has_4kex)
-++		seq_printf(m, "%s", " 4kex");
-++	if (cpu_has_3k_cache)
-++		seq_printf(m, "%s", " 3k_cache");
-++	if (cpu_has_4k_cache)
-++		seq_printf(m, "%s", " 4k_cache");
-++	if (cpu_has_tx39_cache)
-++		seq_printf(m, "%s", " tx39_cache");
-++	if (cpu_has_octeon_cache)
-++		seq_printf(m, "%s", " octeon_cache");
-++	if (cpu_has_fpu)
-++		seq_printf(m, "%s", " fpu");
-++	if (cpu_has_32fpr)
-++		seq_printf(m, "%s", " 32fpr");
-++	if (cpu_has_cache_cdex_p)
-++		seq_printf(m, "%s", " cache_cdex_p");
-++	if (cpu_has_cache_cdex_s)
-++		seq_printf(m, "%s", " cache_cdex_s");
-++	if (cpu_has_prefetch)
-++		seq_printf(m, "%s", " prefetch");
-++	if (cpu_has_mcheck)
-++		seq_printf(m, "%s", " mcheck");
-++	if (cpu_has_ejtag)
-++		seq_printf(m, "%s", " ejtag");
-++	if (cpu_has_llsc)
-++		seq_printf(m, "%s", " llsc");
-++	if (cpu_has_guestctl0ext)
-++		seq_printf(m, "%s", " guestctl0ext");
-++	if (cpu_has_guestctl1)
-++		seq_printf(m, "%s", " guestctl1");
-++	if (cpu_has_guestctl2)
-++		seq_printf(m, "%s", " guestctl2");
-++	if (cpu_has_guestid)
-++		seq_printf(m, "%s", " guestid");
-++	if (cpu_has_drg)
-++		seq_printf(m, "%s", " drg");
-++	if (cpu_has_rixi)
-++		seq_printf(m, "%s", " rixi");
-++	if (cpu_has_lpa)
-++		seq_printf(m, "%s", " lpa");
-++	if (cpu_has_mvh)
-++		seq_printf(m, "%s", " mvh");
-++	if (cpu_has_vtag_icache)
-++		seq_printf(m, "%s", " vtag_icache");
-++	if (cpu_has_dc_aliases)
-++		seq_printf(m, "%s", " dc_aliases");
-++	if (cpu_has_ic_fills_f_dc)
-++		seq_printf(m, "%s", " ic_fills_f_dc");
-++	if (cpu_has_pindexed_dcache)
-++		seq_printf(m, "%s", " pindexed_dcache");
-++	if (cpu_has_userlocal)
-++		seq_printf(m, "%s", " userlocal");
-++	if (cpu_has_nofpuex)
-++		seq_printf(m, "%s", " nofpuex");
-++	if (cpu_has_vint)
-++		seq_printf(m, "%s", " vint");
-++	if (cpu_has_veic)
-++		seq_printf(m, "%s", " veic");
-++	if (cpu_has_inclusive_pcaches)
-++		seq_printf(m, "%s", " inclusive_pcaches");
-++	if (cpu_has_perf_cntr_intr_bit)
-++		seq_printf(m, "%s", " perf_cntr_intr_bit");
-++	if (cpu_has_ufr)
-++		seq_printf(m, "%s", " ufr");
-++	if (cpu_has_fre)
-++		seq_printf(m, "%s", " fre");
-++	if (cpu_has_cdmm)
-++		seq_printf(m, "%s", " cdmm");
-++	if (cpu_has_small_pages)
-++		seq_printf(m, "%s", " small_pages");
-++	if (cpu_has_nan_legacy)
-++		seq_printf(m, "%s", " nan_legacy");
-++	if (cpu_has_nan_2008)
-++		seq_printf(m, "%s", " nan_2008");
-++	if (cpu_has_ebase_wg)
-++		seq_printf(m, "%s", " ebase_wg");
-++	if (cpu_has_badinstr)
-++		seq_printf(m, "%s", " badinstr");
-++	if (cpu_has_badinstrp)
-++		seq_printf(m, "%s", " badinstrp");
-++	if (cpu_has_contextconfig)
-++		seq_printf(m, "%s", " contextconfig");
-++	if (cpu_has_perf)
-++		seq_printf(m, "%s", " perf");
-++	if (cpu_has_shared_ftlb_ram)
-++		seq_printf(m, "%s", " shared_ftlb_ram");
-++	if (cpu_has_shared_ftlb_entries)
-++		seq_printf(m, "%s", " shared_ftlb_entries");
-++	if (cpu_has_mipsmt_pertccounters)
-++		seq_printf(m, "%s", " mipsmt_pertccounters");
-++	seq_printf(m, "\n");
-++
-+ 	seq_printf(m, "shadow register sets\t: %d\n",
-+ 		      cpu_data[n].srsets);
-+ 	seq_printf(m, "kscratch registers\t: %d\n",
 diff --git a/target/linux/generic/pending-5.15/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-5.15/310-arm_module_unresolved_weak_sym.patch
 new file mode 100644
 index 0000000000..191dc6ac3c
@@ -1837,10 +1730,10 @@ index 0000000000..191dc6ac3c
 + 		switch (ELF32_R_TYPE(rel->r_info)) {
 diff --git a/target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
 new file mode 100644
-index 0000000000..3a5f5a238d
+index 0000000000..3f553b28b3
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
-@@ -0,0 +1,283 @@
+@@ -0,0 +1,282 @@
 +From: Yousong Zhou <yszhou4tech@gmail.com>
 +Subject: MIPS: kexec: Accept command line parameters from userspace.
 +
@@ -2103,7 +1996,7 @@ index 0000000000..3a5f5a238d
 + 
 + #ifdef CONFIG_SMP
 + /*
-+@@ -181,9 +182,15 @@ kexec_indirection_page:
++@@ -177,8 +178,15 @@ EXPORT(kexec_indirection_page)
 + 	PTR_WD		0
 + 	.size		kexec_indirection_page, PTRSIZE
 + 
@@ -2118,8 +2011,7 @@ index 0000000000..3a5f5a238d
 ++	.skip		KEXEC_ARGV_SIZE
 ++	.size		kexec_argv, KEXEC_ARGV_SIZE
 + 
-+-relocate_new_kernel_size:
-+-	EXPORT(relocate_new_kernel_size)
++-EXPORT(relocate_new_kernel_size)
 +-	PTR_WD		relocate_new_kernel_end - relocate_new_kernel
 +-	.size		relocate_new_kernel_size, PTRSIZE
 ++kexec_relocate_new_kernel_end:
@@ -2277,10 +2169,10 @@ index 0000000000..082b122cb4
 + 	select HAVE_KRETPROBES
 diff --git a/target/linux/generic/pending-5.15/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-5.15/400-mtd-mtdsplit-support.patch
 new file mode 100644
-index 0000000000..63df1d070d
+index 0000000000..bf82bb3950
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/400-mtd-mtdsplit-support.patch
-@@ -0,0 +1,339 @@
+@@ -0,0 +1,328 @@
 +From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 11:47:35 +0200
@@ -2294,8 +2186,6 @@ index 0000000000..63df1d070d
 + include/linux/mtd/partitions.h |   7 ++
 + 5 files changed, 197 insertions(+), 25 deletions(-)
 +
-+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
-+index 796a2eccbef0..f9ed93c4cf0f 100644
 +--- a/drivers/mtd/Kconfig
 ++++ b/drivers/mtd/Kconfig
 +@@ -12,6 +12,25 @@ menuconfig MTD
@@ -2324,11 +2214,9 @@ index 0000000000..63df1d070d
 + config MTD_TESTS
 + 	tristate "MTD tests support (DANGEROUS)"
 + 	depends on m
-+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
-+index 593d0593a038..b14b7fe0f597 100644
 +--- a/drivers/mtd/Makefile
 ++++ b/drivers/mtd/Makefile
-+@@ -9,6 +9,8 @@ mtd-y				:= mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
++@@ -9,6 +9,8 @@ mtd-y				:= mtdcore.o mtdsuper.o mtdconc
 + 
 + obj-y				+= parsers/
 + 
@@ -2337,8 +2225,6 @@ index 0000000000..63df1d070d
 + # 'Users' - code which presents functionality to userspace.
 + obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
 + obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
-+diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
-+index d442fa94c872..f1ed12aae1fe 100644
 +--- a/drivers/mtd/mtdpart.c
 ++++ b/drivers/mtd/mtdpart.c
 +@@ -15,11 +15,13 @@
@@ -2355,7 +2241,7 @@ index 0000000000..63df1d070d
 + 
 + /*
 +  * MTD methods which simply translate the effective address and pass through
-+@@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struct mtd_info *new)
++@@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc
 + 	return ret;
 + }
 + 
@@ -2487,7 +2373,8 @@ index 0000000000..63df1d070d
 ++	if (rootfs_found)
 ++		return;
 ++
-++	if (!strcmp(part->name, "rootfs")) {
+++	if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
+++	    !strcmp(part->name, "rootfs")) {
 ++		run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
 ++
 ++		rootfs_found = 1;
@@ -2502,7 +2389,7 @@ index 0000000000..63df1d070d
 + int mtd_add_partition(struct mtd_info *parent, const char *name,
 + 		      long long offset, long long length)
 + {
-+@@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
++@@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
 + 	if (ret)
 + 		goto err_remove_part;
 + 
@@ -2510,7 +2397,7 @@ index 0000000000..63df1d070d
 + 	mtd_add_partition_attrs(child);
 + 
 + 	return 0;
-+@@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *parent,
++@@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
 + 			goto err_del_partitions;
 + 		}
 + 
@@ -2518,7 +2405,7 @@ index 0000000000..63df1d070d
 + 		mtd_add_partition_attrs(child);
 + 
 + 		/* Look for subpartitions */
-+@@ -438,31 +582,6 @@ int add_mtd_partitions(struct mtd_info *parent,
++@@ -438,31 +583,6 @@ err_del_partitions:
 + 	return ret;
 + }
 + 
@@ -2550,11 +2437,9 @@ index 0000000000..63df1d070d
 + /*
 +  * Many partition parsers just expected the core to kfree() all their data in
 +  * one chunk. Do that by default.
-+diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
-+index 8a2c60235ebb..b092bf6ff97d 100644
 +--- a/include/linux/mtd/mtd.h
 ++++ b/include/linux/mtd/mtd.h
-+@@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(struct mtd_info *mtd,
++@@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(s
 + 		req->len += mtd->erasesize - mod;
 + }
 + 
@@ -2579,7 +2464,7 @@ index 0000000000..63df1d070d
 + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
 + {
 + 	if (mtd->writesize_shift)
-+@@ -685,6 +703,13 @@ extern void __put_mtd_device(struct mtd_info *mtd);
++@@ -686,6 +704,13 @@ extern struct mtd_info *of_get_mtd_devic
 + extern struct mtd_info *get_mtd_device_nm(const char *name);
 + extern void put_mtd_device(struct mtd_info *mtd);
 + 
@@ -2593,8 +2478,6 @@ index 0000000000..63df1d070d
 + 
 + struct mtd_notifier {
 + 	void (*add)(struct mtd_info *mtd);
-+diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
-+index b74a539ec581..65ba0dbf961d 100644
 +--- a/include/linux/mtd/partitions.h
 ++++ b/include/linux/mtd/partitions.h
 +@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
@@ -2618,11 +2501,9 @@ index 0000000000..63df1d070d
 + };
 + 
 + /* Container for passing around a set of parsed partitions */
-+-- 
-+
 diff --git a/target/linux/generic/pending-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
 new file mode 100644
-index 0000000000..ed9d1c9dc1
+index 0000000000..b4fa08f0ea
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
 @@ -0,0 +1,245 @@
@@ -2708,7 +2589,7 @@ index 0000000000..ed9d1c9dc1
 +
 +--- a/drivers/mtd/mtdcore.c
 ++++ b/drivers/mtd/mtdcore.c
-+@@ -168,6 +168,15 @@ static ssize_t mtd_erasesize_show(struct
++@@ -169,6 +169,15 @@ static ssize_t mtd_erasesize_show(struct
 + }
 + MTD_DEVICE_ATTR_RO(erasesize);
 + 
@@ -2724,7 +2605,7 @@ index 0000000000..ed9d1c9dc1
 + static ssize_t mtd_writesize_show(struct device *dev,
 + 		struct device_attribute *attr, char *buf)
 + {
-+@@ -313,6 +322,7 @@ static struct attribute *mtd_attrs[] = {
++@@ -314,6 +323,7 @@ static struct attribute *mtd_attrs[] = {
 + 	&dev_attr_flags.attr,
 + 	&dev_attr_size.attr,
 + 	&dev_attr_erasesize.attr,
@@ -2873,7 +2754,7 @@ index 0000000000..ed9d1c9dc1
 + 	 * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
 diff --git a/target/linux/generic/pending-5.15/420-mtd-redboot_space.patch b/target/linux/generic/pending-5.15/420-mtd-redboot_space.patch
 new file mode 100644
-index 0000000000..fee1936593
+index 0000000000..5518ea71dd
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/420-mtd-redboot_space.patch
 @@ -0,0 +1,41 @@
@@ -2890,7 +2771,7 @@ index 0000000000..fee1936593
 +
 +--- a/drivers/mtd/parsers/redboot.c
 ++++ b/drivers/mtd/parsers/redboot.c
-+@@ -277,14 +277,21 @@ nogood:
++@@ -278,14 +278,21 @@ nogood:
 + #endif
 + 		names += strlen(names) + 1;
 + 
@@ -2920,7 +2801,7 @@ index 0000000000..fee1936593
 + 		kfree(tmp_fl);
 diff --git a/target/linux/generic/pending-5.15/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-5.15/430-mtd-add-myloader-partition-parser.patch
 new file mode 100644
-index 0000000000..0889c9a343
+index 0000000000..8a6e630530
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/430-mtd-add-myloader-partition-parser.patch
 @@ -0,0 +1,229 @@
@@ -2946,7 +2827,7 @@ index 0000000000..0889c9a343
 ++	help
 ++	  MyLoader is a bootloader which allows the user to define partitions
 ++	  in flash devices, by putting a table in the second erase block
-++	  on the device, similar to a partition table. This table gives the 
+++	  on the device, similar to a partition table. This table gives the
 ++	  offsets and lengths of the user defined partitions.
 ++
 ++	  If you need code which can detect and parse these tables, and
@@ -3272,7 +3153,7 @@ index 0000000000..852654d924
 + 		if (buf[0x000 / 4] == TRX_MAGIC) {
 diff --git a/target/linux/generic/pending-5.15/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-5.15/435-mtd-add-routerbootpart-parser-config.patch
 new file mode 100644
-index 0000000000..30f5334af4
+index 0000000000..ee949f73c0
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/435-mtd-add-routerbootpart-parser-config.patch
 @@ -0,0 +1,38 @@
@@ -3294,7 +3175,7 @@ index 0000000000..30f5334af4
 +
 +--- a/drivers/mtd/parsers/Kconfig
 ++++ b/drivers/mtd/parsers/Kconfig
-+@@ -211,3 +211,12 @@ config MTD_SERCOMM_PARTS
++@@ -226,3 +226,12 @@ config MTD_SERCOMM_PARTS
 + 	  partition map. This partition table contains real partition
 + 	  offsets, which may differ from device to device depending on the
 + 	  number and location of bad blocks on NAND.
@@ -3309,7 +3190,7 @@ index 0000000000..30f5334af4
 ++	 formatted DTS.
 +--- a/drivers/mtd/parsers/Makefile
 ++++ b/drivers/mtd/parsers/Makefile
-+@@ -15,3 +15,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS)		+= scpa
++@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS)		+= scpa
 + obj-$(CONFIG_MTD_SHARPSL_PARTS)		+= sharpslpart.o
 + obj-$(CONFIG_MTD_REDBOOT_PARTS)		+= redboot.o
 + obj-$(CONFIG_MTD_QCOMSMEM_PARTS)	+= qcomsmempart.o
@@ -3531,10 +3412,10 @@ index 0000000000..c32cde559d
 + 			  SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
 diff --git a/target/linux/generic/pending-5.15/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-5.15/482-mtd-spi-nor-add-gd25q512.patch
 new file mode 100644
-index 0000000000..96848e103f
+index 0000000000..7c8b686277
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/482-mtd-spi-nor-add-gd25q512.patch
-@@ -0,0 +1,25 @@
+@@ -0,0 +1,21 @@
 +From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Thu, 14 Jul 2022 08:38:07 +0200
@@ -3544,11 +3425,9 @@ index 0000000000..96848e103f
 + drivers/mtd/spi-nor/gigadevice.c | 3 +++
 + 1 file changed, 3 insertions(+)
 +
-+diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
-+index e52ed19d864f..d25292ec1eb9 100644
 +--- a/drivers/mtd/spi-nor/gigadevice.c
 ++++ b/drivers/mtd/spi-nor/gigadevice.c
-+@@ -53,6 +53,9 @@ static const struct flash_info gigadevice_parts[] = {
++@@ -53,6 +53,9 @@ static const struct flash_info gigadevic
 + 			   SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK |
 + 			   SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
 + 		.fixups = &gd25q256_fixups },
@@ -3558,198 +3437,12 @@ index 0000000000..96848e103f
 + };
 + 
 + const struct spi_nor_manufacturer spi_nor_gigadevice = {
-+-- 
-+
-diff --git a/target/linux/generic/pending-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch b/target/linux/generic/pending-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch
-new file mode 100644
-index 0000000000..a73c9fa97d
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch
-@@ -0,0 +1,178 @@
-+From a07e31adf2753cad2fd9790db5bfc047c81e8152 Mon Sep 17 00:00:00 2001
-+From: Felix Matouschek <felix@matouschek.org>
-+Date: Fri, 2 Jul 2021 20:31:23 +0200
-+Subject: [PATCH] mtd: spinand: Add support for XTX XT26G0xA
-+
-+Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and
-+XTX26G04AXXXXX SPI NAND.
-+
-+These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC
-+(8bit strength per 512bytes).
-+
-+Tested on Teltonika RUTX10 flashed with OpenWrt.
-+
-+Datasheets available at
-+http://www.xtxtech.com/download/?AId=225
-+https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf
-+
-+Signed-off-by: Felix Matouschek <felix@matouschek.org>
-+---
-+ drivers/mtd/nand/spi/Makefile |   2 +-
-+ drivers/mtd/nand/spi/core.c   |   1 +
-+ drivers/mtd/nand/spi/xtx.c    | 122 ++++++++++++++++++++++++++++++++++
-+ include/linux/mtd/spinand.h   |   1 +
-+ 4 files changed, 125 insertions(+), 1 deletion(-)
-+ create mode 100644 drivers/mtd/nand/spi/xtx.c
-+
-+--- a/drivers/mtd/nand/spi/Makefile
-++++ b/drivers/mtd/nand/spi/Makefile
-+@@ -1,3 +1,3 @@
-+ # SPDX-License-Identifier: GPL-2.0
-+-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
-++spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
-+ obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
-+--- a/drivers/mtd/nand/spi/core.c
-++++ b/drivers/mtd/nand/spi/core.c
-+@@ -902,6 +902,7 @@ static const struct spinand_manufacturer
-+ 	&paragon_spinand_manufacturer,
-+ 	&toshiba_spinand_manufacturer,
-+ 	&winbond_spinand_manufacturer,
-++	&xtx_spinand_manufacturer,
-+ };
-+ 
-+ static int spinand_manufacturer_match(struct spinand_device *spinand,
-+--- /dev/null
-++++ b/drivers/mtd/nand/spi/xtx.c
-+@@ -0,0 +1,122 @@
-++// SPDX-License-Identifier: GPL-2.0
-++/*
-++ * Author:
-++ * Felix Matouschek <felix@matouschek.org>
-++ */
-++
-++#include <linux/device.h>
-++#include <linux/kernel.h>
-++#include <linux/mtd/spinand.h>
-++
-++#define SPINAND_MFR_XTX	0x0B
-++
-++#define XT26G0XA_STATUS_ECC_MASK	GENMASK(5, 2)
-++#define XT26G0XA_STATUS_ECC_NO_DETECTED	(0 << 2)
-++#define XT26G0XA_STATUS_ECC_8_CORRECTED	(3 << 4)
-++#define XT26G0XA_STATUS_ECC_UNCOR_ERROR	(2 << 4)
-++
-++static SPINAND_OP_VARIANTS(read_cache_variants,
-++		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
-++		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
-++		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
-++		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
-++		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
-++		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
-++
-++static SPINAND_OP_VARIANTS(write_cache_variants,
-++		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
-++		SPINAND_PROG_LOAD(true, 0, NULL, 0));
-++
-++static SPINAND_OP_VARIANTS(update_cache_variants,
-++		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
-++		SPINAND_PROG_LOAD(false, 0, NULL, 0));
-++
-++static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
-++				   struct mtd_oob_region *region)
-++{
-++	if (section)
-++		return -ERANGE;
-++
-++	region->offset = 48;
-++	region->length = 16;
-++
-++	return 0;
-++}
-++
-++static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
-++				   struct mtd_oob_region *region)
-++{
-++	if (section)
-++		return -ERANGE;
-++
-++	region->offset = 1;
-++	region->length = 47;
-++
-++	return 0;
-++}
-++
-++static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
-++	.ecc = xt26g0xa_ooblayout_ecc,
-++	.free = xt26g0xa_ooblayout_free,
-++};
-++
-++static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
-++					 u8 status)
-++{
-++	switch (status & XT26G0XA_STATUS_ECC_MASK) {
-++	case XT26G0XA_STATUS_ECC_NO_DETECTED:
-++		return 0;
-++	case XT26G0XA_STATUS_ECC_8_CORRECTED:
-++		return 8;
-++	case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
-++		return -EBADMSG;
-++	default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
-++		return (status & XT26G0XA_STATUS_ECC_MASK) >> 2;
-++	}
-++
-++	return -EINVAL;
-++}
-++
-++static const struct spinand_info xtx_spinand_table[] = {
-++	SPINAND_INFO("XT26G01A",
-++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
-++		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
-++		     NAND_ECCREQ(8, 512),
-++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-++					      &write_cache_variants,
-++					      &update_cache_variants),
-++		     SPINAND_HAS_QE_BIT,
-++		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
-++				     xt26g0xa_ecc_get_status)),
-++	SPINAND_INFO("XT26G02A",
-++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
-++		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
-++		     NAND_ECCREQ(8, 512),
-++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-++					      &write_cache_variants,
-++					      &update_cache_variants),
-++		     SPINAND_HAS_QE_BIT,
-++		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
-++				     xt26g0xa_ecc_get_status)),
-++	SPINAND_INFO("XT26G04A",
-++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
-++		     NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
-++		     NAND_ECCREQ(8, 512),
-++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-++					      &write_cache_variants,
-++					      &update_cache_variants),
-++		     SPINAND_HAS_QE_BIT,
-++		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
-++				     xt26g0xa_ecc_get_status)),
-++};
-++
-++static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
-++};
-++
-++const struct spinand_manufacturer xtx_spinand_manufacturer = {
-++	.id = SPINAND_MFR_XTX,
-++	.name = "XTX",
-++	.chips = xtx_spinand_table,
-++	.nchips = ARRAY_SIZE(xtx_spinand_table),
-++	.ops = &xtx_spinand_manuf_ops,
-++};
-+--- a/include/linux/mtd/spinand.h
-++++ b/include/linux/mtd/spinand.h
-+@@ -266,6 +266,7 @@ extern const struct spinand_manufacturer
-+ extern const struct spinand_manufacturer paragon_spinand_manufacturer;
-+ extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
-+ extern const struct spinand_manufacturer winbond_spinand_manufacturer;
-++extern const struct spinand_manufacturer xtx_spinand_manufacturer;
-+ 
-+ /**
-+  * struct spinand_op_variants - SPI NAND operation variants
 diff --git a/target/linux/generic/pending-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch
 new file mode 100644
-index 0000000000..d5b7259351
+index 0000000000..ab402e622a
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch
-@@ -0,0 +1,27 @@
+@@ -0,0 +1,23 @@
 +From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 12:17:21 +0200
@@ -3762,8 +3455,6 @@ index 0000000000..d5b7259351
 + drivers/mtd/spi-nor/esmt.c | 2 ++
 + 1 file changed, 2 insertions(+)
 +
-+diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c
-+index cfc9218c1053..d1750807f005 100644
 +--- a/drivers/mtd/spi-nor/esmt.c
 ++++ b/drivers/mtd/spi-nor/esmt.c
 +@@ -10,6 +10,8 @@
@@ -3775,14 +3466,12 @@ index 0000000000..d5b7259351
 + 	{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64,
 + 			   SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
 + 	{ "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64,
-+-- 
-+
 diff --git a/target/linux/generic/pending-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
 new file mode 100644
-index 0000000000..2823464a60
+index 0000000000..68e373ea23
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
-@@ -0,0 +1,28 @@
+@@ -0,0 +1,24 @@
 +From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 12:19:01 +0200
@@ -3796,11 +3485,9 @@ index 0000000000..2823464a60
 + drivers/mtd/spi-nor/xmc.c | 2 ++
 + 1 file changed, 2 insertions(+)
 +
-+diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c
-+index 2c7773b68993..99ee43a654c4 100644
 +--- a/drivers/mtd/spi-nor/xmc.c
 ++++ b/drivers/mtd/spi-nor/xmc.c
-+@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts[] = {
++@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts
 + 			    SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 + 	{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256,
 + 			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
@@ -3809,8 +3496,6 @@ index 0000000000..2823464a60
 + };
 + 
 + const struct spi_nor_manufacturer spi_nor_xmc = {
-+-- 
-+
 diff --git a/target/linux/generic/pending-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/target/linux/generic/pending-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch
 new file mode 100644
 index 0000000000..c170fedc67
@@ -3960,6 +3645,180 @@ index 0000000000..c170fedc67
 + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
 + extern const struct spinand_manufacturer macronix_spinand_manufacturer;
 + extern const struct spinand_manufacturer micron_spinand_manufacturer;
+diff --git a/target/linux/generic/pending-5.15/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-5.15/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
+new file mode 100644
+index 0000000000..2f604cfa98
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
+@@ -0,0 +1,168 @@
++From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001
++From: Daniel Danzberger <daniel@dd-wrt.com>
++Date: Wed, 3 Aug 2022 17:31:03 +0200
++Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx
++
++Airoha is a new ARM platform based on Cortex-A53 which has recently been
++merged into linux-next.
++
++Due to BootROM limitations on this platform, the Cortex-A53 can't run in
++Aarch64 mode and code must be compiled for 32-Bit ARM.
++
++This support is based mostly on those linux-next commits backported
++for kernel 5.15.
++
++Patches:
++1 - platform support = linux-next
++2 - clock driver = linux-next
++3 - gpio driver = linux-next
++4 - linux,usable-memory-range dts support = linux-next
++5 - mtd spinand driver
++6 - spi driver
++7 - pci driver (kconfig only, uses mediatek PCI) = linux-next
++
++Still missing:
++- Ethernet driver
++- Sysupgrade support
++
++A.t.m there exists one subtarget EN7523 with only one evaluation
++board.
++
++The initramfs can be run with the following commands from u-boot:
++-
++u-boot> setenv bootfile \
++	openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin
++u-boot> tftpboot
++u-boot> bootm 0x81800000
++-
++
++Submitted-by: Daniel Danzberger <daniel@dd-wrt.com>
++
++--- a/drivers/mtd/nand/spi/Makefile
+++++ b/drivers/mtd/nand/spi/Makefile
++@@ -1,3 +1,3 @@
++ # SPDX-License-Identifier: GPL-2.0
++-spinand-objs := core.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
+++spinand-objs := core.o esmt.o etron.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
++ obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
++--- a/drivers/mtd/nand/spi/core.c
+++++ b/drivers/mtd/nand/spi/core.c
++@@ -898,6 +898,7 @@ static const struct nand_ops spinand_ops
++ static const struct spinand_manufacturer *spinand_manufacturers[] = {
++ 	&esmt_c8_spinand_manufacturer,
++ 	&gigadevice_spinand_manufacturer,
+++	&etron_spinand_manufacturer,
++ 	&macronix_spinand_manufacturer,
++ 	&micron_spinand_manufacturer,
++ 	&paragon_spinand_manufacturer,
++--- /dev/null
+++++ b/drivers/mtd/nand/spi/etron.c
++@@ -0,0 +1,98 @@
+++// SPDX-License-Identifier: GPL-2.0
+++
+++#include <linux/device.h>
+++#include <linux/kernel.h>
+++#include <linux/mtd/spinand.h>
+++
+++#define SPINAND_MFR_ETRON			0xd5
+++
+++
+++static SPINAND_OP_VARIANTS(read_cache_variants,
+++		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+++		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+++		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+++		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+++		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+++		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+++
+++static SPINAND_OP_VARIANTS(write_cache_variants,
+++		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+++		SPINAND_PROG_LOAD(true, 0, NULL, 0));
+++
+++static SPINAND_OP_VARIANTS(update_cache_variants,
+++		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+++		SPINAND_PROG_LOAD(false, 0, NULL, 0));
+++
+++static int etron_ooblayout_ecc(struct mtd_info *mtd, int section,
+++					struct mtd_oob_region *oobregion)
+++{
+++	if (section)
+++		return -ERANGE;
+++
+++	oobregion->offset = 72;
+++	oobregion->length = 56;
+++
+++	return 0;
+++}
+++
+++static int etron_ooblayout_free(struct mtd_info *mtd, int section,
+++			   struct mtd_oob_region *oobregion)
+++{
+++	if (section)
+++		return -ERANGE;
+++
+++	oobregion->offset = 1;
+++	oobregion->length = 71;
+++
+++	return 0;
+++}
+++
+++static int etron_ecc_get_status(struct spinand_device *spinand, u8 status)
+++{
+++	switch (status & STATUS_ECC_MASK) {
+++	case STATUS_ECC_NO_BITFLIPS:
+++		return 0;
+++
+++	case STATUS_ECC_HAS_BITFLIPS:
+++		/* Between 1-7 bitflips were corrected */
+++		return 7;
+++
+++	case STATUS_ECC_MASK:
+++		/* Maximum bitflips were corrected */
+++		return 8;
+++
+++	case STATUS_ECC_UNCOR_ERROR:
+++		return -EBADMSG;
+++	}
+++
+++	return -EINVAL;
+++}
+++
+++static const struct mtd_ooblayout_ops etron_ooblayout = {
+++	.ecc = etron_ooblayout_ecc,
+++	.free = etron_ooblayout_free,
+++};
+++
+++static const struct spinand_info etron_spinand_table[] = {
+++	SPINAND_INFO("EM73D044VCx",
+++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f),
+++		     // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t
+++		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+++		     NAND_ECCREQ(8, 512),
+++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+++					      &write_cache_variants,
+++					      &update_cache_variants),
+++		     SPINAND_HAS_QE_BIT,
+++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
+++};
+++
+++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
+++};
+++
+++const struct spinand_manufacturer etron_spinand_manufacturer = {
+++	.id = SPINAND_MFR_ETRON,
+++	.name = "Etron",
+++	.chips = etron_spinand_table,
+++	.nchips = ARRAY_SIZE(etron_spinand_table),
+++	.ops = &etron_spinand_manuf_ops,
+++};
++--- a/include/linux/mtd/spinand.h
+++++ b/include/linux/mtd/spinand.h
++@@ -261,6 +261,7 @@ struct spinand_manufacturer {
++ 
++ /* SPI NAND manufacturers */
++ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
+++extern const struct spinand_manufacturer etron_spinand_manufacturer;
++ extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
++ extern const struct spinand_manufacturer macronix_spinand_manufacturer;
++ extern const struct spinand_manufacturer micron_spinand_manufacturer;
 diff --git a/target/linux/generic/pending-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
 new file mode 100644
 index 0000000000..b120548d2e
@@ -4303,87 +4162,6 @@ index 0000000000..413431755f
 + 	struct kmem_cache *aeb_slab_cache;
 + 	struct ubi_ec_hdr *ech;
 + 	struct ubi_vid_io_buf *vidb;
-diff --git a/target/linux/generic/pending-5.15/495-mtd-core-add-get_mtd_device_by_node.patch b/target/linux/generic/pending-5.15/495-mtd-core-add-get_mtd_device_by_node.patch
-new file mode 100644
-index 0000000000..431c80795e
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/495-mtd-core-add-get_mtd_device_by_node.patch
-@@ -0,0 +1,75 @@
-+From 1bd1b740f208d1cf4071932cc51860d37266c402 Mon Sep 17 00:00:00 2001
-+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
-+Date: Sat, 1 Sep 2018 00:30:11 +0200
-+Subject: [PATCH 495/497] mtd: core: add get_mtd_device_by_node
-+
-+Add function to retrieve a mtd device by its OF node. Since drivers can
-+assign arbitrary names to mtd devices in the absence of a label
-+property, there is no other reliable way to retrieve a mtd device for a
-+given OF node.
-+
-+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
-+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-+---
-+ drivers/mtd/mtdcore.c   | 38 ++++++++++++++++++++++++++++++++++++++
-+ include/linux/mtd/mtd.h |  2 ++
-+ 2 files changed, 40 insertions(+)
-+
-+--- a/drivers/mtd/mtdcore.c
-++++ b/drivers/mtd/mtdcore.c
-+@@ -1274,6 +1274,44 @@ out_unlock:
-+ }
-+ EXPORT_SYMBOL_GPL(get_mtd_device_nm);
-+ 
-++/**
-++ *	get_mtd_device_by_node - obtain a validated handle for an MTD device
-++ *	by of_node
-++ *	@of_node: OF node of MTD device to open
-++ *
-++ *	This function returns MTD device description structure in case of
-++ *	success and an error code in case of failure.
-++ */
-++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node)
-++{
-++	int err = -ENODEV;
-++	struct mtd_info *mtd = NULL, *other;
-++
-++	mutex_lock(&mtd_table_mutex);
-++
-++	mtd_for_each_device(other) {
-++		if (of_node == other->dev.of_node) {
-++			mtd = other;
-++			break;
-++		}
-++	}
-++
-++	if (!mtd)
-++		goto out_unlock;
-++
-++	err = __get_mtd_device(mtd);
-++	if (err)
-++		goto out_unlock;
-++
-++	mutex_unlock(&mtd_table_mutex);
-++	return mtd;
-++
-++out_unlock:
-++	mutex_unlock(&mtd_table_mutex);
-++	return ERR_PTR(err);
-++}
-++EXPORT_SYMBOL_GPL(get_mtd_device_by_node);
-++
-+ void put_mtd_device(struct mtd_info *mtd)
-+ {
-+ 	mutex_lock(&mtd_table_mutex);
-+--- a/include/linux/mtd/mtd.h
-++++ b/include/linux/mtd/mtd.h
-+@@ -703,6 +703,8 @@ extern struct mtd_info *get_mtd_device(s
-+ extern int __get_mtd_device(struct mtd_info *mtd);
-+ extern void __put_mtd_device(struct mtd_info *mtd);
-+ extern struct mtd_info *get_mtd_device_nm(const char *name);
-++extern struct mtd_info *get_mtd_device_by_node(
-++		const struct device_node *of_node);
-+ extern void put_mtd_device(struct mtd_info *mtd);
-+ 
-+ static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
 diff --git a/target/linux/generic/pending-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
 new file mode 100644
 index 0000000000..01f3b9ec2d
@@ -4444,7 +4222,7 @@ index 0000000000..01f3b9ec2d
 ++}
 diff --git a/target/linux/generic/pending-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
 new file mode 100644
-index 0000000000..321680154d
+index 0000000000..e0cbc4508b
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
 @@ -0,0 +1,216 @@
@@ -4615,7 +4393,7 @@ index 0000000000..321680154d
 ++	platform_set_drvdata(pdev, info);
 ++
 ++	of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
-++		mtd = get_mtd_device_by_node(it.node);
+++		mtd = of_get_mtd_device_by_node(it.node);
 ++		if (IS_ERR(mtd)) {
 ++			of_node_put(it.node);
 ++			err = -EPROBE_DEFER;
@@ -4742,10 +4520,10 @@ index 0000000000..ec14f6341c
 + 
 diff --git a/target/linux/generic/pending-5.15/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-5.15/500-fs_cdrom_dependencies.patch
 new file mode 100644
-index 0000000000..b46865fb55
+index 0000000000..2053c0fbe2
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/500-fs_cdrom_dependencies.patch
-@@ -0,0 +1,62 @@
+@@ -0,0 +1,52 @@
 +From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 12:21:15 +0200
@@ -4758,8 +4536,6 @@ index 0000000000..b46865fb55
 + fs/udf/Kconfig     | 1 +
 + 4 files changed, 4 insertions(+)
 +
-+diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig
-+index 129926b5142d..e0d2c647aa07 100644
 +--- a/fs/hfs/Kconfig
 ++++ b/fs/hfs/Kconfig
 +@@ -2,6 +2,7 @@
@@ -4770,8 +4546,6 @@ index 0000000000..b46865fb55
 + 	select NLS
 + 	help
 + 	  If you say Y here, you will be able to mount Macintosh-formatted
-+diff --git a/fs/hfsplus/Kconfig b/fs/hfsplus/Kconfig
-+index 7d4229aecec0..648d91d1881f 100644
 +--- a/fs/hfsplus/Kconfig
 ++++ b/fs/hfsplus/Kconfig
 +@@ -2,6 +2,7 @@
@@ -4782,8 +4556,6 @@ index 0000000000..b46865fb55
 + 	select NLS
 + 	select NLS_UTF8
 + 	help
-+diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig
-+index 08ffd37b9bb8..f74680379207 100644
 +--- a/fs/isofs/Kconfig
 ++++ b/fs/isofs/Kconfig
 +@@ -1,6 +1,7 @@
@@ -4794,8 +4566,6 @@ index 0000000000..b46865fb55
 + 	help
 + 	  This is the standard file system used on CD-ROMs.  It was previously
 + 	  known as "High Sierra File System" and is called "hsfs" on other
-+diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig
-+index 26e1a49f3ba7..3f85a084d2b5 100644
 +--- a/fs/udf/Kconfig
 ++++ b/fs/udf/Kconfig
 +@@ -1,6 +1,7 @@
@@ -4806,14 +4576,12 @@ index 0000000000..b46865fb55
 + 	select CRC_ITU_T
 + 	select NLS
 + 	help
-+-- 
-+
 diff --git a/target/linux/generic/pending-5.15/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-5.15/530-jffs2_make_lzma_available.patch
 new file mode 100644
-index 0000000000..fd6ca784c0
+index 0000000000..ac784d0b0b
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/530-jffs2_make_lzma_available.patch
-@@ -0,0 +1,5180 @@
+@@ -0,0 +1,4581 @@
 +From: Alexandros C. Couloumbis <alex@ozo.com>
 +Subject: fs: add jffs2/lzma support (not activated by default yet)
 +
@@ -5172,7 +4940,7 @@ index 0000000000..fd6ca784c0
 ++#endif
 +--- /dev/null
 ++++ b/include/linux/lzma/LzFind.h
-+@@ -0,0 +1,115 @@
++@@ -0,0 +1,98 @@
 ++/* LzFind.h -- Match finder for LZ algorithms
 ++2009-04-22 : Igor Pavlov : Public domain */
 ++
@@ -5230,11 +4998,6 @@ index 0000000000..fd6ca784c0
 ++
 ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
 ++
-++int MatchFinder_NeedMove(CMatchFinder *p);
-++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
-++void MatchFinder_MoveBlock(CMatchFinder *p);
-++void MatchFinder_ReadIfRequired(CMatchFinder *p);
-++
 ++void MatchFinder_Construct(CMatchFinder *p);
 ++
 ++/* Conditions:
@@ -5245,12 +5008,6 @@ index 0000000000..fd6ca784c0
 ++    UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
 ++    ISzAlloc *alloc);
 ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
-++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
-++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
-++
-++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
-++    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
-++    UInt32 *distances, UInt32 maxLen);
 ++
 ++/*
 ++Conditions:
@@ -5277,12 +5034,6 @@ index 0000000000..fd6ca784c0
 ++
 ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
 ++
-++void MatchFinder_Init(CMatchFinder *p);
-++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
-++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
-++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
-++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
-++
 ++#ifdef __cplusplus
 ++}
 ++#endif
@@ -5347,7 +5098,7 @@ index 0000000000..fd6ca784c0
 ++#endif
 +--- /dev/null
 ++++ b/include/linux/lzma/LzmaDec.h
-+@@ -0,0 +1,231 @@
++@@ -0,0 +1,130 @@
 ++/* LzmaDec.h -- LZMA Decoder
 ++2009-02-07 : Igor Pavlov : Public domain */
 ++
@@ -5381,14 +5132,6 @@ index 0000000000..fd6ca784c0
 ++  UInt32 dicSize;
 ++} CLzmaProps;
 ++
-++/* LzmaProps_Decode - decodes properties
-++Returns:
-++  SZ_OK
-++  SZ_ERROR_UNSUPPORTED - Unsupported properties
-++*/
-++
-++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
-++
 ++
 ++/* ---------- LZMA Decoder state ---------- */
 ++
@@ -5420,8 +5163,6 @@ index 0000000000..fd6ca784c0
 ++
 ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
 ++
-++void LzmaDec_Init(CLzmaDec *p);
-++
 ++/* There are two types of LZMA streams:
 ++     0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
 ++     1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
@@ -5458,97 +5199,6 @@ index 0000000000..fd6ca784c0
 ++
 ++/* ELzmaStatus is used only as output value for function call */
 ++
-++
-++/* ---------- Interfaces ---------- */
-++
-++/* There are 3 levels of interfaces:
-++     1) Dictionary Interface
-++     2) Buffer Interface
-++     3) One Call Interface
-++   You can select any of these interfaces, but don't mix functions from different
-++   groups for same object. */
-++
-++
-++/* There are two variants to allocate state for Dictionary Interface:
-++     1) LzmaDec_Allocate / LzmaDec_Free
-++     2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
-++   You can use variant 2, if you set dictionary buffer manually.
-++   For Buffer Interface you must always use variant 1.
-++
-++LzmaDec_Allocate* can return:
-++  SZ_OK
-++  SZ_ERROR_MEM         - Memory allocation error
-++  SZ_ERROR_UNSUPPORTED - Unsupported properties
-++*/
-++
-++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
-++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
-++
-++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
-++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
-++
-++/* ---------- Dictionary Interface ---------- */
-++
-++/* You can use it, if you want to eliminate the overhead for data copying from
-++   dictionary to some other external buffer.
-++   You must work with CLzmaDec variables directly in this interface.
-++
-++   STEPS:
-++     LzmaDec_Constr()
-++     LzmaDec_Allocate()
-++     for (each new stream)
-++     {
-++       LzmaDec_Init()
-++       while (it needs more decompression)
-++       {
-++         LzmaDec_DecodeToDic()
-++         use data from CLzmaDec::dic and update CLzmaDec::dicPos
-++       }
-++     }
-++     LzmaDec_Free()
-++*/
-++
-++/* LzmaDec_DecodeToDic
-++
-++   The decoding to internal dictionary buffer (CLzmaDec::dic).
-++   You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
-++
-++finishMode:
-++  It has meaning only if the decoding reaches output limit (dicLimit).
-++  LZMA_FINISH_ANY - Decode just dicLimit bytes.
-++  LZMA_FINISH_END - Stream must be finished after dicLimit.
-++
-++Returns:
-++  SZ_OK
-++    status:
-++      LZMA_STATUS_FINISHED_WITH_MARK
-++      LZMA_STATUS_NOT_FINISHED
-++      LZMA_STATUS_NEEDS_MORE_INPUT
-++      LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
-++  SZ_ERROR_DATA - Data error
-++*/
-++
-++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
-++    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
-++
-++
-++/* ---------- Buffer Interface ---------- */
-++
-++/* It's zlib-like interface.
-++   See LzmaDec_DecodeToDic description for information about STEPS and return results,
-++   but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
-++   to work with CLzmaDec variables manually.
-++
-++finishMode:
-++  It has meaning only if the decoding reaches output limit (*destLen).
-++  LZMA_FINISH_ANY - Decode just destLen bytes.
-++  LZMA_FINISH_END - Stream must be finished after (*destLen).
-++*/
-++
-++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
-++    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
-++
-++
 ++/* ---------- One Call Interface ---------- */
 ++
 ++/* LzmaDecode
@@ -5581,7 +5231,7 @@ index 0000000000..fd6ca784c0
 ++#endif
 +--- /dev/null
 ++++ b/include/linux/lzma/LzmaEnc.h
-+@@ -0,0 +1,80 @@
++@@ -0,0 +1,60 @@
 ++/*  LzmaEnc.h -- LZMA Encoder
 ++2009-02-07 : Igor Pavlov : Public domain */
 ++
@@ -5615,9 +5265,6 @@ index 0000000000..fd6ca784c0
 ++} CLzmaEncProps;
 ++
 ++void LzmaEncProps_Init(CLzmaEncProps *p);
-++void LzmaEncProps_Normalize(CLzmaEncProps *p);
-++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
-++
 ++
 ++/* ---------- CLzmaEncHandle Interface ---------- */
 ++
@@ -5637,26 +5284,9 @@ index 0000000000..fd6ca784c0
 ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
 ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
 ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
-++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
-++    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
 ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
 ++    int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
 ++
-++/* ---------- One Call Interface ---------- */
-++
-++/* LzmaEncode
-++Return code:
-++  SZ_OK               - OK
-++  SZ_ERROR_MEM        - Memory allocation error
-++  SZ_ERROR_PARAM      - Incorrect paramater
-++  SZ_ERROR_OUTPUT_EOF - output buffer overflow
-++  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version)
-++*/
-++
-++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-++    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
-++    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
-++
 ++#ifdef __cplusplus
 ++}
 ++#endif
@@ -5946,7 +5576,7 @@ index 0000000000..fd6ca784c0
 + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
 +--- /dev/null
 ++++ b/lib/lzma/LzFind.c
-+@@ -0,0 +1,761 @@
++@@ -0,0 +1,522 @@
 ++/* LzFind.c -- Match finder for LZ algorithms
 ++2009-04-22 : Igor Pavlov : Public domain */
 ++
@@ -5963,9 +5593,15 @@ index 0000000000..fd6ca784c0
 ++
 ++#define kStartMaxLen 3
 ++
+++#if 0
+++#define DIRECT_INPUT	p->directInput
+++#else
+++#define DIRECT_INPUT	1
+++#endif
+++
 ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
 ++{
-++  if (!p->directInput)
+++  if (!DIRECT_INPUT)
 ++  {
 ++    alloc->Free(alloc, p->bufferBase);
 ++    p->bufferBase = 0;
@@ -5977,7 +5613,7 @@ index 0000000000..fd6ca784c0
 ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
 ++{
 ++  UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
-++  if (p->directInput)
+++  if (DIRECT_INPUT)
 ++  {
 ++    p->blockSize = blockSize;
 ++    return 1;
@@ -5991,12 +5627,12 @@ index 0000000000..fd6ca784c0
 ++  return (p->bufferBase != 0);
 ++}
 ++
-++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
-++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
+++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
+++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
 ++
-++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
+++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
 ++
-++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
+++static void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
 ++{
 ++  p->posLimit -= subValue;
 ++  p->pos -= subValue;
@@ -6007,7 +5643,7 @@ index 0000000000..fd6ca784c0
 ++{
 ++  if (p->streamEndWasReached || p->result != SZ_OK)
 ++    return;
-++  if (p->directInput)
+++  if (DIRECT_INPUT)
 ++  {
 ++    UInt32 curSize = 0xFFFFFFFF - p->streamPos;
 ++    if (curSize > p->directInputRem)
@@ -6038,7 +5674,7 @@ index 0000000000..fd6ca784c0
 ++  }
 ++}
 ++
-++void MatchFinder_MoveBlock(CMatchFinder *p)
+++static void MatchFinder_MoveBlock(CMatchFinder *p)
 ++{
 ++  memmove(p->bufferBase,
 ++    p->buffer - p->keepSizeBefore,
@@ -6046,22 +5682,14 @@ index 0000000000..fd6ca784c0
 ++  p->buffer = p->bufferBase + p->keepSizeBefore;
 ++}
 ++
-++int MatchFinder_NeedMove(CMatchFinder *p)
+++static int MatchFinder_NeedMove(CMatchFinder *p)
 ++{
-++  if (p->directInput)
+++  if (DIRECT_INPUT)
 ++    return 0;
 ++  /* if (p->streamEndWasReached) return 0; */
 ++  return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
 ++}
 ++
-++void MatchFinder_ReadIfRequired(CMatchFinder *p)
-++{
-++  if (p->streamEndWasReached)
-++    return;
-++  if (p->keepSizeAfter >= p->streamPos - p->pos)
-++    MatchFinder_ReadBlock(p);
-++}
-++
 ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
 ++{
 ++  if (MatchFinder_NeedMove(p))
@@ -6217,7 +5845,7 @@ index 0000000000..fd6ca784c0
 ++  p->posLimit = p->pos + limit;
 ++}
 ++
-++void MatchFinder_Init(CMatchFinder *p)
+++static void MatchFinder_Init(CMatchFinder *p)
 ++{
 ++  UInt32 i;
 ++  for (i = 0; i < p->hashSizeSum; i++)
@@ -6236,7 +5864,7 @@ index 0000000000..fd6ca784c0
 ++  return (p->pos - p->historySize - 1) & kNormalizeMask;
 ++}
 ++
-++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
+++static void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
 ++{
 ++  UInt32 i;
 ++  for (i = 0; i < numItems; i++)
@@ -6268,38 +5896,7 @@ index 0000000000..fd6ca784c0
 ++  MatchFinder_SetLimits(p);
 ++}
 ++
-++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
-++    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
-++    UInt32 *distances, UInt32 maxLen)
-++{
-++  son[_cyclicBufferPos] = curMatch;
-++  for (;;)
-++  {
-++    UInt32 delta = pos - curMatch;
-++    if (cutValue-- == 0 || delta >= _cyclicBufferSize)
-++      return distances;
-++    {
-++      const Byte *pb = cur - delta;
-++      curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
-++      if (pb[maxLen] == cur[maxLen] && *pb == *cur)
-++      {
-++        UInt32 len = 0;
-++        while (++len != lenLimit)
-++          if (pb[len] != cur[len])
-++            break;
-++        if (maxLen < len)
-++        {
-++          *distances++ = maxLen = len;
-++          *distances++ = delta - 1;
-++          if (len == lenLimit)
-++            return distances;
-++        }
-++      }
-++    }
-++  }
-++}
-++
-++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+++static UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
 ++    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
 ++    UInt32 *distances, UInt32 maxLen)
 ++{
@@ -6409,10 +6006,10 @@ index 0000000000..fd6ca784c0
 ++  p->buffer++; \
 ++  if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
 ++
-++#define MOVE_POS_RET MOVE_POS return offset;
-++
 ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
 ++
+++#define MOVE_POS_RET MatchFinder_MovePos(p); return offset;
+++
 ++#define GET_MATCHES_HEADER2(minLen, ret_op) \
 ++  UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
 ++  lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
@@ -6428,62 +6025,7 @@ index 0000000000..fd6ca784c0
 ++  distances + offset, maxLen) - distances); MOVE_POS_RET;
 ++
 ++#define SKIP_FOOTER \
-++  SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
-++
-++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-++{
-++  UInt32 offset;
-++  GET_MATCHES_HEADER(2)
-++  HASH2_CALC;
-++  curMatch = p->hash[hashValue];
-++  p->hash[hashValue] = p->pos;
-++  offset = 0;
-++  GET_MATCHES_FOOTER(offset, 1)
-++}
-++
-++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-++{
-++  UInt32 offset;
-++  GET_MATCHES_HEADER(3)
-++  HASH_ZIP_CALC;
-++  curMatch = p->hash[hashValue];
-++  p->hash[hashValue] = p->pos;
-++  offset = 0;
-++  GET_MATCHES_FOOTER(offset, 2)
-++}
-++
-++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-++{
-++  UInt32 hash2Value, delta2, maxLen, offset;
-++  GET_MATCHES_HEADER(3)
-++
-++  HASH3_CALC;
-++
-++  delta2 = p->pos - p->hash[hash2Value];
-++  curMatch = p->hash[kFix3HashSize + hashValue];
-++
-++  p->hash[hash2Value] =
-++  p->hash[kFix3HashSize + hashValue] = p->pos;
-++
-++
-++  maxLen = 2;
-++  offset = 0;
-++  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
-++  {
-++    for (; maxLen != lenLimit; maxLen++)
-++      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
-++        break;
-++    distances[0] = maxLen;
-++    distances[1] = delta2 - 1;
-++    offset = 2;
-++    if (maxLen == lenLimit)
-++    {
-++      SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
-++      MOVE_POS_RET;
-++    }
-++  }
-++  GET_MATCHES_FOOTER(offset, maxLen)
-++}
+++  SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MatchFinder_MovePos(p);
 ++
 ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
 ++{
@@ -6532,108 +6074,6 @@ index 0000000000..fd6ca784c0
 ++  GET_MATCHES_FOOTER(offset, maxLen)
 ++}
 ++
-++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-++{
-++  UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
-++  GET_MATCHES_HEADER(4)
-++
-++  HASH4_CALC;
-++
-++  delta2 = p->pos - p->hash[                hash2Value];
-++  delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
-++  curMatch = p->hash[kFix4HashSize + hashValue];
-++
-++  p->hash[                hash2Value] =
-++  p->hash[kFix3HashSize + hash3Value] =
-++  p->hash[kFix4HashSize + hashValue] = p->pos;
-++
-++  maxLen = 1;
-++  offset = 0;
-++  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
-++  {
-++    distances[0] = maxLen = 2;
-++    distances[1] = delta2 - 1;
-++    offset = 2;
-++  }
-++  if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
-++  {
-++    maxLen = 3;
-++    distances[offset + 1] = delta3 - 1;
-++    offset += 2;
-++    delta2 = delta3;
-++  }
-++  if (offset != 0)
-++  {
-++    for (; maxLen != lenLimit; maxLen++)
-++      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
-++        break;
-++    distances[offset - 2] = maxLen;
-++    if (maxLen == lenLimit)
-++    {
-++      p->son[p->cyclicBufferPos] = curMatch;
-++      MOVE_POS_RET;
-++    }
-++  }
-++  if (maxLen < 3)
-++    maxLen = 3;
-++  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
-++    distances + offset, maxLen) - (distances));
-++  MOVE_POS_RET
-++}
-++
-++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-++{
-++  UInt32 offset;
-++  GET_MATCHES_HEADER(3)
-++  HASH_ZIP_CALC;
-++  curMatch = p->hash[hashValue];
-++  p->hash[hashValue] = p->pos;
-++  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
-++    distances, 2) - (distances));
-++  MOVE_POS_RET
-++}
-++
-++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-++{
-++  do
-++  {
-++    SKIP_HEADER(2)
-++    HASH2_CALC;
-++    curMatch = p->hash[hashValue];
-++    p->hash[hashValue] = p->pos;
-++    SKIP_FOOTER
-++  }
-++  while (--num != 0);
-++}
-++
-++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-++{
-++  do
-++  {
-++    SKIP_HEADER(3)
-++    HASH_ZIP_CALC;
-++    curMatch = p->hash[hashValue];
-++    p->hash[hashValue] = p->pos;
-++    SKIP_FOOTER
-++  }
-++  while (--num != 0);
-++}
-++
-++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-++{
-++  do
-++  {
-++    UInt32 hash2Value;
-++    SKIP_HEADER(3)
-++    HASH3_CALC;
-++    curMatch = p->hash[kFix3HashSize + hashValue];
-++    p->hash[hash2Value] =
-++    p->hash[kFix3HashSize + hashValue] = p->pos;
-++    SKIP_FOOTER
-++  }
-++  while (--num != 0);
-++}
-++
 ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
 ++{
 ++  do
@@ -6650,67 +6090,18 @@ index 0000000000..fd6ca784c0
 ++  while (--num != 0);
 ++}
 ++
-++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-++{
-++  do
-++  {
-++    UInt32 hash2Value, hash3Value;
-++    SKIP_HEADER(4)
-++    HASH4_CALC;
-++    curMatch = p->hash[kFix4HashSize + hashValue];
-++    p->hash[                hash2Value] =
-++    p->hash[kFix3HashSize + hash3Value] =
-++    p->hash[kFix4HashSize + hashValue] = p->pos;
-++    p->son[p->cyclicBufferPos] = curMatch;
-++    MOVE_POS
-++  }
-++  while (--num != 0);
-++}
-++
-++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-++{
-++  do
-++  {
-++    SKIP_HEADER(3)
-++    HASH_ZIP_CALC;
-++    curMatch = p->hash[hashValue];
-++    p->hash[hashValue] = p->pos;
-++    p->son[p->cyclicBufferPos] = curMatch;
-++    MOVE_POS
-++  }
-++  while (--num != 0);
-++}
-++
 ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
 ++{
 ++  vTable->Init = (Mf_Init_Func)MatchFinder_Init;
 ++  vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
 ++  vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
 ++  vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
-++  if (!p->btMode)
-++  {
-++    vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
-++    vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
-++  }
-++  else if (p->numHashBytes == 2)
-++  {
-++    vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
-++    vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
-++  }
-++  else if (p->numHashBytes == 3)
-++  {
-++    vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
-++    vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
-++  }
-++  else
-++  {
-++    vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
-++    vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
-++  }
+++  vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
+++  vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
 ++}
 +--- /dev/null
 ++++ b/lib/lzma/LzmaDec.c
-+@@ -0,0 +1,999 @@
++@@ -0,0 +1,925 @@
 ++/* LzmaDec.c -- LZMA Decoder
 ++2009-09-20 : Igor Pavlov : Public domain */
 ++
@@ -7395,7 +6786,7 @@ index 0000000000..fd6ca784c0
 ++  p->needFlush = 0;
 ++}
 ++
-++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
+++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
 ++{
 ++  p->needFlush = 1;
 ++  p->remainLen = 0;
@@ -7411,7 +6802,7 @@ index 0000000000..fd6ca784c0
 ++    p->needInitState = 1;
 ++}
 ++
-++void LzmaDec_Init(CLzmaDec *p)
+++static void LzmaDec_Init(CLzmaDec *p)
 ++{
 ++  p->dicPos = 0;
 ++  LzmaDec_InitDicAndState(p, True, True);
@@ -7429,7 +6820,7 @@ index 0000000000..fd6ca784c0
 ++  p->needInitState = 0;
 ++}
 ++
-++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
+++static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
 ++    ELzmaFinishMode finishMode, ELzmaStatus *status)
 ++{
 ++  SizeT inSize = *srcLen;
@@ -7550,65 +6941,13 @@ index 0000000000..fd6ca784c0
 ++  return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
 ++}
 ++
-++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
-++{
-++  SizeT outSize = *destLen;
-++  SizeT inSize = *srcLen;
-++  *srcLen = *destLen = 0;
-++  for (;;)
-++  {
-++    SizeT inSizeCur = inSize, outSizeCur, dicPos;
-++    ELzmaFinishMode curFinishMode;
-++    SRes res;
-++    if (p->dicPos == p->dicBufSize)
-++      p->dicPos = 0;
-++    dicPos = p->dicPos;
-++    if (outSize > p->dicBufSize - dicPos)
-++    {
-++      outSizeCur = p->dicBufSize;
-++      curFinishMode = LZMA_FINISH_ANY;
-++    }
-++    else
-++    {
-++      outSizeCur = dicPos + outSize;
-++      curFinishMode = finishMode;
-++    }
-++
-++    res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
-++    src += inSizeCur;
-++    inSize -= inSizeCur;
-++    *srcLen += inSizeCur;
-++    outSizeCur = p->dicPos - dicPos;
-++    memcpy(dest, p->dic + dicPos, outSizeCur);
-++    dest += outSizeCur;
-++    outSize -= outSizeCur;
-++    *destLen += outSizeCur;
-++    if (res != 0)
-++      return res;
-++    if (outSizeCur == 0 || outSize == 0)
-++      return SZ_OK;
-++  }
-++}
-++
-++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
+++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
 ++{
 ++  alloc->Free(alloc, p->probs);
 ++  p->probs = 0;
 ++}
 ++
-++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
-++{
-++  alloc->Free(alloc, p->dic);
-++  p->dic = 0;
-++}
-++
-++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
-++{
-++  LzmaDec_FreeProbs(p, alloc);
-++  LzmaDec_FreeDict(p, alloc);
-++}
-++
-++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
+++static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
 ++{
 ++  UInt32 dicSize;
 ++  Byte d;
@@ -7648,33 +6987,11 @@ index 0000000000..fd6ca784c0
 ++  return SZ_OK;
 ++}
 ++
-++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
-++{
-++  CLzmaProps propNew;
-++  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
-++  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
-++  p->prop = propNew;
-++  return SZ_OK;
-++}
-++
-++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+++static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
 ++{
 ++  CLzmaProps propNew;
-++  SizeT dicBufSize;
 ++  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
 ++  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
-++  dicBufSize = propNew.dicSize;
-++  if (p->dic == 0 || dicBufSize != p->dicBufSize)
-++  {
-++    LzmaDec_FreeDict(p, alloc);
-++    p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
-++    if (p->dic == 0)
-++    {
-++      LzmaDec_FreeProbs(p, alloc);
-++      return SZ_ERROR_MEM;
-++    }
-++  }
-++  p->dicBufSize = dicBufSize;
 ++  p->prop = propNew;
 ++  return SZ_OK;
 ++}
@@ -7712,7 +7029,7 @@ index 0000000000..fd6ca784c0
 ++}
 +--- /dev/null
 ++++ b/lib/lzma/LzmaEnc.c
-+@@ -0,0 +1,2271 @@
++@@ -0,0 +1,2123 @@
 ++/* LzmaEnc.c -- LZMA Encoder
 ++2009-11-24 : Igor Pavlov : Public domain */
 ++
@@ -7768,7 +7085,7 @@ index 0000000000..fd6ca784c0
 ++  p->writeEndMark = 0;
 ++}
 ++
-++void LzmaEncProps_Normalize(CLzmaEncProps *p)
+++static void LzmaEncProps_Normalize(CLzmaEncProps *p)
 ++{
 ++  int level = p->level;
 ++  if (level < 0) level = 5;
@@ -7791,7 +7108,7 @@ index 0000000000..fd6ca784c0
 ++      #endif
 ++}
 ++
-++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
+++static UInt32 __maybe_unused LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
 ++{
 ++  CLzmaEncProps props = *props2;
 ++  LzmaEncProps_Normalize(&props);
@@ -7808,7 +7125,7 @@ index 0000000000..fd6ca784c0
 ++
 ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
 ++
-++UInt32 GetPosSlot1(UInt32 pos)
+++static UInt32 GetPosSlot1(UInt32 pos)
 ++{
 ++  UInt32 res;
 ++  BSR2_RET(pos, res);
@@ -7822,7 +7139,7 @@ index 0000000000..fd6ca784c0
 ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2)
 ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
 ++
-++void LzmaEnc_FastPosInit(Byte *g_FastPos)
+++static void LzmaEnc_FastPosInit(Byte *g_FastPos)
 ++{
 ++  int c = 2, slotFast;
 ++  g_FastPos[0] = 0;
@@ -8054,59 +7371,7 @@ index 0000000000..fd6ca784c0
 ++  CSaveState saveState;
 ++} CLzmaEnc;
 ++
-++void LzmaEnc_SaveState(CLzmaEncHandle pp)
-++{
-++  CLzmaEnc *p = (CLzmaEnc *)pp;
-++  CSaveState *dest = &p->saveState;
-++  int i;
-++  dest->lenEnc = p->lenEnc;
-++  dest->repLenEnc = p->repLenEnc;
-++  dest->state = p->state;
-++
-++  for (i = 0; i < kNumStates; i++)
-++  {
-++    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
-++    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
-++  }
-++  for (i = 0; i < kNumLenToPosStates; i++)
-++    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
-++  memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
-++  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
-++  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
-++  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
-++  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
-++  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
-++  memcpy(dest->reps, p->reps, sizeof(p->reps));
-++  memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
-++}
-++
-++void LzmaEnc_RestoreState(CLzmaEncHandle pp)
-++{
-++  CLzmaEnc *dest = (CLzmaEnc *)pp;
-++  const CSaveState *p = &dest->saveState;
-++  int i;
-++  dest->lenEnc = p->lenEnc;
-++  dest->repLenEnc = p->repLenEnc;
-++  dest->state = p->state;
-++
-++  for (i = 0; i < kNumStates; i++)
-++  {
-++    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
-++    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
-++  }
-++  for (i = 0; i < kNumLenToPosStates; i++)
-++    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
-++  memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
-++  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
-++  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
-++  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
-++  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
-++  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
-++  memcpy(dest->reps, p->reps, sizeof(p->reps));
-++  memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
-++}
-++
-++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
+++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
 ++{
 ++  CLzmaEnc *p = (CLzmaEnc *)pp;
 ++  CLzmaEncProps props = *props2;
@@ -8315,7 +7580,7 @@ index 0000000000..fd6ca784c0
 ++  while (symbol < 0x10000);
 ++}
 ++
-++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
+++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
 ++{
 ++  UInt32 i;
 ++  for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
@@ -9391,7 +8656,7 @@ index 0000000000..fd6ca784c0
 ++  p->matchPriceCount = 0;
 ++}
 ++
-++void LzmaEnc_Construct(CLzmaEnc *p)
+++static void LzmaEnc_Construct(CLzmaEnc *p)
 ++{
 ++  RangeEnc_Construct(&p->rc);
 ++  MatchFinder_Construct(&p->matchFinderBase);
@@ -9424,7 +8689,7 @@ index 0000000000..fd6ca784c0
 ++  return p;
 ++}
 ++
-++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
+++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
 ++{
 ++  alloc->Free(alloc, p->litProbs);
 ++  alloc->Free(alloc, p->saveState.litProbs);
@@ -9432,7 +8697,7 @@ index 0000000000..fd6ca784c0
 ++  p->saveState.litProbs = 0;
 ++}
 ++
-++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
+++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
 ++{
 ++  #ifndef _7ZIP_ST
 ++  MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
@@ -9662,7 +8927,7 @@ index 0000000000..fd6ca784c0
 ++  return SZ_OK;
 ++}
 ++
-++void LzmaEnc_Init(CLzmaEnc *p)
+++static void LzmaEnc_Init(CLzmaEnc *p)
 ++{
 ++  UInt32 i;
 ++  p->state = 0;
@@ -9720,7 +8985,7 @@ index 0000000000..fd6ca784c0
 ++  p->lpMask = (1 << p->lp) - 1;
 ++}
 ++
-++void LzmaEnc_InitPrices(CLzmaEnc *p)
+++static void LzmaEnc_InitPrices(CLzmaEnc *p)
 ++{
 ++  if (!p->fastMode)
 ++  {
@@ -9752,26 +9017,6 @@ index 0000000000..fd6ca784c0
 ++  return SZ_OK;
 ++}
 ++
-++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream,
-++    ISzAlloc *alloc, ISzAlloc *allocBig)
-++{
-++  CLzmaEnc *p = (CLzmaEnc *)pp;
-++  p->matchFinderBase.stream = inStream;
-++  p->needInit = 1;
-++  p->rc.outStream = outStream;
-++  return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
-++}
-++
-++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
-++    ISeqInStream *inStream, UInt32 keepWindowSize,
-++    ISzAlloc *alloc, ISzAlloc *allocBig)
-++{
-++  CLzmaEnc *p = (CLzmaEnc *)pp;
-++  p->matchFinderBase.stream = inStream;
-++  p->needInit = 1;
-++  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
-++}
-++
 ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
 ++{
 ++  p->matchFinderBase.directInput = 1;
@@ -9779,7 +9024,7 @@ index 0000000000..fd6ca784c0
 ++  p->matchFinderBase.directInputRem = srcLen;
 ++}
 ++
-++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
+++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
 ++    UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
 ++{
 ++  CLzmaEnc *p = (CLzmaEnc *)pp;
@@ -9789,7 +9034,7 @@ index 0000000000..fd6ca784c0
 ++  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
 ++}
 ++
-++void LzmaEnc_Finish(CLzmaEncHandle pp)
+++static void LzmaEnc_Finish(CLzmaEncHandle pp)
 ++{
 ++  #ifndef _7ZIP_ST
 ++  CLzmaEnc *p = (CLzmaEnc *)pp;
@@ -9822,53 +9067,6 @@ index 0000000000..fd6ca784c0
 ++  return size;
 ++}
 ++
-++
-++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
-++{
-++  const CLzmaEnc *p = (CLzmaEnc *)pp;
-++  return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-++}
-++
-++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
-++{
-++  const CLzmaEnc *p = (CLzmaEnc *)pp;
-++  return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
-++}
-++
-++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
-++    Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
-++{
-++  CLzmaEnc *p = (CLzmaEnc *)pp;
-++  UInt64 nowPos64;
-++  SRes res;
-++  CSeqOutStreamBuf outStream;
-++
-++  outStream.funcTable.Write = MyWrite;
-++  outStream.data = dest;
-++  outStream.rem = *destLen;
-++  outStream.overflow = False;
-++
-++  p->writeEndMark = False;
-++  p->finished = False;
-++  p->result = SZ_OK;
-++
-++  if (reInit)
-++    LzmaEnc_Init(p);
-++  LzmaEnc_InitPrices(p);
-++  nowPos64 = p->nowPos64;
-++  RangeEnc_Init(&p->rc);
-++  p->rc.outStream = &outStream.funcTable;
-++
-++  res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
-++
-++  *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
-++  *destLen -= outStream.rem;
-++  if (outStream.overflow)
-++    return SZ_ERROR_OUTPUT_EOF;
-++
-++  return res;
-++}
-++
 ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
 ++{
 ++  SRes res = SZ_OK;
@@ -9899,13 +9097,6 @@ index 0000000000..fd6ca784c0
 ++  return res;
 ++}
 ++
-++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
-++    ISzAlloc *alloc, ISzAlloc *allocBig)
-++{
-++  RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig));
-++  return LzmaEnc_Encode2((CLzmaEnc *)pp, progress);
-++}
-++
 ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
 ++{
 ++  CLzmaEnc *p = (CLzmaEnc *)pp;
@@ -9962,28 +9153,6 @@ index 0000000000..fd6ca784c0
 ++    return SZ_ERROR_OUTPUT_EOF;
 ++  return res;
 ++}
-++
-++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-++    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
-++    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
-++{
-++  CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
-++  SRes res;
-++  if (p == 0)
-++    return SZ_ERROR_MEM;
-++
-++  res = LzmaEnc_SetProps(p, props);
-++  if (res == SZ_OK)
-++  {
-++    res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
-++    if (res == SZ_OK)
-++      res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
-++          writeEndMark, progress, alloc, allocBig);
-++  }
-++
-++  LzmaEnc_Destroy(p, alloc, allocBig);
-++  return res;
-++}
 +--- /dev/null
 ++++ b/lib/lzma/Makefile
 +@@ -0,0 +1,7 @@
@@ -10417,7 +9586,7 @@ index 0000000000..79da6778b6
 + 
 diff --git a/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch b/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch
 new file mode 100644
-index 0000000000..fd9e63a613
+index 0000000000..93c52e8842
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch
 @@ -0,0 +1,83 @@
@@ -10442,7 +9611,7 @@ index 0000000000..fd9e63a613
 + 	/*
 + 	 * Get the required data from the packet.
 + 	 */
-+@@ -1160,7 +1163,7 @@ int nf_conntrack_tcp_packet(struct nf_co
++@@ -1191,7 +1194,7 @@ int nf_conntrack_tcp_packet(struct nf_co
 + 		 IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
 + 		 timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
 + 		timeout = timeouts[TCP_CONNTRACK_UNACK];
@@ -10451,7 +9620,7 @@ index 0000000000..fd9e63a613
 + 		 timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
 + 		timeout = timeouts[TCP_CONNTRACK_RETRANS];
 + 	else
-+@@ -1476,6 +1479,9 @@ void nf_conntrack_tcp_init_net(struct ne
++@@ -1507,6 +1510,9 @@ void nf_conntrack_tcp_init_net(struct ne
 + 	 */
 + 	tn->tcp_be_liberal = 0;
 + 
@@ -10742,7 +9911,7 @@ index 0000000000..c61935ffb0
 + static inline struct packet_sock *pkt_sk(struct sock *sk)
 diff --git a/target/linux/generic/pending-5.15/655-increase_skb_pad.patch b/target/linux/generic/pending-5.15/655-increase_skb_pad.patch
 new file mode 100644
-index 0000000000..9d03fe5c31
+index 0000000000..d1bb72d353
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/655-increase_skb_pad.patch
 @@ -0,0 +1,20 @@
@@ -10757,7 +9926,7 @@ index 0000000000..9d03fe5c31
 +
 +--- a/include/linux/skbuff.h
 ++++ b/include/linux/skbuff.h
-+@@ -2737,7 +2737,7 @@ static inline int pskb_network_may_pull(
++@@ -2820,7 +2820,7 @@ static inline int pskb_network_may_pull(
 +  * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
 +  */
 + #ifndef NET_SKB_PAD
@@ -10768,7 +9937,7 @@ index 0000000000..9d03fe5c31
 + int ___pskb_trim(struct sk_buff *skb, unsigned int len);
 diff --git a/target/linux/generic/pending-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
 new file mode 100644
-index 0000000000..09efa1e72f
+index 0000000000..2f6fbd59e4
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
 @@ -0,0 +1,511 @@
@@ -11112,7 +10281,7 @@ index 0000000000..09efa1e72f
 + 	if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
 + 		return -1;
 + 
-+@@ -1525,6 +1686,14 @@ ip6_tnl_change(struct ip6_tnl *t, const
++@@ -1526,6 +1687,14 @@ ip6_tnl_change(struct ip6_tnl *t, const
 + 	t->parms.link = p->link;
 + 	t->parms.proto = p->proto;
 + 	t->parms.fwmark = p->fwmark;
@@ -11127,7 +10296,7 @@ index 0000000000..09efa1e72f
 + 	dst_cache_reset(&t->dst_cache);
 + 	ip6_tnl_link_config(t);
 + 	return 0;
-+@@ -1563,6 +1732,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_
++@@ -1564,6 +1733,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_
 + 	p->flowinfo = u->flowinfo;
 + 	p->link = u->link;
 + 	p->proto = u->proto;
@@ -11135,7 +10304,7 @@ index 0000000000..09efa1e72f
 + 	memcpy(p->name, u->name, sizeof(u->name));
 + }
 + 
-+@@ -1949,6 +2119,15 @@ static int ip6_tnl_validate(struct nlatt
++@@ -1950,6 +2120,15 @@ static int ip6_tnl_validate(struct nlatt
 + 	return 0;
 + }
 + 
@@ -11151,7 +10320,7 @@ index 0000000000..09efa1e72f
 + static void ip6_tnl_netlink_parms(struct nlattr *data[],
 + 				  struct __ip6_tnl_parm *parms)
 + {
-+@@ -1986,6 +2165,46 @@ static void ip6_tnl_netlink_parms(struct
++@@ -1987,6 +2166,46 @@ static void ip6_tnl_netlink_parms(struct
 + 
 + 	if (data[IFLA_IPTUN_FWMARK])
 + 		parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
@@ -11198,7 +10367,7 @@ index 0000000000..09efa1e72f
 + }
 + 
 + static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[],
-+@@ -2101,6 +2320,12 @@ static void ip6_tnl_dellink(struct net_d
++@@ -2102,6 +2321,12 @@ static void ip6_tnl_dellink(struct net_d
 + 
 + static size_t ip6_tnl_get_size(const struct net_device *dev)
 + {
@@ -11211,7 +10380,7 @@ index 0000000000..09efa1e72f
 + 	return
 + 		/* IFLA_IPTUN_LINK */
 + 		nla_total_size(4) +
-+@@ -2130,6 +2355,24 @@ static size_t ip6_tnl_get_size(const str
++@@ -2131,6 +2356,24 @@ static size_t ip6_tnl_get_size(const str
 + 		nla_total_size(0) +
 + 		/* IFLA_IPTUN_FWMARK */
 + 		nla_total_size(4) +
@@ -11236,7 +10405,7 @@ index 0000000000..09efa1e72f
 + 		0;
 + }
 + 
-+@@ -2137,6 +2380,9 @@ static int ip6_tnl_fill_info(struct sk_b
++@@ -2138,6 +2381,9 @@ static int ip6_tnl_fill_info(struct sk_b
 + {
 + 	struct ip6_tnl *tunnel = netdev_priv(dev);
 + 	struct __ip6_tnl_parm *parm = &tunnel->parms;
@@ -11246,7 +10415,7 @@ index 0000000000..09efa1e72f
 + 
 + 	if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
 + 	    nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) ||
-+@@ -2146,9 +2392,27 @@ static int ip6_tnl_fill_info(struct sk_b
++@@ -2147,9 +2393,27 @@ static int ip6_tnl_fill_info(struct sk_b
 + 	    nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) ||
 + 	    nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) ||
 + 	    nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) ||
@@ -11275,7 +10444,7 @@ index 0000000000..09efa1e72f
 + 	if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) ||
 + 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) ||
 + 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) ||
-+@@ -2188,6 +2452,7 @@ static const struct nla_policy ip6_tnl_p
++@@ -2189,6 +2453,7 @@ static const struct nla_policy ip6_tnl_p
 + 	[IFLA_IPTUN_ENCAP_DPORT]	= { .type = NLA_U16 },
 + 	[IFLA_IPTUN_COLLECT_METADATA]	= { .type = NLA_FLAG },
 + 	[IFLA_IPTUN_FWMARK]		= { .type = NLA_U32 },
@@ -11285,7 +10454,7 @@ index 0000000000..09efa1e72f
 + static struct rtnl_link_ops ip6_link_ops __read_mostly = {
 diff --git a/target/linux/generic/pending-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
 new file mode 100644
-index 0000000000..68960765bb
+index 0000000000..c58e6b545b
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
 @@ -0,0 +1,263 @@
@@ -11357,7 +10526,7 @@ index 0000000000..68960765bb
 + static void rt_fibinfo_free(struct rtable __rcu **rtp)
 +--- a/net/ipv4/fib_trie.c
 ++++ b/net/ipv4/fib_trie.c
-+@@ -2767,6 +2767,7 @@ static const char *const rtn_type_names[
++@@ -2770,6 +2770,7 @@ static const char *const rtn_type_names[
 + 	[RTN_THROW] = "THROW",
 + 	[RTN_NAT] = "NAT",
 + 	[RTN_XRESOLVE] = "XRESOLVE",
@@ -11542,7 +10711,7 @@ index 0000000000..68960765bb
 + 	kfree(net->ipv6.ip6_blk_hole_entry);
 + #endif
 + 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
-+@@ -6639,6 +6688,9 @@ void __init ip6_route_init_special_entri
++@@ -6645,6 +6694,9 @@ void __init ip6_route_init_special_entri
 + 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
 + 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
 + 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
@@ -11610,7 +10779,7 @@ index 0000000000..bea43b2bad
 + 
 diff --git a/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
 new file mode 100644
-index 0000000000..8b4cd10488
+index 0000000000..72938abd37
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
 @@ -0,0 +1,149 @@
@@ -11627,7 +10796,7 @@ index 0000000000..8b4cd10488
 +
 +--- a/include/linux/netdevice.h
 ++++ b/include/linux/netdevice.h
-+@@ -2075,6 +2075,8 @@ struct net_device {
++@@ -2089,6 +2089,8 @@ struct net_device {
 + 	struct netdev_hw_addr_list	mc;
 + 	struct netdev_hw_addr_list	dev_addrs;
 + 
@@ -11638,7 +10807,7 @@ index 0000000000..8b4cd10488
 + #endif
 +--- a/include/linux/skbuff.h
 ++++ b/include/linux/skbuff.h
-+@@ -855,6 +855,7 @@ struct sk_buff {
++@@ -892,6 +892,7 @@ struct sk_buff {
 + #ifdef CONFIG_IPV6_NDISC_NODETYPE
 + 	__u8			ndisc_nodetype:2;
 + #endif
@@ -11648,7 +10817,7 @@ index 0000000000..8b4cd10488
 + 	__u8			inner_protocol_type:1;
 +--- a/net/core/dev.c
 ++++ b/net/core/dev.c
-+@@ -6061,6 +6061,9 @@ static enum gro_result dev_gro_receive(s
++@@ -6063,6 +6063,9 @@ static enum gro_result dev_gro_receive(s
 + 	int same_flow;
 + 	int grow;
 + 
@@ -11658,7 +10827,7 @@ index 0000000000..8b4cd10488
 + 	if (netif_elide_gro(skb->dev))
 + 		goto normal;
 + 
-+@@ -8075,6 +8078,48 @@ static void __netdev_adjacent_dev_unlink
++@@ -8077,6 +8080,48 @@ static void __netdev_adjacent_dev_unlink
 + 					   &upper_dev->adj_list.lower);
 + }
 + 
@@ -11707,7 +10876,7 @@ index 0000000000..8b4cd10488
 + static int __netdev_upper_dev_link(struct net_device *dev,
 + 				   struct net_device *upper_dev, bool master,
 + 				   void *upper_priv, void *upper_info,
-+@@ -8126,6 +8171,7 @@ static int __netdev_upper_dev_link(struc
++@@ -8128,6 +8173,7 @@ static int __netdev_upper_dev_link(struc
 + 	if (ret)
 + 		return ret;
 + 
@@ -11715,7 +10884,7 @@ index 0000000000..8b4cd10488
 + 	ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
 + 					    &changeupper_info.info);
 + 	ret = notifier_to_errno(ret);
-+@@ -8222,6 +8268,7 @@ static void __netdev_upper_dev_unlink(st
++@@ -8224,6 +8270,7 @@ static void __netdev_upper_dev_unlink(st
 + 
 + 	__netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
 + 
@@ -11723,7 +10892,7 @@ index 0000000000..8b4cd10488
 + 	call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
 + 				      &changeupper_info.info);
 + 
-+@@ -9041,6 +9088,7 @@ int dev_set_mac_address(struct net_devic
++@@ -9043,6 +9090,7 @@ int dev_set_mac_address(struct net_devic
 + 	if (err)
 + 		return err;
 + 	dev->addr_assign_type = NET_ADDR_SET;
@@ -11860,10 +11029,10 @@ index 0000000000..fe6faddc7d
 + EXPORT_SYMBOL(of_get_mac_address);
 diff --git a/target/linux/generic/pending-5.15/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-5.15/683-of_net-add-mac-address-to-of-tree.patch
 new file mode 100644
-index 0000000000..742d58b708
+index 0000000000..f7ef06a14a
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/683-of_net-add-mac-address-to-of-tree.patch
-@@ -0,0 +1,59 @@
+@@ -0,0 +1,55 @@
 +From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001
 +From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 +Date: Wed, 13 Jul 2022 12:22:48 +0200
@@ -11881,11 +11050,9 @@ index 0000000000..742d58b708
 + net/core/of_net.c | 22 ++++++++++++++++++++++
 + 1 file changed, 22 insertions(+)
 +
-+diff --git a/net/core/of_net.c b/net/core/of_net.c
-+index 71c38b532f72..43b28c8ddff9 100644
 +--- a/net/core/of_net.c
 ++++ b/net/core/of_net.c
-+@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
++@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct
 + 	return 0;
 + }
 + 
@@ -11913,7 +11080,7 @@ index 0000000000..742d58b708
 + /**
 +  * of_get_mac_address()
 +  * @np:		Caller's Device Node
-+@@ -175,6 +196,7 @@ int of_get_mac_address(struct device_node *np, u8 *addr)
++@@ -175,6 +196,7 @@ found:
 + 		addr[5] = (mac_val >> 0) & 0xff;
 + 	}
 + 
@@ -11921,11 +11088,203 @@ index 0000000000..742d58b708
 + 	return ret;
 + }
 + EXPORT_SYMBOL(of_get_mac_address);
-+-- 
+diff --git a/target/linux/generic/pending-5.15/684-of_net-do-mac-address-increment-only-once.patch b/target/linux/generic/pending-5.15/684-of_net-do-mac-address-increment-only-once.patch
+new file mode 100644
+index 0000000000..44d88e31a2
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/684-of_net-do-mac-address-increment-only-once.patch
+@@ -0,0 +1,31 @@
++From dd07dd394d8bfdb5d527fab18ca54f20815ec4e4 Mon Sep 17 00:00:00 2001
++From: Will Moss <willormos@gmail.com>
++Date: Wed, 3 Aug 2022 13:48:55 +0000
++Subject: [PATCH] of_net: do mac-address-increment only once
++
++Remove mac-address-increment and mac-address-increment-byte
++DT property after incrementing process to make sure MAC address
++would not get incremented more if this function is stared again.
++It could happen if device initialization is deferred after
++unsuccessful attempt.
++
++Signed-off-by: Will Moss <willormos@gmail.com>
++---
++ drivers/of/of_net.c | 6 ++++++
++ 1 file changed, 6 insertions(+)
++
++--- a/net/core/of_net.c
+++++ b/net/core/of_net.c
++@@ -194,6 +194,12 @@ found:
++ 		addr[3] = (mac_val >> 16) & 0xff;
++ 		addr[4] = (mac_val >> 8) & 0xff;
++ 		addr[5] = (mac_val >> 0) & 0xff;
+++
+++		/* Remove mac-address-increment and mac-address-increment-byte
+++		 * DT property to make sure MAC address would not get incremented
+++		 * more if this function is stared again. */
+++		of_remove_property(np, of_find_property(np, "mac-address-increment", NULL));
+++		of_remove_property(np, of_find_property(np, "mac-address-increment-byte", NULL));
++ 	}
++ 
++ 	of_add_mac_address(np, addr);
+diff --git a/target/linux/generic/pending-5.15/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-5.15/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
+new file mode 100644
+index 0000000000..488c6a8d92
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
+@@ -0,0 +1,106 @@
++From: Pablo Neira Ayuso <pablo@netfilter.org>
++Date: Thu, 25 Jan 2018 12:58:55 +0100
++Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from
++ nf_flow_table
++
++Move the code that deals with device events to the core.
++
++Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
++---
++
++--- a/net/netfilter/nf_flow_table_core.c
+++++ b/net/netfilter/nf_flow_table_core.c
++@@ -613,13 +613,41 @@ void nf_flow_table_free(struct nf_flowta
++ }
++ EXPORT_SYMBOL_GPL(nf_flow_table_free);
++ 
+++static int nf_flow_table_netdev_event(struct notifier_block *this,
+++				      unsigned long event, void *ptr)
+++{
+++	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+++
+++	if (event != NETDEV_DOWN)
+++		return NOTIFY_DONE;
+++
+++	nf_flow_table_cleanup(dev);
+++
+++	return NOTIFY_DONE;
+++}
+++
+++static struct notifier_block flow_offload_netdev_notifier = {
+++	.notifier_call	= nf_flow_table_netdev_event,
+++};
+++
++ static int __init nf_flow_table_module_init(void)
++ {
++-	return nf_flow_table_offload_init();
+++	int ret;
+++
+++	ret = nf_flow_table_offload_init();
+++	if (ret)
+++		return ret;
+++
+++	ret = register_netdevice_notifier(&flow_offload_netdev_notifier);
+++	if (ret)
+++		nf_flow_table_offload_exit();
+++
+++	return ret;
++ }
++ 
++ static void __exit nf_flow_table_module_exit(void)
++ {
+++	unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++ 	nf_flow_table_offload_exit();
++ }
++ 
++--- a/net/netfilter/nft_flow_offload.c
+++++ b/net/netfilter/nft_flow_offload.c
++@@ -444,47 +444,14 @@ static struct nft_expr_type nft_flow_off
++ 	.owner		= THIS_MODULE,
++ };
++ 
++-static int flow_offload_netdev_event(struct notifier_block *this,
++-				     unsigned long event, void *ptr)
++-{
++-	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++-
++-	if (event != NETDEV_DOWN)
++-		return NOTIFY_DONE;
++-
++-	nf_flow_table_cleanup(dev);
++-
++-	return NOTIFY_DONE;
++-}
++-
++-static struct notifier_block flow_offload_netdev_notifier = {
++-	.notifier_call	= flow_offload_netdev_event,
++-};
++-
++ static int __init nft_flow_offload_module_init(void)
++ {
++-	int err;
++-
++-	err = register_netdevice_notifier(&flow_offload_netdev_notifier);
++-	if (err)
++-		goto err;
++-
++-	err = nft_register_expr(&nft_flow_offload_type);
++-	if (err < 0)
++-		goto register_expr;
++-
++-	return 0;
++-
++-register_expr:
++-	unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++-err:
++-	return err;
+++	return nft_register_expr(&nft_flow_offload_type);
++ }
++ 
++ static void __exit nft_flow_offload_module_exit(void)
++ {
++ 	nft_unregister_expr(&nft_flow_offload_type);
++-	unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++ }
++ 
++ module_init(nft_flow_offload_module_init);
+diff --git a/target/linux/generic/pending-5.15/701-netfilter-nf_flow_table-add-missing-locking.patch b/target/linux/generic/pending-5.15/701-netfilter-nf_flow_table-add-missing-locking.patch
+new file mode 100644
+index 0000000000..9ff393a8b7
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/701-netfilter-nf_flow_table-add-missing-locking.patch
+@@ -0,0 +1,39 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sat, 19 Nov 2022 18:48:42 +0100
++Subject: [PATCH] netfilter: nf_flow_table: add missing locking
++
++nf_flow_table_block_setup and the driver TC_SETUP_FT call can modify the flow
++block cb list while they are being traversed elsewhere, causing a crash.
++Add a write lock around the calls to protect readers
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
 +
++--- a/net/netfilter/nf_flow_table_offload.c
+++++ b/net/netfilter/nf_flow_table_offload.c
++@@ -1074,6 +1074,7 @@ static int nf_flow_table_block_setup(str
++ 	struct flow_block_cb *block_cb, *next;
++ 	int err = 0;
++ 
+++	down_write(&flowtable->flow_block_lock);
++ 	switch (cmd) {
++ 	case FLOW_BLOCK_BIND:
++ 		list_splice(&bo->cb_list, &flowtable->flow_block.cb_list);
++@@ -1088,6 +1089,7 @@ static int nf_flow_table_block_setup(str
++ 		WARN_ON_ONCE(1);
++ 		err = -EOPNOTSUPP;
++ 	}
+++	up_write(&flowtable->flow_block_lock);
++ 
++ 	return err;
++ }
++@@ -1144,7 +1146,9 @@ static int nf_flow_table_offload_cmd(str
++ 
++ 	nf_flow_table_block_offload_init(bo, dev_net(dev), cmd, flowtable,
++ 					 extack);
+++	down_write(&flowtable->flow_block_lock);
++ 	err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_FT, bo);
+++	up_write(&flowtable->flow_block_lock);
++ 	if (err < 0)
++ 		return err;
++ 
 diff --git a/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
 new file mode 100644
-index 0000000000..4b5c474204
+index 0000000000..3e1486962c
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
 @@ -0,0 +1,41 @@
@@ -11941,7 +11300,7 @@ index 0000000000..4b5c474204
 +
 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+@@ -2443,8 +2443,8 @@ static irqreturn_t mtk_handle_irq_rx(int
++@@ -2802,8 +2802,8 @@ static irqreturn_t mtk_handle_irq_rx(int
 + 
 + 	eth->rx_events++;
 + 	if (likely(napi_schedule_prep(&eth->rx_napi))) {
@@ -11951,7 +11310,7 @@ index 0000000000..4b5c474204
 + 	}
 + 
 + 	return IRQ_HANDLED;
-+@@ -2456,8 +2456,8 @@ static irqreturn_t mtk_handle_irq_tx(int
++@@ -2815,8 +2815,8 @@ static irqreturn_t mtk_handle_irq_tx(int
 + 
 + 	eth->tx_events++;
 + 	if (likely(napi_schedule_prep(&eth->tx_napi))) {
@@ -11961,18 +11320,18 @@ index 0000000000..4b5c474204
 + 	}
 + 
 + 	return IRQ_HANDLED;
-+@@ -3623,6 +3623,8 @@ static int mtk_probe(struct platform_dev
++@@ -4118,6 +4118,8 @@ static int mtk_probe(struct platform_dev
 + 	 * for NAPI to work
 + 	 */
 + 	init_dummy_netdev(&eth->dummy_dev);
 ++	eth->dummy_dev.threaded = 1;
 ++	strcpy(eth->dummy_dev.name, "mtk_eth");
 + 	netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
-+ 		       MTK_NAPI_WEIGHT);
++ 		       NAPI_POLL_WEIGHT);
 + 	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx,
 diff --git a/target/linux/generic/pending-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch
 new file mode 100644
-index 0000000000..21c322b4f1
+index 0000000000..976239c128
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch
 @@ -0,0 +1,38 @@
@@ -11989,7 +11348,7 @@ index 0000000000..21c322b4f1
 +
 +--- a/drivers/net/phy/phy_device.c
 ++++ b/drivers/net/phy/phy_device.c
-+@@ -1738,6 +1738,9 @@ void phy_detach(struct phy_device *phyde
++@@ -1746,6 +1746,9 @@ void phy_detach(struct phy_device *phyde
 + 	struct module *ndev_owner = NULL;
 + 	struct mii_bus *bus;
 + 
@@ -12001,7 +11360,7 @@ index 0000000000..21c322b4f1
 + 			sysfs_remove_link(&dev->dev.kobj, "phydev");
 +--- a/include/linux/phy.h
 ++++ b/include/linux/phy.h
-+@@ -789,6 +789,12 @@ struct phy_driver {
++@@ -823,6 +823,12 @@ struct phy_driver {
 + 	/** @handle_interrupt: Override default interrupt handling */
 + 	irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
 + 
@@ -12229,3105 +11588,6639 @@ index 0000000000..2a2ca7f1fa
 + 		nla_nest_cancel(skb, protinfo);
 + 		goto nla_put_failure;
 + 	}
-diff --git a/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
+diff --git a/target/linux/generic/pending-5.15/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch b/target/linux/generic/pending-5.15/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch
 new file mode 100644
-index 0000000000..662b956e4c
+index 0000000000..703a0b8b72
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
-@@ -0,0 +1,27 @@
-+From:   Tobias Waldekranz <tobias@waldekranz.com>
-+Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port
-+Date:   Sat, 16 Jan 2021 02:25:15 +0100
-+Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/>
++++ b/target/linux/generic/pending-5.15/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch
+@@ -0,0 +1,101 @@
++From ace6abaa0f9203083fe4c0a6a74da2d96410b625 Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Sat, 13 Aug 2022 12:49:33 +0200
++Subject: [PATCH 01/10] net: phy: realtek: rtl8221: allow to configure SERDES
++ mode
++
++The rtl8221 supports multiple SERDES modes:
++- SGMII
++- 2500base-x
++- HiSGMII
++
++Further it supports rate adaption on SERDES links to allow
++slow ethernet speeds (10/100/1000mbit) to work on 2500base-x/HiSGMII
++links without reducing the SERDES speed.
++
++When operating without rate adapters the SERDES link will follow the
++ethernet speed.
++
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
++---
++ drivers/net/phy/realtek.c | 48 +++++++++++++++++++++++++++++++++++++++
++ 1 file changed, 48 insertions(+)
++
++--- a/drivers/net/phy/realtek.c
+++++ b/drivers/net/phy/realtek.c
++@@ -53,6 +53,15 @@
++ 						 RTL8201F_ISR_LINK)
++ #define RTL8201F_IER				0x13
++ 
+++#define RTL8221B_MMD_SERDES_CTRL		MDIO_MMD_VEND1
+++#define RTL8221B_MMD_PHY_CTRL			MDIO_MMD_VEND2
+++#define RTL8221B_SERDES_OPTION			0x697a
+++#define RTL8221B_SERDES_OPTION_MODE_MASK	GENMASK(5, 0)
+++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII	0
+++#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII	1
+++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX		2
+++#define RTL8221B_SERDES_OPTION_MODE_HISGMII		3
+++
++ #define RTL8366RB_POWER_SAVE			0x15
++ #define RTL8366RB_POWER_SAVE_ON			BIT(12)
++ 
++@@ -841,6 +850,43 @@ static irqreturn_t rtl9000a_handle_inter
++ 	return IRQ_HANDLED;
++ }
++ 
+++static int rtl8221b_config_init(struct phy_device *phydev)
+++{
+++	u16 option_mode;
+++
+++	switch (phydev->interface) {
+++	case PHY_INTERFACE_MODE_SGMII:
+++	case PHY_INTERFACE_MODE_2500BASEX:
+++		option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII;
+++		break;
+++	default:
+++		return 0;
+++	}
+++
+++	phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL,
+++                      0x75f3, 0);
+++
+++	phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL,
+++			       RTL8221B_SERDES_OPTION,
+++			       RTL8221B_SERDES_OPTION_MODE_MASK, option_mode);
+++	switch (option_mode) {
+++        case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII:
+++        case RTL8221B_SERDES_OPTION_MODE_2500BASEX:
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455);
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
+++                break;
+++        case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII:
+++        case RTL8221B_SERDES_OPTION_MODE_HISGMII:
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433);
+++                phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
+++                break;
+++	}
+++
+++	return 0;
+++}
+++
++ static struct phy_driver realtek_drvs[] = {
++ 	{
++ 		PHY_ID_MATCH_EXACT(0x00008201),
++@@ -981,6 +1027,7 @@ static struct phy_driver realtek_drvs[]
++ 		PHY_ID_MATCH_EXACT(0x001cc849),
++ 		.name           = "RTL8221B-VB-CG 2.5Gbps PHY",
++ 		.get_features   = rtl822x_get_features,
+++		.config_init	= rtl8221b_config_init,
++ 		.config_aneg    = rtl822x_config_aneg,
++ 		.read_status    = rtl822x_read_status,
++ 		.suspend        = genphy_suspend,
++@@ -992,6 +1039,7 @@ static struct phy_driver realtek_drvs[]
++ 		.name           = "RTL8221B-VM-CG 2.5Gbps PHY",
++ 		.get_features   = rtl822x_get_features,
++ 		.config_aneg    = rtl822x_config_aneg,
+++		.config_init	= rtl8221b_config_init,
++ 		.read_status    = rtl822x_read_status,
++ 		.suspend        = genphy_suspend,
++ 		.resume         = rtlgen_resume,
+diff --git a/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch
+new file mode 100644
+index 0000000000..cc12d72110
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch
+@@ -0,0 +1,28 @@
++From 3fb8841513c4ec3a2e5d366df86230c45f239a57 Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Sat, 13 Aug 2022 13:08:22 +0200
++Subject: [PATCH 03/10] net: mt7531: ensure all MACs are powered down before
++ reset
 +
-+While the hardware is capable of performing learning on the CPU port,
-+it requires alot of additions to the bridge's forwarding path in order
-+to handle multi-destination traffic correctly.
++The datasheet [1] explicit describes it as requirement for a reset.
 +
-+Until that is in place, opt for the next best thing and let DSA sync
-+the relevant addresses down to the hardware FDB.
++[1] MT7531 Reference Manual for Development Board rev 1.0, page 735
 +
-+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
 +---
-+ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
-+ 1 file changed, 1 insertion(+)
++ drivers/net/dsa/mt7530.c | 4 ++++
++ 1 file changed, 4 insertions(+)
 +
-+--- a/drivers/net/dsa/mv88e6xxx/chip.c
-++++ b/drivers/net/dsa/mv88e6xxx/chip.c
-+@@ -6320,6 +6320,7 @@ static int mv88e6xxx_register_switch(str
-+ 	ds->ops = &mv88e6xxx_switch_ops;
-+ 	ds->ageing_time_min = chip->info->age_time_coeff;
-+ 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
-++	ds->assisted_learning_on_cpu_port = true;
++--- a/drivers/net/dsa/mt7530.c
+++++ b/drivers/net/dsa/mt7530.c
++@@ -2324,6 +2324,10 @@ mt7531_setup(struct dsa_switch *ds)
++ 		return -ENODEV;
++ 	}
 + 
-+ 	/* Some chips support up to 32, but that requires enabling the
-+ 	 * 5-bit port mode, which we do not support. 640k^W16 ought to
-diff --git a/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
+++	/* all MACs must be forced link-down before sw reset */
+++	for (i = 0; i < MT7530_NUM_PORTS; i++)
+++		mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
+++
++ 	/* Reset the switch through internal reset */
++ 	mt7530_write(priv, MT7530_SYS_CTRL,
++ 		     SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+diff --git a/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch b/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch
 new file mode 100644
-index 0000000000..fcf7892c04
+index 0000000000..cd97706658
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
-@@ -0,0 +1,61 @@
-+From patchwork Thu Aug  5 22:23:30 2021
-+Content-Type: text/plain; charset="utf-8"
-+MIME-Version: 1.0
-+Content-Transfer-Encoding: 7bit
-+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-+X-Patchwork-Id: 12422209
-+Date: Thu, 5 Aug 2021 23:23:30 +0100
-+From: Daniel Golle <daniel@makrotopia.org>
-+To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org,
-+ linux-kernel@vger.kernel.org
-+Cc: "David S. Miller" <davem@davemloft.net>, Andrew Lunn <andrew@lunn.ch>,
-+ Michael Walle <michael@walle.cc>
-+Subject: [PATCH] ARM: kirkwood: add missing <linux/if_ether.h> for ETH_ALEN
-+Message-ID: <YQxk4jrbm31NM1US@makrotopia.org>
-+MIME-Version: 1.0
-+Content-Disposition: inline
-+X-BeenThere: linux-arm-kernel@lists.infradead.org
-+X-Mailman-Version: 2.1.34
-+Precedence: list
-+List-Id: <linux-arm-kernel.lists.infradead.org>
-+List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
-+Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
++++ b/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch
+@@ -0,0 +1,46 @@
++From cbfed00575d15eafd85efd9619b7ecc0836a4aa7 Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Sat, 13 Aug 2022 14:42:12 +0200
++Subject: [PATCH 04/10] net: mtk_sgmii: implement mtk_pcs_ops
++
++Implement mtk_pcs_ops for the SGMII pcs to read the current state
++of the hardware.
++
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
++[added DUPLEX_FULL]
++Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++---
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++
++ 1 file changed, 15 insertions(+)
 +
-+After commit 83216e3988cd1 ("of: net: pass the dst buffer to
-+of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not
-+defined.
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -122,10 +122,28 @@ static void mtk_pcs_link_up(struct phyli
++ 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++ }
++ 
+++static void mtk_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state)
+++{
+++	struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+++	unsigned int val;
+++
+++	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+++	state->an_complete = !!(val & SGMII_AN_COMPLETE);
+++	state->link = !!(val & SGMII_LINK_STATYS);
+++	if (!state->link)
+++		return;
+++
+++	regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+++	state->speed = val & RG_PHY_SPEED_3_125G ? SPEED_2500 : SPEED_1000;
+++	state->duplex = DUPLEX_FULL;
+++	state->pause = 0;
+++}
+++
++ static const struct phylink_pcs_ops mtk_pcs_ops = {
++ 	.pcs_config = mtk_pcs_config,
++ 	.pcs_an_restart = mtk_pcs_restart_an,
++ 	.pcs_link_up = mtk_pcs_link_up,
+++	.pcs_get_state = mtk_pcs_get_state,
++ };
++ 
++ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+diff --git a/target/linux/generic/pending-5.15/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch b/target/linux/generic/pending-5.15/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch
+new file mode 100644
+index 0000000000..0fa357d48f
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch
+@@ -0,0 +1,39 @@
++From 7f75f43fe2159123baa101fcc8c6faa0b0a4c598 Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Sat, 13 Aug 2022 14:48:51 +0200
++Subject: [PATCH 05/10] net: mtk_sgmii: fix powering up the SGMII phy
 +
-+arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup':
-+arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'?
-+   u8 tmpmac[ETH_ALEN];
-+             ^~~~~~~~
-+             ESTALE
-+arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in
-+arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable]
-+   u8 tmpmac[ETH_ALEN];
-+      ^~~~~~
-+make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1
-+make[5]: *** Waiting for unfinished jobs....
++There are certain race condition when the SGMII_PHYA_PWD register still
++contains 0x9 which prevents the SGMII from working properly.
 +
-+Add missing #include <linux/if_ether.h> to fix this.
++The SGMII still shows link but no traffic can flow.
 +
-+Cc: David S. Miller <davem@davemloft.net>
-+Cc: Andrew Lunn <andrew@lunn.ch>
-+Cc: Michael Walle <michael@walle.cc>
-+Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio
-+Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()")
-+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
 +---
-+ arch/arm/mach-mvebu/kirkwood.c | 1 +
-+ 1 file changed, 1 insertion(+)
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 8 ++------
++ 1 file changed, 2 insertions(+), 6 deletions(-)
 +
-+--- a/arch/arm/mach-mvebu/kirkwood.c
-++++ b/arch/arm/mach-mvebu/kirkwood.c
-+@@ -14,6 +14,7 @@
-+ #include <linux/kernel.h>
-+ #include <linux/init.h>
-+ #include <linux/mbus.h>
-++#include <linux/if_ether.h>
-+ #include <linux/of.h>
-+ #include <linux/of_address.h>
-+ #include <linux/of_net.h>
-diff --git a/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -36,9 +36,7 @@ static int mtk_pcs_setup_mode_an(struct
++ 	val |= SGMII_AN_RESTART;
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++ 
++-	regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++-	val &= ~SGMII_PHYA_PWD;
++-	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+++	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
++ 
++ 	return 0;
++ 
++@@ -70,9 +68,7 @@ static int mtk_pcs_setup_mode_force(stru
++ 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++ 
++ 	/* Release PHYA power down state */
++-	regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++-	val &= ~SGMII_PHYA_PWD;
++-	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+++	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
++ 
++ 	return 0;
++ }
+diff --git a/target/linux/generic/pending-5.15/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch b/target/linux/generic/pending-5.15/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch
 new file mode 100644
-index 0000000000..478a2cb27d
+index 0000000000..329b41cf03
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
-@@ -0,0 +1,73 @@
-+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-+Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the
-+ subdevices
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+For bus devices to be fully usable it's required to set their DMA
-+parameters.
-+
-+For years it has been missing and remained unnoticed because of
-+mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask.
-+Kernel 4.19 came with a lot of DMA changes and caused a regression on
-+the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic
-+dma noncoherent ops for simple noncoherent platforms") DMA coherent
-+allocations just fail. Example:
-+[    1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed
-+[    1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA
-+[    1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12
-+[    1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded
-+
-+This change fixes above regression in addition to the MIPS bcm47xx
-+commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC").
-+
-+It also fixes another *old* GPIO regression caused by a parent pointing
-+to the NULL:
-+[    0.157054] missing gpiochip .dev parent pointer
-+[    0.157287] bcma: bus0: Error registering GPIO driver: -22
-+introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to
-+use GPIOLIB_IRQCHIP").
++++ b/target/linux/generic/pending-5.15/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch
+@@ -0,0 +1,65 @@
++From 9daea9b71d060d93d7394ac465b2e5ee0b7e7bca Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Mon, 15 Aug 2022 16:02:01 +0200
++Subject: [PATCH 06/10] net: mtk_sgmii: ensure the SGMII PHY is powered down on
++ configuration
 +
-+Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms")
-+Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP")
-+Cc: linux-mips@linux-mips.org
-+Cc: Christoph Hellwig <hch@lst.de>
-+Cc: Linus Walleij <linus.walleij@linaro.org>
-+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
++The code expect the PHY to be in power down (which is only true after reset).
++Allow the changes of SGMII parameters more than once.
 +---
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 16 +++++++++++++++-
++ 1 file changed, 15 insertions(+), 1 deletion(-)
 +
-+--- a/drivers/bcma/host_soc.c
-++++ b/drivers/bcma/host_soc.c
-+@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm
-+ 	struct bcma_bus *bus = &soc->bus;
-+ 	int err;
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -7,6 +7,7 @@
++  *
++  */
 + 
-++	bus->dev = soc->dev;
+++#include <linux/delay.h>
++ #include <linux/mfd/syscon.h>
++ #include <linux/of.h>
++ #include <linux/phylink.h>
++@@ -24,6 +25,9 @@ static int mtk_pcs_setup_mode_an(struct
++ {
++ 	unsigned int val;
++ 
+++	/* PHYA power down */
+++	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
 ++
-+ 	/* Scan bus and initialize it */
-+ 	err = bcma_bus_early_register(bus);
-+ 	if (err)
-+--- a/drivers/bcma/main.c
-++++ b/drivers/bcma/main.c
-+@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq);
++ 	/* Setup the link timer and QPHY power up inside SGMIISYS */
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
++ 		     SGMII_LINK_TIMER_DEFAULT);
++@@ -36,6 +40,10 @@ static int mtk_pcs_setup_mode_an(struct
++ 	val |= SGMII_AN_RESTART;
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
 + 
-+ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
+++	/* Release PHYA power down state
+++	 * unknown how much the QPHY needs but it is racy without a sleep
+++	 */
+++	usleep_range(50, 100);
++ 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
++ 
++ 	return 0;
++@@ -50,6 +58,9 @@ static int mtk_pcs_setup_mode_force(stru
 + {
-+-	device_initialize(&core->dev);
-++	struct device *dev = &core->dev;
++ 	unsigned int val;
++ 
+++	/* PHYA power down */
+++	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
 ++
-++	device_initialize(dev);
-+ 	core->dev.release = bcma_release_core_dev;
-+ 	core->dev.bus = &bcma_bus_type;
-+-	dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
-++	dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index);
-+ 	core->dev.parent = bus->dev;
-+-	if (bus->dev)
-++	if (bus->dev) {
-+ 		bcma_of_fill_device(bus->dev, core);
-++		dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask);
-++	}
++ 	regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
++ 	val &= ~RG_PHY_SPEED_MASK;
++ 	if (interface == PHY_INTERFACE_MODE_2500BASEX)
++@@ -67,7 +78,10 @@ static int mtk_pcs_setup_mode_force(stru
++ 	val |= SGMII_SPEED_1000;
++ 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
 + 
-+ 	switch (bus->hosttype) {
-+ 	case BCMA_HOSTTYPE_PCI:
-diff --git a/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
++-	/* Release PHYA power down state */
+++	/* Release PHYA power down state
+++	 * unknown how much the QPHY needs but it is racy without a sleep
+++	 */
+++	usleep_range(50, 100);
++ 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
++ 
++ 	return 0;
+diff --git a/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch b/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch
 new file mode 100644
-index 0000000000..c1e14b9271
+index 0000000000..5bc187cfc8
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
-@@ -0,0 +1,222 @@
-+From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001
-+From: Mauri Sandberg <maukka@ext.kapsi.fi>
-+Date: Thu, 25 Mar 2021 11:48:05 +0200
-+Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade
++++ b/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch
+@@ -0,0 +1,31 @@
++From e4dca7affb8c03438b63bdb5fddefd6ad2431cfd Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Mon, 15 Aug 2022 14:59:29 +0200
++Subject: [PATCH 07/10] net: mtk_sgmii: mtk_pcs_setup_mode_an: don't rely on
++ register defaults
 +
-+Adds support for building cascades of GPIO lines. That is, it allows
-+setups when there is one upstream line and multiple cascaded lines, out
-+of which one can be chosen at a time. The status of the upstream line
-+can be conveyed to the selected cascaded line or, vice versa, the status
-+of the cascaded line can be conveyed to the upstream line.
++Ensure autonegotiation is enabled.
 +
-+A multiplexer is being used to select, which cascaded GPIO line is being
-+used at any given time.
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
++---
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 5 +++--
++ 1 file changed, 3 insertions(+), 2 deletions(-)
++
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -32,12 +32,13 @@ static int mtk_pcs_setup_mode_an(struct
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
++ 		     SGMII_LINK_TIMER_DEFAULT);
++ 
+++	/* disable remote fault & enable auto neg */
++ 	regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
++-	val |= SGMII_REMOTE_FAULT_DIS;
+++	val |= SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN;
++ 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++ 
++ 	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
++-	val |= SGMII_AN_RESTART;
+++	val |= SGMII_AN_RESTART | SGMII_AN_ENABLE;
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++ 
++ 	/* Release PHYA power down state
+diff --git a/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch b/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch
+new file mode 100644
+index 0000000000..0b17f77eef
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch
+@@ -0,0 +1,47 @@
++From 952b64575613d26163a5afa5ff8bfdb57840091b Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Mon, 15 Aug 2022 15:00:14 +0200
++Subject: [PATCH 08/10] net: mtk_sgmii: set the speed according to the phy
++ interface in AN
++
++The non auto-negotioting code path is setting the correct speed for the
++interface. Ensure auto-negotiation code path is doing it as well.
++
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
++---
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 +++++++++--
++ 1 file changed, 9 insertions(+), 2 deletions(-)
 +
-+At the moment only input direction is supported. In future it should be
-+possible to add support for output direction, too.
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -21,13 +21,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st
++ }
++ 
++ /* For SGMII interface mode */
++-static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs, phy_interface_t interface)
++ {
++ 	unsigned int val;
++ 
++ 	/* PHYA power down */
++ 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
++ 
+++	/* Set SGMII phy speed */
+++	regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+++	val &= ~RG_PHY_SPEED_MASK;
+++	if (interface == PHY_INTERFACE_MODE_2500BASEX)
+++		val |= RG_PHY_SPEED_3_125G;
+++	regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
+++
++ 	/* Setup the link timer and QPHY power up inside SGMIISYS */
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
++ 		     SGMII_LINK_TIMER_DEFAULT);
++@@ -100,7 +107,7 @@ static int mtk_pcs_config(struct phylink
++ 	if (interface != PHY_INTERFACE_MODE_SGMII)
++ 		err = mtk_pcs_setup_mode_force(mpcs, interface);
++ 	else if (phylink_autoneg_inband(mode))
++-		err = mtk_pcs_setup_mode_an(mpcs);
+++		err = mtk_pcs_setup_mode_an(mpcs, interface);
++ 
++ 	return err;
++ }
+diff --git a/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch b/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch
+new file mode 100644
+index 0000000000..80144850ec
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch
+@@ -0,0 +1,22 @@
++From 06773f19cffd6c9d34dcbc8320169afef5ab60ba Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Mon, 15 Aug 2022 13:58:07 +0200
++Subject: [PATCH 09/10] net: mtk_eth_soc: improve comment
 +
-+Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
-+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
 +---
-+v7 -> v8:
-+ - rearrange members in struct gpio_cascade
-+ - cosmetic changes in file header and in one function declaration
-+ - added Reviewed-by tags by Linus and Andy
-+v6 -> v7:
-+ - In Kconfig add info about module name
-+ - adhere to new convention that allows lines longer than 80 chars
-+ - use dev_probe_err with upstream gpio line too
-+ - refactor for cleaner exit of probe function.
-+v5 -> v6:
-+ - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER
-+ - refactor code preferring one-liners
-+ - clean up prints, removing them from success-path.
-+ - don't explicitly set gpio_chip.of_node as it's done in the GPIO library
-+ - use devm_gpiochip_add_data instead of gpiochip_add
-+v4 -> v5:
-+ - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly
-+   here and there and changed to use new bindings and compatible string
-+   - ambigious and vague 'pin' was rename to 'upstream_line'
-+ - dropped Tested-by and Reviewed-by due to changes in bindings
-+ - dropped Reported-by suggested by an automatic bot as it was not really
-+   appropriate to begin with
-+ - functionally it's the same as v4
-+v3 -> v4:
-+ - Changed author email
-+ - Included Tested-by and Reviewed-by from Drew
-+v2 -> v3:
-+ - use managed device resources
-+ - update Kconfig description
-+v1 -> v2:
-+ - removed .owner from platform_driver as per test bot's instruction
-+ - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
-+ - added gpio_mux_input_get_direction as it's recommended for all chips
-+ - removed because this is input only chip: gpio_mux_input_set_value
-+ - removed because they are not needed for input/output only chips:
-+     gpio_mux_input_direction_input
-+     gpio_mux_input_direction_output
-+ - fixed typo in an error message
-+ - added info message about successful registration
-+ - removed can_sleep flag as this does not sleep while getting GPIO value
-+   like I2C or SPI do
-+ - Updated description in Kconfig
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 3 ++-
++ 1 file changed, 2 insertions(+), 1 deletion(-)
++
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -80,7 +80,8 @@ static int mtk_pcs_setup_mode_force(stru
++ 	val &= ~SGMII_AN_ENABLE;
++ 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++ 
++-	/* Set the speed etc but leave the duplex unchanged */
+++	/* Set the speed etc but leave the duplex unchanged.
+++	 * The SGMII mode for 2.5gbit is the same as for 1gbit, expect the speed in ANA_RGC3 */
++ 	regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
++ 	val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
++ 	val |= SGMII_SPEED_1000;
+diff --git a/target/linux/generic/pending-5.15/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch b/target/linux/generic/pending-5.15/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch
+new file mode 100644
+index 0000000000..d185aed7bd
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch
+@@ -0,0 +1,23 @@
++From 95dcd0f223d7cab6e25bc19088016e5eb4ca1804 Mon Sep 17 00:00:00 2001
++From: Alexander Couzens <lynxis@fe80.eu>
++Date: Tue, 16 Aug 2022 00:22:11 +0200
++Subject: [PATCH 10/10] mtk_sgmii: enable PCS polling to allow SFP work
++
++Currently there is no IRQ handling (even the SGMII supports it).
++Enable polling to support SFP ports.
++
++Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
 +---
-+ drivers/gpio/Kconfig        |  15 +++++
-+ drivers/gpio/Makefile       |   1 +
-+ drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
-+ 3 files changed, 133 insertions(+)
-+ create mode 100644 drivers/gpio/gpio-cascade.c
++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 1 +
++ 1 file changed, 1 insertion(+)
 +
-+--- a/drivers/gpio/Kconfig
-++++ b/drivers/gpio/Kconfig
-+@@ -1683,4 +1683,19 @@ config GPIO_VIRTIO
++--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
++@@ -182,6 +182,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
++ 			return PTR_ERR(ss->pcs[i].regmap);
 + 
-+ endmenu
++ 		ss->pcs[i].pcs.ops = &mtk_pcs_ops;
+++		ss->pcs[i].pcs.poll = 1;
++ 	}
 + 
-++comment "Other GPIO expanders"
++ 	return 0;
+diff --git a/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch b/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
+new file mode 100644
+index 0000000000..8bf036d006
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
+@@ -0,0 +1,409 @@
++From patchwork Wed Nov  2 00:58:01 2022
++Content-Type: text/plain; charset="utf-8"
++MIME-Version: 1.0
++Content-Transfer-Encoding: 7bit
++X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
++X-Patchwork-Id: 13027653
++X-Patchwork-Delegate: kuba@kernel.org
++Return-Path: <netdev-owner@kernel.org>
++Date: Wed, 2 Nov 2022 00:58:01 +0000
++From: Daniel Golle <daniel@makrotopia.org>
++To: Felix Fietkau <nbd@nbd.name>, John Crispin <john@phrozen.org>,
++        Sean Wang <sean.wang@mediatek.com>,
++        Mark Lee <Mark-MC.Lee@mediatek.com>,
++        "David S. Miller" <davem@davemloft.net>,
++        Eric Dumazet <edumazet@google.com>,
++        Jakub Kicinski <kuba@kernel.org>,
++        Paolo Abeni <pabeni@redhat.com>,
++        Matthias Brugger <matthias.bgg@gmail.com>,
++        netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
++        linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org
++Subject: [PATCH v4] net: ethernet: mediatek: ppe: add support for flow
++ accounting
++Message-ID: <Y2HAmYYPd77dz+K5@makrotopia.org>
++MIME-Version: 1.0
++Content-Disposition: inline
++Precedence: bulk
++List-ID: <netdev.vger.kernel.org>
++X-Mailing-List: netdev@vger.kernel.org
++X-Patchwork-Delegate: kuba@kernel.org
++
++The PPE units found in MT7622 and newer support packet and byte
++accounting of hw-offloaded flows. Add support for reading those
++counters as found in MediaTek's SDK[1].
++
++[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/bc6a6a375c800dc2b80e1a325a2c732d1737df92
++Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++---
++v4: declare function mtk_mib_entry_read as static
++v3: don't bother to set 'false' values in any zero-initialized struct
++    use mtk_foe_entry_ib2
++    both changes were requested by Felix Fietkau
++
++v2: fix wrong variable name in return value check spotted by Denis Kirjanov
++
++ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   7 +-
++ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   1 +
++ drivers/net/ethernet/mediatek/mtk_ppe.c       | 110 +++++++++++++++++-
++ drivers/net/ethernet/mediatek/mtk_ppe.h       |  23 +++-
++ .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |   9 +-
++ .../net/ethernet/mediatek/mtk_ppe_offload.c   |   7 ++
++ drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  14 +++
++ 7 files changed, 166 insertions(+), 5 deletions(-)
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -4088,7 +4088,9 @@ static int mtk_probe(struct platform_dev
++ 			u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
++ 
++ 			eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
++-						   eth->soc->offload_version, i);
+++						   eth->soc->offload_version, i,
+++						   eth->soc->has_accounting);
+++
++ 			if (!eth->ppe[i]) {
++ 				err = -ENOMEM;
++ 				goto err_free_dev;
++@@ -4211,6 +4213,7 @@ static const struct mtk_soc_data mt7622_
++ 	.required_pctl = false,
++ 	.offload_version = 2,
++ 	.hash_offset = 2,
+++	.has_accounting = true,
++ 	.foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
++ 	.txrx = {
++ 		.txd_size = sizeof(struct mtk_tx_dma),
++@@ -4248,6 +4251,7 @@ static const struct mtk_soc_data mt7629_
++ 	.hw_features = MTK_HW_FEATURES,
++ 	.required_clks = MT7629_CLKS_BITMAP,
++ 	.required_pctl = false,
+++	.has_accounting = true,
++ 	.txrx = {
++ 		.txd_size = sizeof(struct mtk_tx_dma),
++ 		.rxd_size = sizeof(struct mtk_rx_dma),
++@@ -4268,6 +4272,7 @@ static const struct mtk_soc_data mt7986_
++ 	.offload_version = 2,
++ 	.hash_offset = 4,
++ 	.foe_entry_size = sizeof(struct mtk_foe_entry),
+++	.has_accounting = true,
++ 	.txrx = {
++ 		.txd_size = sizeof(struct mtk_tx_dma_v2),
++ 		.rxd_size = sizeof(struct mtk_rx_dma_v2),
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -983,6 +983,7 @@ struct mtk_soc_data {
++ 	u8		hash_offset;
++ 	u16		foe_entry_size;
++ 	netdev_features_t hw_features;
+++	bool		has_accounting;
++ 	struct {
++ 		u32	txd_size;
++ 		u32	rxd_size;
++--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
++@@ -74,6 +74,46 @@ static int mtk_ppe_wait_busy(struct mtk_
++ 	return ret;
++ }
++ 
+++static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
+++{
+++	int ret;
+++	u32 val;
 ++
-++config GPIO_CASCADE
-++	tristate "General GPIO cascade"
-++	select MULTIPLEXER
-++	help
-++	  Say yes here to enable support for generic GPIO cascade.
+++	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
+++				 !(val & MTK_PPE_MIB_SER_CR_ST),
+++				 20, MTK_PPE_WAIT_TIMEOUT_US);
 ++
-++	  This allows building one-to-many cascades of GPIO lines using
-++	  different types of multiplexers readily available. At the
-++	  moment only input lines are supported.
+++	if (ret)
+++		dev_err(ppe->dev, "MIB table busy");
 ++
-++	  To build the driver as a module choose 'm' and the resulting module
-++	  will be called 'gpio-cascade'.
+++	return ret;
+++}
 ++
-+ endif
-+--- a/drivers/gpio/Makefile
-++++ b/drivers/gpio/Makefile
-+@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd
-+ obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
-+ obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o
-+ obj-$(CONFIG_GPIO_CADENCE)		+= gpio-cadence.o
-++obj-$(CONFIG_GPIO_CASCADE)		+= gpio-cascade.o
-+ obj-$(CONFIG_GPIO_CLPS711X)		+= gpio-clps711x.o
-+ obj-$(CONFIG_GPIO_SNPS_CREG)		+= gpio-creg-snps.o
-+ obj-$(CONFIG_GPIO_CRYSTAL_COVE)		+= gpio-crystalcove.o
-+--- /dev/null
-++++ b/drivers/gpio/gpio-cascade.c
-+@@ -0,0 +1,117 @@
-++// SPDX-License-Identifier: GPL-2.0-only
-++/*
-++ *  A generic GPIO cascade driver
-++ *
-++ *  Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi>
-++ *
-++ * This allows building cascades of GPIO lines in a manner illustrated
-++ * below:
-++ *
-++ *                 /|---- Cascaded GPIO line 0
-++ *  Upstream      | |---- Cascaded GPIO line 1
-++ *  GPIO line ----+ | .
-++ *                | | .
-++ *                 \|---- Cascaded GPIO line n
-++ *
-++ * A multiplexer is being used to select, which cascaded line is being
-++ * addressed at any given time.
-++ *
-++ * At the moment only input mode is supported due to lack of means for
-++ * testing output functionality. At least theoretically output should be
-++ * possible with open drain constructions.
-++ */
+++static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
+++{
+++	u32 val, cnt_r0, cnt_r1, cnt_r2;
+++	u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
 ++
-++#include <linux/module.h>
-++#include <linux/slab.h>
-++#include <linux/platform_device.h>
-++#include <linux/mux/consumer.h>
+++	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
+++	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
 ++
-++#include <linux/gpio/consumer.h>
-++#include <linux/gpio/driver.h>
+++	if (mtk_ppe_mib_wait_busy(ppe))
+++		return -ETIMEDOUT;
 ++
-++struct gpio_cascade {
-++	struct gpio_chip	gpio_chip;
-++	struct device		*parent;
-++	struct mux_control	*mux_control;
-++	struct gpio_desc	*upstream_line;
-++};
+++	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
+++	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
+++	cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
 ++
-++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
-++{
-++	return container_of(gc, struct gpio_cascade, gpio_chip);
-++}
+++	byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
+++	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
+++	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
+++	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
+++	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
+++	*packets = (pkt_cnt_high << 16) | pkt_cnt_low;
 ++
-++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
-++{
-++	return GPIO_LINE_DIRECTION_IN;
+++	return 0;
 ++}
 ++
-++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
++ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
++ {
++ 	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++@@ -444,6 +484,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
++ 		hwe->ib1 &= ~MTK_FOE_IB1_STATE;
++ 		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
++ 		dma_wmb();
+++		if (ppe->accounting) {
+++			struct mtk_foe_accounting *acct;
+++
+++			acct = ppe->acct_table + entry->hash * sizeof(*acct);
+++			acct->packets = 0;
+++			acct->bytes = 0;
+++		}
++ 	}
++ 	entry->hash = 0xffff;
++ 
++@@ -551,6 +598,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
++ 	wmb();
++ 	hwe->ib1 = entry->ib1;
++ 
+++	if (ppe->accounting)
+++		*mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
+++
++ 	dma_wmb();
++ 
++ 	mtk_ppe_cache_clear(ppe);
++@@ -716,14 +766,42 @@ int mtk_foe_entry_idle_time(struct mtk_p
++ 	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++ }
++ 
+++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+++						 struct mtk_foe_accounting *diff)
 ++{
-++	struct gpio_cascade *cas = chip_to_cascade(gc);
-++	int ret;
+++	struct mtk_foe_accounting *acct;
+++	int size = sizeof(struct mtk_foe_accounting);
+++	u64 bytes, packets;
 ++
-++	ret = mux_control_select(cas->mux_control, offset);
-++	if (ret)
-++		return ret;
+++	if (!ppe->accounting)
+++		return NULL;
 ++
-++	ret = gpiod_get_value(cas->upstream_line);
-++	mux_control_deselect(cas->mux_control);
-++	return ret;
+++	if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
+++		return NULL;
+++
+++	acct = ppe->acct_table + index * size;
+++
+++	acct->bytes += bytes;
+++	acct->packets += packets;
+++
+++	if (diff) {
+++		diff->bytes = bytes;
+++		diff->packets = packets;
+++	}
+++
+++	return acct;
 ++}
 ++
-++static int gpio_cascade_probe(struct platform_device *pdev)
-++{
-++	struct device *dev = &pdev->dev;
-++	struct gpio_cascade *cas;
-++	struct mux_control *mc;
-++	struct gpio_desc *upstream;
-++	struct gpio_chip *gc;
++ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
++-			     int version, int index)
+++			     int version, int index, bool accounting)
++ {
++ 	const struct mtk_soc_data *soc = eth->soc;
++ 	struct device *dev = eth->dev;
++ 	struct mtk_ppe *ppe;
++ 	u32 foe_flow_size;
++ 	void *foe;
+++	struct mtk_mib_entry *mib;
+++	struct mtk_foe_accounting *acct;
++ 
++ 	ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
++ 	if (!ppe)
++@@ -738,6 +816,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
++ 	ppe->eth = eth;
++ 	ppe->dev = dev;
++ 	ppe->version = version;
+++	ppe->accounting = accounting;
++ 
++ 	foe = dmam_alloc_coherent(ppe->dev,
++ 				  MTK_PPE_ENTRIES * soc->foe_entry_size,
++@@ -753,6 +832,25 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
++ 	if (!ppe->foe_flow)
++ 		return NULL;
++ 
+++	if (accounting) {
+++		mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
+++					  &ppe->mib_phys, GFP_KERNEL);
+++		if (!mib)
+++			return NULL;
+++
+++		memset(mib, 0, MTK_PPE_ENTRIES * sizeof(*mib));
+++
+++		ppe->mib_table = mib;
+++
+++		acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
+++				    GFP_KERNEL);
+++
+++		if (!acct)
+++			return NULL;
+++
+++		ppe->acct_table = acct;
+++	}
 ++
-++	cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
-++	if (!cas)
-++		return -ENOMEM;
++ 	mtk_ppe_debugfs_init(ppe, index);
++ 
++ 	return ppe;
++@@ -867,6 +965,16 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
++ 		ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
++ 		ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
++ 	}
 ++
-++	mc = devm_mux_control_get(dev, NULL);
-++	if (IS_ERR(mc))
-++		return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n");
+++	if (ppe->accounting && ppe->mib_phys) {
+++		ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
+++		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
+++			MTK_PPE_MIB_CFG_EN);
+++		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
+++			MTK_PPE_MIB_CFG_RD_CLR);
+++		ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
+++			MTK_PPE_MIB_CFG_RD_CLR);
+++	}
++ }
++ 
++ int mtk_ppe_stop(struct mtk_ppe *ppe)
++--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
++@@ -57,6 +57,7 @@ enum {
++ #define MTK_FOE_IB2_MULTICAST		BIT(8)
++ 
++ #define MTK_FOE_IB2_WDMA_QID2		GENMASK(13, 12)
+++#define MTK_FOE_IB2_MIB_CNT		BIT(15)
++ #define MTK_FOE_IB2_WDMA_DEVIDX		BIT(16)
++ #define MTK_FOE_IB2_WDMA_WINFO		BIT(17)
++ 
++@@ -284,16 +285,34 @@ struct mtk_flow_entry {
++ 	unsigned long cookie;
++ };
++ 
+++struct mtk_mib_entry {
+++	u32	byt_cnt_l;
+++	u16	byt_cnt_h;
+++	u32	pkt_cnt_l;
+++	u8	pkt_cnt_h;
+++	u8	_rsv0;
+++	u32	_rsv1;
+++} __packed;
+++
+++struct mtk_foe_accounting {
+++	u64	bytes;
+++	u64	packets;
+++};
 ++
-++	cas->mux_control = mc;
-++	upstream = devm_gpiod_get(dev, "upstream",  GPIOD_IN);
-++	if (IS_ERR(upstream))
-++		return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n");
++ struct mtk_ppe {
++ 	struct mtk_eth *eth;
++ 	struct device *dev;
++ 	void __iomem *base;
++ 	int version;
++ 	char dirname[5];
+++	bool accounting;
++ 
++ 	void *foe_table;
++ 	dma_addr_t foe_phys;
++ 
+++	struct mtk_mib_entry *mib_table;
+++	dma_addr_t mib_phys;
 ++
-++	cas->upstream_line = upstream;
-++	cas->parent = dev;
++ 	u16 foe_check_time[MTK_PPE_ENTRIES];
++ 	struct hlist_head *foe_flow;
++ 
++@@ -303,7 +322,7 @@ struct mtk_ppe {
++ };
++ 
++ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
++-			     int version, int index);
+++			     int version, int index, bool accounting);
++ void mtk_ppe_start(struct mtk_ppe *ppe);
++ int mtk_ppe_stop(struct mtk_ppe *ppe);
++ 
++@@ -354,5 +373,7 @@ int mtk_foe_entry_commit(struct mtk_ppe
++ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
++ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
++ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
+++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+++						 struct mtk_foe_accounting *diff);
++ 
++ #endif
++--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++@@ -82,6 +82,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
++ 		struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
++ 		struct mtk_foe_mac_info *l2;
++ 		struct mtk_flow_addr_info ai = {};
+++		struct mtk_foe_accounting *acct;
++ 		unsigned char h_source[ETH_ALEN];
++ 		unsigned char h_dest[ETH_ALEN];
++ 		int type, state;
++@@ -95,6 +96,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file
++ 		if (bind && state != MTK_FOE_STATE_BIND)
++ 			continue;
++ 
+++		acct = mtk_foe_entry_get_mib(ppe, i, NULL);
+++
++ 		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ 		seq_printf(m, "%05x %s %7s", i,
++ 			   mtk_foe_entry_state_str(state),
++@@ -153,9 +156,11 @@ mtk_ppe_debugfs_foe_show(struct seq_file
++ 		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
++ 
++ 		seq_printf(m, " eth=%pM->%pM etype=%04x"
++-			      " vlan=%d,%d ib1=%08x ib2=%08x\n",
+++			      " vlan=%d,%d ib1=%08x ib2=%08x"
+++			      " packets=%lld bytes=%lld\n",
++ 			   h_source, h_dest, ntohs(l2->etype),
++-			   l2->vlan1, l2->vlan2, entry->ib1, ib2);
+++			   l2->vlan1, l2->vlan2, entry->ib1, ib2,
+++			   acct->packets, acct->bytes);
++ 	}
++ 
++ 	return 0;
++--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++@@ -491,6 +491,7 @@ static int
++ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
++ {
++ 	struct mtk_flow_entry *entry;
+++	struct mtk_foe_accounting diff;
++ 	u32 idle;
++ 
++ 	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
++@@ -501,6 +502,12 @@ mtk_flow_offload_stats(struct mtk_eth *e
++ 	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
++ 	f->stats.lastused = jiffies - idle * HZ;
++ 
+++	if (entry->hash != 0xFFFF) {
+++		mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, &diff);
+++		f->stats.pkts += diff.packets;
+++		f->stats.bytes += diff.bytes;
+++	}
 ++
-++	gc = &cas->gpio_chip;
-++	gc->get = gpio_cascade_get_value;
-++	gc->get_direction = gpio_cascade_get_direction;
-++	gc->base = -1;
-++	gc->ngpio = mux_control_states(mc);
-++	gc->label = dev_name(cas->parent);
-++	gc->parent = cas->parent;
-++	gc->owner = THIS_MODULE;
++ 	return 0;
++ }
++ 
++--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++@@ -143,6 +143,20 @@ enum {
++ 
++ #define MTK_PPE_MIB_TB_BASE			0x338
++ 
+++#define MTK_PPE_MIB_SER_CR			0x33C
+++#define MTK_PPE_MIB_SER_CR_ST			BIT(16)
+++#define MTK_PPE_MIB_SER_CR_ADDR			GENMASK(13, 0)
 ++
-++	platform_set_drvdata(pdev, cas);
-++	return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
-++}
+++#define MTK_PPE_MIB_SER_R0			0x340
+++#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW		GENMASK(31, 0)
 ++
-++static const struct of_device_id gpio_cascade_id[] = {
-++	{ .compatible = "gpio-cascade" },
-++	{ /* sentinel */ }
-++};
-++MODULE_DEVICE_TABLE(of, gpio_cascade_id);
+++#define MTK_PPE_MIB_SER_R1			0x344
+++#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW		GENMASK(31, 16)
+++#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH	GENMASK(15, 0)
 ++
-++static struct platform_driver gpio_cascade_driver = {
-++	.driver	= {
-++		.name		= "gpio-cascade",
-++		.of_match_table = gpio_cascade_id,
-++	},
-++	.probe	= gpio_cascade_probe,
-++};
-++module_platform_driver(gpio_cascade_driver);
+++#define MTK_PPE_MIB_SER_R2			0x348
+++#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH		GENMASK(23, 0)
 ++
-++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
-++MODULE_DESCRIPTION("Generic GPIO cascade");
-++MODULE_LICENSE("GPL");
-diff --git a/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch
++ #define MTK_PPE_MIB_CACHE_CTL			0x350
++ #define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0)
++ #define MTK_PPE_MIB_CACHE_CTL_FLUSH		BIT(2)
+diff --git a/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch b/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch
 new file mode 100644
-index 0000000000..7edbd94f76
+index 0000000000..45af898cf0
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch
-@@ -0,0 +1,62 @@
-+From: Gabor Juhos <juhosg@openwrt.org>
-+Subject: debloat: add kernel config option to disabling common PCI quirks
++++ b/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch
+@@ -0,0 +1,22 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 27 Oct 2022 19:50:31 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: account for vlan in rx
++ header length
 +
-+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
++The network stack assumes that devices can handle an extra VLAN tag without
++increasing the MTU
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/pci/Kconfig  | 6 ++++++
-+ drivers/pci/quirks.c | 6 ++++++
-+ 2 files changed, 12 insertions(+)
 +
-+--- a/drivers/pci/Kconfig
-++++ b/drivers/pci/Kconfig
-+@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND
-+ 	  The PCI device frontend driver allows the kernel to import arbitrary
-+ 	  PCI devices from a PCI backend to support PCI driver domains.
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -29,7 +29,7 @@
++ #define MTK_TX_DMA_BUF_LEN_V2	0xffff
++ #define MTK_DMA_SIZE		512
++ #define MTK_MAC_COUNT		2
++-#define MTK_RX_ETH_HLEN		(ETH_HLEN + ETH_FCS_LEN)
+++#define MTK_RX_ETH_HLEN		(VLAN_ETH_HLEN + ETH_FCS_LEN)
++ #define MTK_RX_HLEN		(NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
++ #define MTK_DMA_DUMMY_DESC	0xffffffff
++ #define MTK_DEFAULT_MSG_ENABLE	(NETIF_MSG_DRV | \
+diff --git a/target/linux/generic/pending-5.15/732-02-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch b/target/linux/generic/pending-5.15/732-02-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch
+new file mode 100644
+index 0000000000..c6526a39a8
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-02-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch
+@@ -0,0 +1,143 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 27 Oct 2022 19:53:57 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: increase tx ring side for
++ QDMA devices
++
++In order to use the hardware traffic shaper feature, a larger tx ring is
++needed, especially for the scratch ring, which the hardware shaper uses to
++reorder packets.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -894,7 +894,7 @@ static int mtk_init_fq_dma(struct mtk_et
++ {
++ 	const struct mtk_soc_data *soc = eth->soc;
++ 	dma_addr_t phy_ring_tail;
++-	int cnt = MTK_DMA_SIZE;
+++	int cnt = MTK_QDMA_RING_SIZE;
++ 	dma_addr_t dma_addr;
++ 	int i;
 + 
-++config PCI_DISABLE_COMMON_QUIRKS
-++	bool "PCI disable common quirks"
-++	depends on PCI
-++	help
-++	  If you don't know what to do here, say N.
-++
-++
-+ config PCI_ATS
-+ 	bool
++@@ -2148,19 +2148,25 @@ static int mtk_tx_alloc(struct mtk_eth *
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
++ 	int i, sz = soc->txrx.txd_size;
++ 	struct mtk_tx_dma_v2 *txd;
+++	int ring_size;
 + 
-+--- a/drivers/pci/quirks.c
-++++ b/drivers/pci/quirks.c
-+@@ -206,6 +206,7 @@ static void quirk_mmio_always_on(struct
-+ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
-+ 				PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
++-	ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
+++	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
+++		ring_size = MTK_QDMA_RING_SIZE;
+++	else
+++		ring_size = MTK_DMA_SIZE;
+++
+++	ring->buf = kcalloc(ring_size, sizeof(*ring->buf),
++ 			       GFP_KERNEL);
++ 	if (!ring->buf)
++ 		goto no_tx_mem;
++ 
++-	ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+++	ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
++ 				       &ring->phys, GFP_KERNEL);
++ 	if (!ring->dma)
++ 		goto no_tx_mem;
++ 
++-	for (i = 0; i < MTK_DMA_SIZE; i++) {
++-		int next = (i + 1) % MTK_DMA_SIZE;
+++	for (i = 0; i < ring_size; i++) {
+++		int next = (i + 1) % ring_size;
++ 		u32 next_ptr = ring->phys + next * sz;
++ 
++ 		txd = ring->dma + i * sz;
++@@ -2180,22 +2186,22 @@ static int mtk_tx_alloc(struct mtk_eth *
++ 	 * descriptors in ring->dma_pdma.
++ 	 */
++ 	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++-		ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+++		ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
++ 						    &ring->phys_pdma, GFP_KERNEL);
++ 		if (!ring->dma_pdma)
++ 			goto no_tx_mem;
++ 
++-		for (i = 0; i < MTK_DMA_SIZE; i++) {
+++		for (i = 0; i < ring_size; i++) {
++ 			ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF;
++ 			ring->dma_pdma[i].txd4 = 0;
++ 		}
++ 	}
 + 
-++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
-+ /*
-+  * The Mellanox Tavor device gives false positive parity errors.  Disable
-+  * parity error reporting.
-+@@ -3363,6 +3364,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
-+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
-+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
++-	ring->dma_size = MTK_DMA_SIZE;
++-	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+++	ring->dma_size = ring_size;
+++	atomic_set(&ring->free_count, ring_size - 2);
++ 	ring->next_free = ring->dma;
++ 	ring->last_free = (void *)txd;
++-	ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
+++	ring->last_free_ptr = (u32)(ring->phys + ((ring_size - 1) * sz));
++ 	ring->thresh = MAX_SKB_FRAGS;
++ 
++ 	/* make sure that all changes to the dma ring are flushed before we
++@@ -2207,14 +2213,14 @@ static int mtk_tx_alloc(struct mtk_eth *
++ 		mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr);
++ 		mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr);
++ 		mtk_w32(eth,
++-			ring->phys + ((MTK_DMA_SIZE - 1) * sz),
+++			ring->phys + ((ring_size - 1) * sz),
++ 			soc->reg_map->qdma.crx_ptr);
++ 		mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
++ 		mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
++ 			soc->reg_map->qdma.qtx_cfg);
++ 	} else {
++ 		mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
++-		mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0);
+++		mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0);
++ 		mtk_w32(eth, 0, MT7628_TX_CTX_IDX0);
++ 		mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx);
++ 	}
++@@ -2232,7 +2238,7 @@ static void mtk_tx_clean(struct mtk_eth
++ 	int i;
 + 
-++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
-++
-+ /*
-+  * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum.
-+  * To work around this, query the size it should be configured to by the
-+@@ -3388,6 +3391,8 @@ static void quirk_intel_ntb(struct pci_d
-+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
-+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
++ 	if (ring->buf) {
++-		for (i = 0; i < MTK_DMA_SIZE; i++)
+++		for (i = 0; i < ring->dma_size; i++)
++ 			mtk_tx_unmap(eth, &ring->buf[i], false);
++ 		kfree(ring->buf);
++ 		ring->buf = NULL;
++@@ -2240,14 +2246,14 @@ static void mtk_tx_clean(struct mtk_eth
++ 
++ 	if (ring->dma) {
++ 		dma_free_coherent(eth->dma_dev,
++-				  MTK_DMA_SIZE * soc->txrx.txd_size,
+++				  ring->dma_size * soc->txrx.txd_size,
++ 				  ring->dma, ring->phys);
++ 		ring->dma = NULL;
++ 	}
 + 
-++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
-++
-+ /*
-+  * Some BIOS implementations leave the Intel GPU interrupts enabled, even
-+  * though no one is handling them (e.g., if the i915 driver is never
-+@@ -3426,6 +3431,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
-+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
-+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
++ 	if (ring->dma_pdma) {
++ 		dma_free_coherent(eth->dma_dev,
++-				  MTK_DMA_SIZE * soc->txrx.txd_size,
+++				  ring->dma_size * soc->txrx.txd_size,
++ 				  ring->dma_pdma, ring->phys_pdma);
++ 		ring->dma_pdma = NULL;
++ 	}
++@@ -2767,7 +2773,7 @@ static void mtk_dma_free(struct mtk_eth
++ 			netdev_reset_queue(eth->netdev[i]);
++ 	if (eth->scratch_ring) {
++ 		dma_free_coherent(eth->dma_dev,
++-				  MTK_DMA_SIZE * soc->txrx.txd_size,
+++				  MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
++ 				  eth->scratch_ring, eth->phy_scratch_ring);
++ 		eth->scratch_ring = NULL;
++ 		eth->phy_scratch_ring = 0;
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -27,6 +27,7 @@
++ #define MTK_MAX_RX_LENGTH_2K	2048
++ #define MTK_TX_DMA_BUF_LEN	0x3fff
++ #define MTK_TX_DMA_BUF_LEN_V2	0xffff
+++#define MTK_QDMA_RING_SIZE	2048
++ #define MTK_DMA_SIZE		512
++ #define MTK_MAC_COUNT		2
++ #define MTK_RX_ETH_HLEN		(VLAN_ETH_HLEN + ETH_FCS_LEN)
+diff --git a/target/linux/generic/pending-5.15/732-03-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch b/target/linux/generic/pending-5.15/732-03-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch
+new file mode 100644
+index 0000000000..116ae011f0
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-03-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch
+@@ -0,0 +1,52 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Fri, 4 Nov 2022 19:49:08 +0100
++Subject: [PATCH] net: ethernet: mtk_eth_soc: avoid port_mg assignment on
++ MT7622 and newer
++
++On newer chips, this field is unused and contains some bits related to queue
++assignment. Initialize it to 0 in those cases.
++Fix offload_version on MT7621 and MT7623, which still need the previous value.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -4197,7 +4197,7 @@ static const struct mtk_soc_data mt7621_
++ 	.hw_features = MTK_HW_FEATURES,
++ 	.required_clks = MT7621_CLKS_BITMAP,
++ 	.required_pctl = false,
++-	.offload_version = 2,
+++	.offload_version = 1,
++ 	.hash_offset = 2,
++ 	.foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
++ 	.txrx = {
++@@ -4237,7 +4237,7 @@ static const struct mtk_soc_data mt7623_
++ 	.hw_features = MTK_HW_FEATURES,
++ 	.required_clks = MT7623_CLKS_BITMAP,
++ 	.required_pctl = true,
++-	.offload_version = 2,
+++	.offload_version = 1,
++ 	.hash_offset = 2,
++ 	.foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
++ 	.txrx = {
++--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
++@@ -215,6 +215,8 @@ int mtk_foe_entry_prepare(struct mtk_eth
++ 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) |
++ 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf);
++ 	} else {
+++		int port_mg = eth->soc->offload_version > 1 ? 0 : 0x3f;
+++
++ 		val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
++ 		      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
++ 		      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
++@@ -222,7 +224,7 @@ int mtk_foe_entry_prepare(struct mtk_eth
++ 		entry->ib1 = val;
++ 
++ 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) |
++-		      FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
+++		      FIELD_PREP(MTK_FOE_IB2_PORT_MG, port_mg) |
++ 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f);
++ 	}
 + 
-++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
-++
-+ /*
-+  * PCI devices which are on Intel chips can skip the 10ms delay
-+  * before entering D3 mode.
-diff --git a/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch
+diff --git a/target/linux/generic/pending-5.15/732-04-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch b/target/linux/generic/pending-5.15/732-04-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch
 new file mode 100644
-index 0000000000..67406bac84
+index 0000000000..fd1898586f
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch
-@@ -0,0 +1,115 @@
++++ b/target/linux/generic/pending-5.15/732-04-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch
+@@ -0,0 +1,654 @@
 +From: Felix Fietkau <nbd@nbd.name>
-+Subject: debloat: disable common USB quirks
++Date: Thu, 27 Oct 2022 20:17:27 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: implement multi-queue
++ support for per-port queues
++
++When sending traffic to multiple ports with different link speeds, queued
++packets to one port can drown out tx to other ports.
++In order to better handle transmission to multiple ports, use the hardware
++shaper feature to implement weighted fair queueing between ports.
++Weight and maximum rate are automatically adjusted based on the link speed
++of the port.
++The first 3 queues are unrestricted and reserved for non-DSA direct tx on
++GMAC ports. The following queues are automatically assigned by the MTK DSA
++tag driver based on the target port number.
++The PPE offload code configures the queues for offloaded traffic in the same
++way.
++This feature is only supported on devices supporting QDMA. All queues still
++share the same DMA ring and descriptor pool.
 +
 +Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
-+ drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
-+ include/linux/usb/hcd.h       |  7 +++++++
-+ 3 files changed, 40 insertions(+), 1 deletion(-)
 +
-+--- a/drivers/usb/host/pci-quirks.c
-++++ b/drivers/usb/host/pci-quirks.c
-+@@ -128,6 +128,8 @@ struct amd_chipset_type {
-+ 	u8 rev;
-+ };
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -54,6 +54,7 @@ static const struct mtk_reg_map mtk_reg_
++ 	},
++ 	.qdma = {
++ 		.qtx_cfg	= 0x1800,
+++		.qtx_sch	= 0x1804,
++ 		.rx_ptr		= 0x1900,
++ 		.rx_cnt_cfg	= 0x1904,
++ 		.qcrx_ptr	= 0x1908,
++@@ -61,6 +62,7 @@ static const struct mtk_reg_map mtk_reg_
++ 		.rst_idx	= 0x1a08,
++ 		.delay_irq	= 0x1a0c,
++ 		.fc_th		= 0x1a10,
+++		.tx_sch_rate	= 0x1a14,
++ 		.int_grp	= 0x1a20,
++ 		.hred		= 0x1a44,
++ 		.ctx_ptr	= 0x1b00,
++@@ -113,6 +115,7 @@ static const struct mtk_reg_map mt7986_r
++ 	},
++ 	.qdma = {
++ 		.qtx_cfg	= 0x4400,
+++		.qtx_sch	= 0x4404,
++ 		.rx_ptr		= 0x4500,
++ 		.rx_cnt_cfg	= 0x4504,
++ 		.qcrx_ptr	= 0x4508,
++@@ -130,6 +133,7 @@ static const struct mtk_reg_map mt7986_r
++ 		.fq_tail	= 0x4724,
++ 		.fq_count	= 0x4728,
++ 		.fq_blen	= 0x472c,
+++		.tx_sch_rate	= 0x4798,
++ 	},
++ 	.gdm1_cnt		= 0x1c00,
++ 	.gdma_to_ppe0		= 0x3333,
++@@ -570,6 +574,75 @@ static void mtk_mac_link_down(struct phy
++ 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
++ }
 + 
-++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+++static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx,
+++				int speed)
+++{
+++	const struct mtk_soc_data *soc = eth->soc;
+++	u32 ofs, val;
 ++
-+ static struct amd_chipset_info {
-+ 	struct pci_dev	*nb_dev;
-+ 	struct pci_dev	*smbus_dev;
-+@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device
-+ }
-+ EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
-+ 
-++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */
+++	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
+++		return;
 ++
-++#if IS_ENABLED(CONFIG_USB_UHCI_HCD)
+++	val = MTK_QTX_SCH_MIN_RATE_EN |
+++	      /* minimum: 10 Mbps */
+++	      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+++	      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+++	      MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
+++	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+++		val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
+++
+++	if (IS_ENABLED(CONFIG_SOC_MT7621)) {
+++		switch (speed) {
+++		case SPEED_10:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 2) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
+++			break;
+++		case SPEED_100:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 3);
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
+++			break;
+++		case SPEED_1000:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 105) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
+++			break;
+++		default:
+++			break;
+++		}
+++	} else {
+++		switch (speed) {
+++		case SPEED_10:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
+++			break;
+++		case SPEED_100:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5);
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
+++			break;
+++		case SPEED_1000:
+++			val |= MTK_QTX_SCH_MAX_RATE_EN |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 10) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5) |
+++			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
+++			break;
+++		default:
+++			break;
+++		}
+++	}
 ++
-+ /*
-+  * Make sure the controller is completely inactive, unable to
-+  * generate interrupts or do DMA.
-+@@ -712,8 +718,17 @@ reset_needed:
-+ 	uhci_reset_hc(pdev, base);
-+ 	return 1;
-+ }
-++#else
-++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
-++{
-++	return 0;
+++	ofs = MTK_QTX_OFFSET * idx;
+++	mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
 ++}
 ++
-++#endif
-+ EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
++ static void mtk_mac_link_up(struct phylink_config *config,
++ 			    struct phy_device *phy,
++ 			    unsigned int mode, phy_interface_t interface,
++@@ -595,6 +668,8 @@ static void mtk_mac_link_up(struct phyli
++ 		break;
++ 	}
 + 
-++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+++	mtk_set_queue_speed(mac->hw, mac->id, speed);
 ++
-+ static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
-+ {
-+ 	u16 cmd;
-+@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(stru
++ 	/* Configure duplex */
++ 	if (duplex == DUPLEX_FULL)
++ 		mcr |= MAC_MCR_FORCE_DPX;
++@@ -1053,7 +1128,8 @@ static void mtk_tx_set_dma_desc_v1(struc
++ 
++ 	WRITE_ONCE(desc->txd1, info->addr);
++ 
++-	data = TX_DMA_SWC | TX_DMA_PLEN0(info->size);
+++	data = TX_DMA_SWC | TX_DMA_PLEN0(info->size) |
+++	       FIELD_PREP(TX_DMA_PQID, info->qid);
++ 	if (info->last)
++ 		data |= TX_DMA_LS0;
++ 	WRITE_ONCE(desc->txd3, data);
++@@ -1087,9 +1163,6 @@ static void mtk_tx_set_dma_desc_v2(struc
++ 		data |= TX_DMA_LS0;
++ 	WRITE_ONCE(desc->txd3, data);
++ 
++-	if (!info->qid && mac->id)
++-		info->qid = MTK_QDMA_GMAC2_QID;
++-
++ 	data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
++ 	data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid);
++ 	WRITE_ONCE(desc->txd4, data);
++@@ -1133,11 +1206,12 @@ static int mtk_tx_map(struct sk_buff *sk
++ 		.gso = gso,
++ 		.csum = skb->ip_summed == CHECKSUM_PARTIAL,
++ 		.vlan = skb_vlan_tag_present(skb),
++-		.qid = skb->mark & MTK_QDMA_TX_MASK,
+++		.qid = skb_get_queue_mapping(skb),
++ 		.vlan_tci = skb_vlan_tag_get(skb),
++ 		.first = true,
++ 		.last = !skb_is_nonlinear(skb),
++ 	};
+++	struct netdev_queue *txq;
++ 	struct mtk_mac *mac = netdev_priv(dev);
++ 	struct mtk_eth *eth = mac->hw;
++ 	const struct mtk_soc_data *soc = eth->soc;
++@@ -1145,8 +1219,10 @@ static int mtk_tx_map(struct sk_buff *sk
++ 	struct mtk_tx_dma *itxd_pdma, *txd_pdma;
++ 	struct mtk_tx_buf *itx_buf, *tx_buf;
++ 	int i, n_desc = 1;
+++	int queue = skb_get_queue_mapping(skb);
++ 	int k = 0;
++ 
+++	txq = netdev_get_tx_queue(dev, queue);
++ 	itxd = ring->next_free;
++ 	itxd_pdma = qdma_to_pdma(ring, itxd);
++ 	if (itxd == ring->last_free)
++@@ -1195,7 +1271,7 @@ static int mtk_tx_map(struct sk_buff *sk
++ 			memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
++ 			txd_info.size = min_t(unsigned int, frag_size,
++ 					      soc->txrx.dma_max_len);
++-			txd_info.qid = skb->mark & MTK_QDMA_TX_MASK;
+++			txd_info.qid = queue;
++ 			txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
++ 					!(frag_size - txd_info.size);
++ 			txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag,
++@@ -1234,7 +1310,7 @@ static int mtk_tx_map(struct sk_buff *sk
++ 			txd_pdma->txd2 |= TX_DMA_LS1;
++ 	}
++ 
++-	netdev_sent_queue(dev, skb->len);
+++	netdev_tx_sent_queue(txq, skb->len);
++ 	skb_tx_timestamp(skb);
++ 
++ 	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
++@@ -1246,8 +1322,7 @@ static int mtk_tx_map(struct sk_buff *sk
++ 	wmb();
++ 
++ 	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++-		if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) ||
++-		    !netdev_xmit_more())
+++		if (netif_xmit_stopped(txq) || !netdev_xmit_more())
++ 			mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
++ 	} else {
++ 		int next_idx;
++@@ -1316,7 +1391,7 @@ static void mtk_wake_queue(struct mtk_et
++ 	for (i = 0; i < MTK_MAC_COUNT; i++) {
++ 		if (!eth->netdev[i])
++ 			continue;
++-		netif_wake_queue(eth->netdev[i]);
+++		netif_tx_wake_all_queues(eth->netdev[i]);
++ 	}
 + }
-+ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
-+ 			PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
-++#endif
-+--- a/drivers/usb/host/pci-quirks.h
-++++ b/drivers/usb/host/pci-quirks.h
-+@@ -5,6 +5,9 @@
-+ #ifdef CONFIG_USB_PCI
-+ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
-+ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
-++#endif  /* CONFIG_USB_PCI */
++ 
++@@ -1340,7 +1415,7 @@ static netdev_tx_t mtk_start_xmit(struct
++ 
++ 	tx_num = mtk_cal_txd_req(eth, skb);
++ 	if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
++-		netif_stop_queue(dev);
+++		netif_tx_stop_all_queues(dev);
++ 		netif_err(eth, tx_queued, dev,
++ 			  "Tx Ring full when queue awake!\n");
++ 		spin_unlock(&eth->page_lock);
++@@ -1366,7 +1441,7 @@ static netdev_tx_t mtk_start_xmit(struct
++ 		goto drop;
++ 
++ 	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
++-		netif_stop_queue(dev);
+++		netif_tx_stop_all_queues(dev);
++ 
++ 	spin_unlock(&eth->page_lock);
++ 
++@@ -1533,10 +1608,12 @@ static int mtk_xdp_submit_frame(struct m
++ 	struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf);
++ 	const struct mtk_soc_data *soc = eth->soc;
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
+++	struct mtk_mac *mac = netdev_priv(dev);
++ 	struct mtk_tx_dma_desc_info txd_info = {
++ 		.size	= xdpf->len,
++ 		.first	= true,
++ 		.last	= !xdp_frame_has_frags(xdpf),
+++		.qid	= mac->id,
++ 	};
++ 	int err, index = 0, n_desc = 1, nr_frags;
++ 	struct mtk_tx_dma *htxd, *txd, *txd_pdma;
++@@ -1587,6 +1664,7 @@ static int mtk_xdp_submit_frame(struct m
++ 		memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
++ 		txd_info.size = skb_frag_size(&sinfo->frags[index]);
++ 		txd_info.last = index + 1 == nr_frags;
+++		txd_info.qid = mac->id;
++ 		data = skb_frag_address(&sinfo->frags[index]);
++ 
++ 		index++;
++@@ -1938,8 +2016,46 @@ rx_done:
++ 	return done;
++ }
++ 
+++struct mtk_poll_state {
+++    struct netdev_queue *txq;
+++    unsigned int total;
+++    unsigned int done;
+++    unsigned int bytes;
+++};
 ++
-++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS)
-+ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
-+ bool usb_amd_hang_symptom_quirk(void);
-+ bool usb_amd_prefetch_quirk(void);
-+@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev,
-+ bool usb_amd_pt_check_port(struct device *device, int port);
-+ #else
-+ struct pci_dev;
-++static inline int usb_amd_quirk_pll_check(void)
-++{
-++	return 0;
-++}
-++static inline bool usb_amd_hang_symptom_quirk(void)
-++{
-++	return false;
-++}
-++static inline bool usb_amd_prefetch_quirk(void)
+++static void
+++mtk_poll_tx_done(struct mtk_eth *eth, struct mtk_poll_state *state, u8 mac,
+++		 struct sk_buff *skb)
 ++{
-++	return false;
+++	struct netdev_queue *txq;
+++	struct net_device *dev;
+++	unsigned int bytes = skb->len;
+++
+++	state->total++;
+++	eth->tx_packets++;
+++	eth->tx_bytes += bytes;
+++
+++	dev = eth->netdev[mac];
+++	if (!dev)
+++		return;
+++
+++	txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
+++	if (state->txq == txq) {
+++		state->done++;
+++		state->bytes += bytes;
+++		return;
+++	}
+++
+++	if (state->txq)
+++		netdev_tx_completed_queue(state->txq, state->done, state->bytes);
+++
+++	state->txq = txq;
+++	state->done = 1;
+++	state->bytes = bytes;
 ++}
-+ static inline void usb_amd_quirk_pll_disable(void) {}
-+ static inline void usb_amd_quirk_pll_enable(void) {}
-+ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
-+@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port
+++
++ static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
++-			    unsigned int *done, unsigned int *bytes)
+++			    struct mtk_poll_state *state)
 + {
-+ 	return false;
++ 	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
++@@ -1969,12 +2085,9 @@ static int mtk_poll_tx_qdma(struct mtk_e
++ 			break;
++ 
++ 		if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++-			if (tx_buf->type == MTK_TYPE_SKB) {
++-				struct sk_buff *skb = tx_buf->data;
+++			if (tx_buf->type == MTK_TYPE_SKB)
+++				mtk_poll_tx_done(eth, state, mac, tx_buf->data);
++ 
++-				bytes[mac] += skb->len;
++-				done[mac]++;
++-			}
++ 			budget--;
++ 		}
++ 		mtk_tx_unmap(eth, tx_buf, true);
++@@ -1992,7 +2105,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
 + }
-++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {}
-++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
++ 
++ static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget,
++-			    unsigned int *done, unsigned int *bytes)
+++			    struct mtk_poll_state *state)
++ {
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
++ 	struct mtk_tx_buf *tx_buf;
++@@ -2008,12 +2121,8 @@ static int mtk_poll_tx_pdma(struct mtk_e
++ 			break;
++ 
++ 		if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++-			if (tx_buf->type == MTK_TYPE_SKB) {
++-				struct sk_buff *skb = tx_buf->data;
++-
++-				bytes[0] += skb->len;
++-				done[0]++;
++-			}
+++			if (tx_buf->type == MTK_TYPE_SKB)
+++				mtk_poll_tx_done(eth, state, 0, tx_buf->data);
++ 			budget--;
++ 		}
++ 		mtk_tx_unmap(eth, tx_buf, true);
++@@ -2034,26 +2143,15 @@ static int mtk_poll_tx(struct mtk_eth *e
++ {
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
++ 	struct dim_sample dim_sample = {};
++-	unsigned int done[MTK_MAX_DEVS];
++-	unsigned int bytes[MTK_MAX_DEVS];
++-	int total = 0, i;
++-
++-	memset(done, 0, sizeof(done));
++-	memset(bytes, 0, sizeof(bytes));
+++	struct mtk_poll_state state = {};
++ 
++ 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
++-		budget = mtk_poll_tx_qdma(eth, budget, done, bytes);
+++		budget = mtk_poll_tx_qdma(eth, budget, &state);
++ 	else
++-		budget = mtk_poll_tx_pdma(eth, budget, done, bytes);
+++		budget = mtk_poll_tx_pdma(eth, budget, &state);
++ 
++-	for (i = 0; i < MTK_MAC_COUNT; i++) {
++-		if (!eth->netdev[i] || !done[i])
++-			continue;
++-		netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
++-		total += done[i];
++-		eth->tx_packets += done[i];
++-		eth->tx_bytes += bytes[i];
++-	}
+++	if (state.txq)
+++		netdev_tx_completed_queue(state.txq, state.done, state.bytes);
++ 
++ 	dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes,
++ 			  &dim_sample);
++@@ -2063,7 +2161,7 @@ static int mtk_poll_tx(struct mtk_eth *e
++ 	    (atomic_read(&ring->free_count) > ring->thresh))
++ 		mtk_wake_queue(eth);
++ 
++-	return total;
+++	return state.total;
++ }
++ 
++ static void mtk_handle_status_irq(struct mtk_eth *eth)
++@@ -2149,6 +2247,7 @@ static int mtk_tx_alloc(struct mtk_eth *
++ 	int i, sz = soc->txrx.txd_size;
++ 	struct mtk_tx_dma_v2 *txd;
++ 	int ring_size;
+++	u32 ofs, val;
++ 
++ 	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
++ 		ring_size = MTK_QDMA_RING_SIZE;
++@@ -2216,8 +2315,25 @@ static int mtk_tx_alloc(struct mtk_eth *
++ 			ring->phys + ((ring_size - 1) * sz),
++ 			soc->reg_map->qdma.crx_ptr);
++ 		mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
++-		mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
++-			soc->reg_map->qdma.qtx_cfg);
+++
+++		for (i = 0, ofs = 0; i < MTK_QDMA_NUM_QUEUES; i++) {
+++			val = (QDMA_RES_THRES << 8) | QDMA_RES_THRES;
+++			mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs);
+++
+++			val = MTK_QTX_SCH_MIN_RATE_EN |
+++			      /* minimum: 10 Mbps */
+++			      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+++			      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+++			      MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
+++			if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+++				val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
+++			mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
+++			ofs += MTK_QTX_OFFSET;
+++		}
+++		val = MTK_QDMA_TX_SCH_MAX_WFQ | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
+++		mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate);
+++		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+++			mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4);
++ 	} else {
++ 		mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
++ 		mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0);
++@@ -2882,7 +2998,7 @@ static int mtk_start_dma(struct mtk_eth
++ 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ 			val |= MTK_MUTLI_CNT | MTK_RESV_BUF |
++ 			       MTK_WCOMP_EN | MTK_DMAD_WR_WDONE |
++-			       MTK_CHK_DDONE_EN;
+++			       MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN;
++ 		else
++ 			val |= MTK_RX_BT_32DWORDS;
++ 		mtk_w32(eth, val, reg_map->qdma.glo_cfg);
++@@ -2928,6 +3044,45 @@ static void mtk_gdm_config(struct mtk_et
++ 	mtk_w32(eth, 0, MTK_RST_GL);
++ }
++ 
+++static int mtk_device_event(struct notifier_block *n, unsigned long event, void *ptr)
 ++{
-++	return false;
+++	struct mtk_mac *mac = container_of(n, struct mtk_mac, device_notifier);
+++	struct mtk_eth *eth = mac->hw;
+++	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+++	struct ethtool_link_ksettings s;
+++	struct net_device *ldev;
+++	struct list_head *iter;
+++	struct dsa_port *dp;
+++
+++	if (event != NETDEV_CHANGE)
+++		return NOTIFY_DONE;
+++
+++	netdev_for_each_lower_dev(dev, ldev, iter) {
+++		if (netdev_priv(ldev) == mac)
+++			goto found;
+++	}
+++
+++	return NOTIFY_DONE;
+++
+++found:
+++	if (!dsa_slave_dev_check(dev))
+++		return NOTIFY_DONE;
+++
+++	if (__ethtool_get_link_ksettings(dev, &s))
+++		return NOTIFY_DONE;
+++
+++	if (s.base.speed == 0 || s.base.speed == ((__u32)-1))
+++		return NOTIFY_DONE;
+++
+++	dp = dsa_port_from_netdev(dev);
+++	if (dp->index >= MTK_QDMA_NUM_QUEUES)
+++		return NOTIFY_DONE;
+++
+++	mtk_set_queue_speed(eth, dp->index + 3, s.base.speed);
+++
+++	return NOTIFY_DONE;
 ++}
-+ #endif  /* CONFIG_USB_PCI */
+++
++ static int mtk_open(struct net_device *dev)
++ {
++ 	struct mtk_mac *mac = netdev_priv(dev);
++@@ -2970,7 +3125,8 @@ static int mtk_open(struct net_device *d
++ 		refcount_inc(&eth->dma_refcnt);
 + 
-+ #endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
-+--- a/include/linux/usb/hcd.h
-++++ b/include/linux/usb/hcd.h
-+@@ -497,7 +497,14 @@ extern int usb_hcd_pci_probe(struct pci_
-+ extern void usb_hcd_pci_remove(struct pci_dev *dev);
-+ extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
++ 	phylink_start(mac->phylink);
++-	netif_start_queue(dev);
+++	netif_tx_start_all_queues(dev);
+++
++ 	return 0;
++ }
 + 
-++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
-+ extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
-++#else
-++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev)
++@@ -3486,8 +3642,12 @@ static int mtk_unreg_dev(struct mtk_eth
++ 	int i;
++ 
++ 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+++		struct mtk_mac *mac;
++ 		if (!eth->netdev[i])
++ 			continue;
+++		mac = netdev_priv(eth->netdev[i]);
+++		if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+++			unregister_netdevice_notifier(&mac->device_notifier);
++ 		unregister_netdev(eth->netdev[i]);
++ 	}
++ 
++@@ -3703,6 +3863,23 @@ static int mtk_set_rxnfc(struct net_devi
++ 	return ret;
++ }
++ 
+++static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb,
+++			    struct net_device *sb_dev)
 ++{
-++	return 0;
+++	struct mtk_mac *mac = netdev_priv(dev);
+++	unsigned int queue = 0;
+++
+++	if (netdev_uses_dsa(dev))
+++		queue = skb_get_queue_mapping(skb) + 3;
+++	else
+++		queue = mac->id;
+++
+++	if (queue >= dev->num_tx_queues)
+++		queue = 0;
+++
+++	return queue;
 ++}
-++#endif
+++
++ static const struct ethtool_ops mtk_ethtool_ops = {
++ 	.get_link_ksettings	= mtk_get_link_ksettings,
++ 	.set_link_ksettings	= mtk_set_link_ksettings,
++@@ -3738,6 +3915,7 @@ static const struct net_device_ops mtk_n
++ 	.ndo_setup_tc		= mtk_eth_setup_tc,
++ 	.ndo_bpf		= mtk_xdp,
++ 	.ndo_xdp_xmit		= mtk_xdp_xmit,
+++	.ndo_select_queue	= mtk_select_queue,
++ };
 + 
-+ #ifdef CONFIG_PM
-+ extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
-diff --git a/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
++ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
++@@ -3747,6 +3925,7 @@ static int mtk_add_mac(struct mtk_eth *e
++ 	struct phylink *phylink;
++ 	struct mtk_mac *mac;
++ 	int id, err;
+++	int txqs = 1;
++ 
++ 	if (!_id) {
++ 		dev_err(eth->dev, "missing mac id\n");
++@@ -3764,7 +3943,10 @@ static int mtk_add_mac(struct mtk_eth *e
++ 		return -EINVAL;
++ 	}
++ 
++-	eth->netdev[id] = alloc_etherdev(sizeof(*mac));
+++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+++		txqs = MTK_QDMA_NUM_QUEUES;
+++
+++	eth->netdev[id] = alloc_etherdev_mqs(sizeof(*mac), txqs, 1);
++ 	if (!eth->netdev[id]) {
++ 		dev_err(eth->dev, "alloc_etherdev failed\n");
++ 		return -ENOMEM;
++@@ -3861,6 +4043,11 @@ static int mtk_add_mac(struct mtk_eth *e
++ 	else
++ 		eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
++ 
+++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+++		mac->device_notifier.notifier_call = mtk_device_event;
+++		register_netdevice_notifier(&mac->device_notifier);
+++	}
+++
++ 	return 0;
++ 
++ free_netdev:
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -22,6 +22,7 @@
++ #include <linux/bpf_trace.h>
++ #include "mtk_ppe.h"
++ 
+++#define MTK_QDMA_NUM_QUEUES	16
++ #define MTK_QDMA_PAGE_SIZE	2048
++ #define MTK_MAX_RX_LENGTH	1536
++ #define MTK_MAX_RX_LENGTH_2K	2048
++@@ -203,8 +204,26 @@
++ #define MTK_RING_MAX_AGG_CNT_H		((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3)
++ 
++ /* QDMA TX Queue Configuration Registers */
+++#define MTK_QTX_OFFSET		0x10
++ #define QDMA_RES_THRES		4
++ 
+++/* QDMA Tx Queue Scheduler Configuration Registers */
+++#define MTK_QTX_SCH_TX_SEL		BIT(31)
+++#define MTK_QTX_SCH_TX_SEL_V2		GENMASK(31, 30)
+++
+++#define MTK_QTX_SCH_LEAKY_BUCKET_EN	BIT(30)
+++#define MTK_QTX_SCH_LEAKY_BUCKET_SIZE	GENMASK(29, 28)
+++#define MTK_QTX_SCH_MIN_RATE_EN		BIT(27)
+++#define MTK_QTX_SCH_MIN_RATE_MAN	GENMASK(26, 20)
+++#define MTK_QTX_SCH_MIN_RATE_EXP	GENMASK(19, 16)
+++#define MTK_QTX_SCH_MAX_RATE_WEIGHT	GENMASK(15, 12)
+++#define MTK_QTX_SCH_MAX_RATE_EN		BIT(11)
+++#define MTK_QTX_SCH_MAX_RATE_MAN	GENMASK(10, 4)
+++#define MTK_QTX_SCH_MAX_RATE_EXP	GENMASK(3, 0)
+++
+++/* QDMA TX Scheduler Rate Control Register */
+++#define MTK_QDMA_TX_SCH_MAX_WFQ		BIT(15)
+++
++ /* QDMA Global Configuration Register */
++ #define MTK_RX_2B_OFFSET	BIT(31)
++ #define MTK_RX_BT_32DWORDS	(3 << 11)
++@@ -223,6 +242,7 @@
++ #define MTK_WCOMP_EN		BIT(24)
++ #define MTK_RESV_BUF		(0x40 << 16)
++ #define MTK_MUTLI_CNT		(0x4 << 12)
+++#define MTK_LEAKY_BUCKET_EN	BIT(11)
++ 
++ /* QDMA Flow Control Register */
++ #define FC_THRES_DROP_MODE	BIT(20)
++@@ -251,8 +271,6 @@
++ #define MTK_STAT_OFFSET		0x40
++ 
++ /* QDMA TX NUM */
++-#define MTK_QDMA_TX_NUM		16
++-#define MTK_QDMA_TX_MASK	(MTK_QDMA_TX_NUM - 1)
++ #define QID_BITS_V2(x)		(((x) & 0x3f) << 16)
++ #define MTK_QDMA_GMAC2_QID	8
++ 
++@@ -282,6 +300,7 @@
++ #define TX_DMA_PLEN0(x)		(((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
++ #define TX_DMA_PLEN1(x)		((x) & eth->soc->txrx.dma_max_len)
++ #define TX_DMA_SWC		BIT(14)
+++#define TX_DMA_PQID		GENMASK(3, 0)
++ 
++ /* PDMA on MT7628 */
++ #define TX_DMA_DONE		BIT(31)
++@@ -930,6 +949,7 @@ struct mtk_reg_map {
++ 	} pdma;
++ 	struct {
++ 		u32	qtx_cfg;	/* tx queue configuration */
+++		u32	qtx_sch;	/* tx queue scheduler configuration */
++ 		u32	rx_ptr;		/* rx base pointer */
++ 		u32	rx_cnt_cfg;	/* rx max count configuration */
++ 		u32	qcrx_ptr;	/* rx cpu pointer */
++@@ -947,6 +967,7 @@ struct mtk_reg_map {
++ 		u32	fq_tail;	/* fq tail pointer */
++ 		u32	fq_count;	/* fq free page count */
++ 		u32	fq_blen;	/* fq free page buffer length */
+++		u32	tx_sch_rate;	/* tx scheduler rate control registers */
++ 	} qdma;
++ 	u32	gdm1_cnt;
++ 	u32	gdma_to_ppe0;
++@@ -1139,6 +1160,7 @@ struct mtk_mac {
++ 	__be32				hwlro_ip[MTK_MAX_LRO_IP_CNT];
++ 	int				hwlro_ip_cnt;
++ 	unsigned int			syscfg0;
+++	struct notifier_block		device_notifier;
++ };
++ 
++ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+diff --git a/target/linux/generic/pending-5.15/732-05-net-dsa-tag_mtk-assign-per-port-queues.patch b/target/linux/generic/pending-5.15/732-05-net-dsa-tag_mtk-assign-per-port-queues.patch
 new file mode 100644
-index 0000000000..33eb34c913
+index 0000000000..7739366ade
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
-@@ -0,0 +1,26 @@
-+From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
-+From: Pawel Dembicki <paweldembicki@gmail.com>
-+Date: Sun, 18 Feb 2018 17:08:04 +0100
-+Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
-+
-+In devices, where fdt is used, is impossible to apply platform data
-+without proper fdt node.
++++ b/target/linux/generic/pending-5.15/732-05-net-dsa-tag_mtk-assign-per-port-queues.patch
+@@ -0,0 +1,20 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Fri, 28 Oct 2022 18:16:03 +0200
++Subject: [PATCH] net: dsa: tag_mtk: assign per-port queues
 +
-+This patch allow to use platform data in devices with fdt.
++Keeps traffic sent to the switch within link speed limits
 +
-+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/w1/masters/w1-gpio.c | 7 +++----
-+ 1 file changed, 3 insertions(+), 4 deletions(-)
 +
-+--- a/drivers/w1/masters/w1-gpio.c
-++++ b/drivers/w1/masters/w1-gpio.c
-+@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
-+ 	enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
-+ 	int err;
++--- a/net/dsa/tag_mtk.c
+++++ b/net/dsa/tag_mtk.c
++@@ -33,6 +33,8 @@ static struct sk_buff *mtk_tag_xmit(stru
++ 	if (__skb_put_padto(skb, ETH_ZLEN + MTK_HDR_LEN, false))
++ 		return NULL;
 + 
-+-	if (of_have_populated_dt()) {
-++	if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
-+ 		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-+ 		if (!pdata)
-+ 			return -ENOMEM;
-diff --git a/target/linux/generic/pending-5.15/834-ledtrig-libata.patch b/target/linux/generic/pending-5.15/834-ledtrig-libata.patch
+++	skb_set_queue_mapping(skb, dp->index);
+++
++ 	/* Build the special tag after the MAC Source Address. If VLAN header
++ 	 * is present, it's required that VLAN header and special tag is
++ 	 * being combined. Only in this way we can allow the switch can parse
+diff --git a/target/linux/generic/pending-5.15/732-06-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch b/target/linux/generic/pending-5.15/732-06-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch
 new file mode 100644
-index 0000000000..3977b57d9f
+index 0000000000..05161c3479
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/834-ledtrig-libata.patch
-@@ -0,0 +1,149 @@
-+From: Daniel Golle <daniel@makrotopia.org>
-+Subject: libata: add ledtrig support
-+
-+This adds a LED trigger for each ATA port indicating disk activity.
++++ b/target/linux/generic/pending-5.15/732-06-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch
+@@ -0,0 +1,93 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 3 Nov 2022 17:49:44 +0100
++Subject: [PATCH] net: ethernet: mediatek: ppe: assign per-port queues
++ for offloaded traffic
 +
-+As this is needed only on specific platforms (NAS SoCs and such),
-+these platforms should define ARCH_WANTS_LIBATA_LEDS if there
-+are boards with LED(s) intended to indicate ATA disk activity and
-+need the OS to take care of that.
-+In that way, if not selected, LED trigger support not will be
-+included in libata-core and both, codepaths and structures remain
-+untouched.
++Keeps traffic sent to the switch within link speed limits
 +
-+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/ata/Kconfig       | 16 ++++++++++++++++
-+ drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++
-+ include/linux/libata.h    |  9 +++++++++
-+ 3 files changed, 66 insertions(+)
 +
-+--- a/drivers/ata/Kconfig
-++++ b/drivers/ata/Kconfig
-+@@ -67,6 +67,22 @@ config ATA_FORCE
-+ 
-+ 	  If unsure, say Y.
++--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
++@@ -445,6 +445,24 @@ static inline bool mtk_foe_entry_usable(
++ 	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
++ }
 + 
-++config ARCH_WANT_LIBATA_LEDS
-++	bool
+++int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+++			    unsigned int queue)
+++{
+++	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
 ++
-++config ATA_LEDS
-++	bool "support ATA port LED triggers"
-++	depends on ARCH_WANT_LIBATA_LEDS
-++	select NEW_LEDS
-++	select LEDS_CLASS
-++	select LEDS_TRIGGERS
-++	default y
-++	help
-++	  This option adds a LED trigger for each registered ATA port.
-++	  It is used to drive disk activity leds connected via GPIO.
-++
-++	  If unsure, say N.
-++
-+ config ATA_ACPI
-+ 	bool "ATA ACPI Support"
-+ 	depends on ACPI
-+--- a/drivers/ata/libata-core.c
-++++ b/drivers/ata/libata-core.c
-+@@ -656,6 +656,19 @@ u64 ata_tf_read_block(const struct ata_t
-+ 	return block;
-+ }
-+ 
-++#ifdef CONFIG_ATA_LEDS
-++#define LIBATA_BLINK_DELAY 20 /* ms */
-++static inline void ata_led_act(struct ata_port *ap)
-++{
-++	unsigned long led_delay = LIBATA_BLINK_DELAY;
-++
-++	if (unlikely(!ap->ledtrig))
-++		return;
+++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+++		*ib2 &= ~MTK_FOE_IB2_QID_V2;
+++		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue);
+++		*ib2 |= MTK_FOE_IB2_PSE_QOS_V2;
+++	} else {
+++		*ib2 &= ~MTK_FOE_IB2_QID;
+++		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, queue);
+++		*ib2 |= MTK_FOE_IB2_PSE_QOS;
+++	}
 ++
-++	led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0);
+++	return 0;
 ++}
-++#endif
 ++
-+ /**
-+  *	ata_build_rw_tf - Build ATA taskfile for given read/write request
-+  *	@tf: Target ATA taskfile
-+@@ -4576,6 +4589,9 @@ struct ata_queued_cmd *ata_qc_new_init(s
-+ 		if (tag < 0)
-+ 			return NULL;
-+ 	}
-++#ifdef CONFIG_ATA_LEDS
-++	ata_led_act(ap);
-++#endif
++ static bool
++ mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
++ 		     struct mtk_foe_entry *data)
++--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
++@@ -69,7 +69,9 @@ enum {
++ #define MTK_FOE_IB2_DSCP		GENMASK(31, 24)
++ 
++ /* CONFIG_MEDIATEK_NETSYS_V2 */
+++#define MTK_FOE_IB2_QID_V2			GENMASK(6, 0)
++ #define MTK_FOE_IB2_PORT_MG_V2		BIT(7)
+++#define MTK_FOE_IB2_PSE_QOS_V2		BIT(8)
++ #define MTK_FOE_IB2_DEST_PORT_V2	GENMASK(12, 9)
++ #define MTK_FOE_IB2_MULTICAST_V2	BIT(13)
++ #define MTK_FOE_IB2_WDMA_WINFO_V2	BIT(19)
++@@ -369,6 +371,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e
++ 			    int sid);
++ int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ 			   int wdma_idx, int txq, int bss, int wcid);
+++int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+++			    unsigned int queue);
++ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
++ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
++ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
++--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++@@ -188,7 +188,7 @@ mtk_flow_set_output_device(struct mtk_et
++ 			   int *wed_index)
++ {
++ 	struct mtk_wdma_info info = {};
++-	int pse_port, dsa_port;
+++	int pse_port, dsa_port, queue;
 + 
-+ 	qc = __ata_qc_from_tag(ap, tag);
-+ 	qc->tag = qc->hw_tag = tag;
-+@@ -5354,6 +5370,9 @@ struct ata_port *ata_port_alloc(struct a
-+ 	ap->stats.unhandled_irq = 1;
-+ 	ap->stats.idle_irq = 1;
-+ #endif
-++#ifdef CONFIG_ATA_LEDS
-++	ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
-++#endif
-+ 	ata_sff_port_init(ap);
++ 	if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
++ 		mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
++@@ -212,8 +212,6 @@ mtk_flow_set_output_device(struct mtk_et
++ 	}
 + 
-+ 	return ap;
-+@@ -5389,6 +5408,12 @@ static void ata_host_release(struct kref
++ 	dsa_port = mtk_flow_get_dsa_port(&dev);
++-	if (dsa_port >= 0)
++-		mtk_foe_entry_set_dsa(eth, foe, dsa_port);
 + 
-+ 		kfree(ap->pmp_link);
-+ 		kfree(ap->slave_link);
-++#ifdef CONFIG_ATA_LEDS
-++		if (ap->ledtrig) {
-++			led_trigger_unregister(ap->ledtrig);
-++			kfree(ap->ledtrig);
-++		};
-++#endif
-+ 		kfree(ap);
-+ 		host->ports[i] = NULL;
-+ 	}
-+@@ -5795,7 +5820,23 @@ int ata_host_register(struct ata_host *h
-+ 		host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
-+ 		host->ports[i]->local_port_no = i + 1;
-+ 	}
-++#ifdef CONFIG_ATA_LEDS
-++	for (i = 0; i < host->n_ports; i++) {
-++		if (unlikely(!host->ports[i]->ledtrig))
-++			continue;
++ 	if (dev == eth->netdev[0])
++ 		pse_port = 1;
++@@ -222,6 +220,14 @@ mtk_flow_set_output_device(struct mtk_et
++ 	else
++ 		return -EOPNOTSUPP;
 + 
-++		snprintf(host->ports[i]->ledtrig_name,
-++			sizeof(host->ports[i]->ledtrig_name), "ata%u",
-++			host->ports[i]->print_id);
-++
-++		host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name;
-++
-++		if (led_trigger_register(host->ports[i]->ledtrig)) {
-++			kfree(host->ports[i]->ledtrig);
-++			host->ports[i]->ledtrig = NULL;
-++		}
+++	if (dsa_port >= 0) {
+++		mtk_foe_entry_set_dsa(eth, foe, dsa_port);
+++		queue = 3 + dsa_port;
+++	} else {
+++		queue = pse_port - 1;
 ++	}
-++#endif
-+ 	/* Create associated sysfs transport objects  */
-+ 	for (i = 0; i < host->n_ports; i++) {
-+ 		rc = ata_tport_add(host->dev,host->ports[i]);
-+--- a/include/linux/libata.h
-++++ b/include/linux/libata.h
-+@@ -23,6 +23,9 @@
-+ #include <linux/cdrom.h>
-+ #include <linux/sched.h>
-+ #include <linux/async.h>
-++#ifdef CONFIG_ATA_LEDS
-++#include <linux/leds.h>
-++#endif
-+ 
-+ /*
-+  * Define if arch has non-standard setup.  This is a _PCI_ standard
-+@@ -888,6 +891,12 @@ struct ata_port {
-+ #ifdef CONFIG_ATA_ACPI
-+ 	struct ata_acpi_gtm	__acpi_init_gtm; /* use ata_acpi_init_gtm() */
-+ #endif
-++
-++#ifdef CONFIG_ATA_LEDS
-++	struct led_trigger	*ledtrig;
-++	char			ledtrig_name[8];
-++#endif
+++	mtk_foe_entry_set_queue(eth, foe, queue);
 ++
-+ 	/* owned by EH */
-+ 	u8			sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
-+ };
-diff --git a/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
++ out:
++ 	mtk_foe_entry_set_pse_port(eth, foe, pse_port);
++ 
+diff --git a/target/linux/generic/pending-5.15/732-07-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/pending-5.15/732-07-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch
 new file mode 100644
-index 0000000000..5ca8933d6f
+index 0000000000..8c711ba802
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
-@@ -0,0 +1,26 @@
-+From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-+Date: Sat, 20 Feb 2021 18:36:38 +0100
-+Subject: [PATCH] hwrng: bcm2835: set quality to 1000
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
++++ b/target/linux/generic/pending-5.15/732-07-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch
+@@ -0,0 +1,28 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 27 Oct 2022 23:39:52 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: compile out netsys v2 code
++ on mt7621
 +
-+This allows devices without a high precission timer to reduce boot from >100s
-+to <30s.
++Avoid some branches in the hot path on low-end devices with limited CPU power,
++and reduce code size
 +
-+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/char/hw_random/bcm2835-rng.c | 1 +
-+ 1 file changed, 1 insertion(+)
 +
-+--- a/drivers/char/hw_random/bcm2835-rng.c
-++++ b/drivers/char/hw_random/bcm2835-rng.c
-+@@ -170,6 +170,7 @@ static int bcm2835_rng_probe(struct plat
-+ 	priv->rng.init = bcm2835_rng_init;
-+ 	priv->rng.read = bcm2835_rng_read;
-+ 	priv->rng.cleanup = bcm2835_rng_cleanup;
-++	priv->rng.quality = 1000;
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -895,7 +895,13 @@ enum mkt_eth_capabilities {
++ #define MTK_MUX_GMAC12_TO_GEPHY_SGMII   \
++ 	(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX)
 + 
-+ 	if (dev_of_node(dev)) {
-+ 		rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
-diff --git a/target/linux/generic/pending-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch b/target/linux/generic/pending-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
++-#define MTK_HAS_CAPS(caps, _x)		(((caps) & (_x)) == (_x))
+++#ifdef CONFIG_SOC_MT7621
+++#define MTK_CAP_MASK MTK_NETSYS_V2
+++#else
+++#define MTK_CAP_MASK 0
+++#endif
+++
+++#define MTK_HAS_CAPS(caps, _x)		(((caps) & (_x) & ~(MTK_CAP_MASK)) == (_x))
++ 
++ #define MT7621_CAPS  (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \
++ 		      MTK_GMAC2_RGMII | MTK_SHARED_INT | \
+diff --git a/target/linux/generic/pending-5.15/732-08-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch b/target/linux/generic/pending-5.15/732-08-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch
 new file mode 100644
-index 0000000000..530ab9ba13
+index 0000000000..0478cb528e
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
-@@ -0,0 +1,59 @@
-+From 078c6a1cbd4cd7496048786beec2e312577bebbf Mon Sep 17 00:00:00 2001
-+From: Pawel Dembicki <paweldembicki@gmail.com>
-+Date: Tue, 11 Jan 2022 23:11:32 +0100
-+Subject: [PATCH] net: qmi_wwan: add ZTE MF286D modem 19d2:1485
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
++++ b/target/linux/generic/pending-5.15/732-08-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch
+@@ -0,0 +1,72 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Tue, 8 Nov 2022 15:03:15 +0100
++Subject: [PATCH] net: dsa: add support for DSA rx offloading via
++ metadata dst
 +
-+Modem from ZTE MF286D is an Qualcomm MDM9250 based 3G/4G modem.
-+
-+T:  Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  3 Spd=5000 MxCh= 0
-+D:  Ver= 3.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs=  1
-+P:  Vendor=19d2 ProdID=1485 Rev=52.87
-+S:  Manufacturer=ZTE,Incorporated
-+S:  Product=ZTE Technologies MSM
-+S:  SerialNumber=MF286DZTED000000
-+C:* #Ifs= 7 Cfg#= 1 Atr=80 MxPwr=896mA
-+A:  FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=06 Prot=00
-+I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=ff Driver=rndis_host
-+E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
-+I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
-+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
-+E:  Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
-+E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
-+E:  Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
-+E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
-+E:  Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
-+E:  Ad=88(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
-+E:  Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs
-+E:  Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
-+E:  Ad=89(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
++If a metadata dst is present with the type METADATA_HW_PORT_MUX on a dsa cpu
++port netdev, assume that it carries the port number and that there is no DSA
++tag present in the skb data.
 +
-+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
-+Acked-by: Bjørn Mork <bjorn@mork.no>
-+Signed-off-by: David S. Miller <davem@davemloft.net>
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/net/usb/qmi_wwan.c | 1 +
-+ 1 file changed, 1 insertion(+)
 +
-+--- a/drivers/net/usb/qmi_wwan.c
-++++ b/drivers/net/usb/qmi_wwan.c
-+@@ -1313,6 +1313,7 @@ static const struct usb_device_id produc
-+ 	{QMI_FIXED_INTF(0x19d2, 0x1426, 2)},	/* ZTE MF91 */
-+ 	{QMI_FIXED_INTF(0x19d2, 0x1428, 2)},	/* Telewell TW-LTE 4G v2 */
-+ 	{QMI_FIXED_INTF(0x19d2, 0x1432, 3)},	/* ZTE ME3620 */
-++	{QMI_FIXED_INTF(0x19d2, 0x1485, 5)},	/* ZTE MF286D */
-+ 	{QMI_FIXED_INTF(0x19d2, 0x2002, 4)},	/* ZTE (Vodafone) K3765-Z */
-+ 	{QMI_FIXED_INTF(0x2001, 0x7e16, 3)},	/* D-Link DWM-221 */
-+ 	{QMI_FIXED_INTF(0x2001, 0x7e19, 4)},	/* D-Link DWM-221 B1 */
-diff --git a/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
++--- a/net/core/flow_dissector.c
+++++ b/net/core/flow_dissector.c
++@@ -940,12 +940,14 @@ bool __skb_flow_dissect(const struct net
++ #if IS_ENABLED(CONFIG_NET_DSA)
++ 		if (unlikely(skb->dev && netdev_uses_dsa(skb->dev) &&
++ 			     proto == htons(ETH_P_XDSA))) {
+++			struct metadata_dst *md_dst = skb_metadata_dst(skb);
++ 			const struct dsa_device_ops *ops;
++ 			int offset = 0;
++ 
++ 			ops = skb->dev->dsa_ptr->tag_ops;
++ 			/* Only DSA header taggers break flow dissection */
++-			if (ops->needed_headroom) {
+++			if (ops->needed_headroom &&
+++			    (!md_dst || md_dst->type != METADATA_HW_PORT_MUX)) {
++ 				if (ops->flow_dissect)
++ 					ops->flow_dissect(skb, &proto, &offset);
++ 				else
++--- a/net/dsa/dsa.c
+++++ b/net/dsa/dsa.c
++@@ -20,6 +20,7 @@
++ #include <linux/phy_fixed.h>
++ #include <linux/ptp_classify.h>
++ #include <linux/etherdevice.h>
+++#include <net/dst_metadata.h>
++ 
++ #include "dsa_priv.h"
++ 
++@@ -225,6 +226,7 @@ static bool dsa_skb_defer_rx_timestamp(s
++ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
++ 			  struct packet_type *pt, struct net_device *unused)
++ {
+++	struct metadata_dst *md_dst = skb_metadata_dst(skb);
++ 	struct dsa_port *cpu_dp = dev->dsa_ptr;
++ 	struct sk_buff *nskb = NULL;
++ 	struct dsa_slave_priv *p;
++@@ -238,7 +240,22 @@ static int dsa_switch_rcv(struct sk_buff
++ 	if (!skb)
++ 		return 0;
++ 
++-	nskb = cpu_dp->rcv(skb, dev);
+++	if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) {
+++		unsigned int port = md_dst->u.port_info.port_id;
+++
+++		skb_dst_drop(skb);
+++		if (!skb_has_extensions(skb))
+++			skb->slow_gro = 0;
+++
+++		skb->dev = dsa_master_find_slave(dev, 0, port);
+++		if (likely(skb->dev)) {
+++			dsa_default_offload_fwd_mark(skb);
+++			nskb = skb;
+++		}
+++	} else {
+++		nskb = cpu_dp->rcv(skb, dev);
+++	}
+++
++ 	if (!nskb) {
++ 		kfree_skb(skb);
++ 		return 0;
+diff --git a/target/linux/generic/pending-5.15/732-09-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch b/target/linux/generic/pending-5.15/732-09-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch
 new file mode 100644
-index 0000000000..de28ebe73a
+index 0000000000..c109067802
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
-@@ -0,0 +1,102 @@
-+From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-+Date: Mon, 10 Jan 2022 02:02:00 +0100
-+Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver
-+ structure
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+Marc Zyngier says [1] that we should use struct irq_chip as a global
-+static struct in the driver. Even though the structure currently
-+contains a dynamic member (parent_device), Marc says [2] that he plans
-+to kill it and make the structure completely static.
-+
-+We have already converted others irq_chip structures in this driver in
-+this way, but we omitted this one because the .name member is
-+dynamically created from device's name, and the name is displayed in
-+sysfs, so changing it would break sysfs ABI.
-+
-+The rationale for changing the name (to "advk-INT") in spite of sysfs
-+ABI, and thus allowing to convert to a static structure, is that after
-+the other changes we made in this series, the IRQ chip is basically
-+something different: it no logner generates ERR and PME interrupts (they
-+are generated by emulated bridge's rp_irq_chip).
++++ b/target/linux/generic/pending-5.15/732-09-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch
+@@ -0,0 +1,192 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Fri, 28 Oct 2022 11:01:12 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: fix VLAN rx hardware
++ acceleration
 +
-+[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
-+[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
++- enable VLAN untagging for PDMA rx
++- make it possible to disable the feature via ethtool
++- pass VLAN tag to the DSA driver
++- untag special tag on PDMA only if no non-DSA devices are in use
++- disable special tag untagging on 7986 for now, since it's not working yet
 +
-+Signed-off-by: Marek Behún <kabel@kernel.org>
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------
-+ 1 file changed, 7 insertions(+), 18 deletions(-)
 +
-+--- a/drivers/pci/controller/pci-aardvark.c
-++++ b/drivers/pci/controller/pci-aardvark.c
-+@@ -274,7 +274,6 @@ struct advk_pcie {
-+ 	u8 wins_count;
-+ 	struct irq_domain *rp_irq_domain;
-+ 	struct irq_domain *irq_domain;
-+-	struct irq_chip irq_chip;
-+ 	raw_spinlock_t irq_lock;
-+ 	struct irq_domain *msi_domain;
-+ 	struct irq_domain *msi_inner_domain;
-+@@ -1330,14 +1329,19 @@ static void advk_pcie_irq_unmask(struct
-+ 	raw_spin_unlock_irqrestore(&pcie->irq_lock, flags);
-+ }
-+ 
-++static struct irq_chip advk_irq_chip = {
-++	.name		= "advk-INT",
-++	.irq_mask	= advk_pcie_irq_mask,
-++	.irq_unmask	= advk_pcie_irq_unmask,
-++};
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -23,6 +23,7 @@
++ #include <linux/jhash.h>
++ #include <linux/bitfield.h>
++ #include <net/dsa.h>
+++#include <net/dst_metadata.h>
++ 
++ #include "mtk_eth_soc.h"
++ #include "mtk_wed.h"
++@@ -1967,16 +1968,22 @@ static int mtk_poll_rx(struct napi_struc
++ 						htons(RX_DMA_VPID(trxd.rxd4)),
++ 						RX_DMA_VID(trxd.rxd4));
++ 			} else if (trxd.rxd2 & RX_DMA_VTAG) {
++-				__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+++				__vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
++ 						       RX_DMA_VID(trxd.rxd3));
++ 			}
+++		}
 ++
-+ static int advk_pcie_irq_map(struct irq_domain *h,
-+ 			     unsigned int virq, irq_hw_number_t hwirq)
+++		/* When using VLAN untagging in combination with DSA, the
+++		 * hardware treats the MTK special tag as a VLAN and untags it.
+++		 */
+++		if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
+++			unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
++ 
++-			/* If the device is attached to a dsa switch, the special
++-			 * tag inserted in VLAN field by hw switch can * be offloaded
++-			 * by RX HW VLAN offload. Clear vlan info.
++-			 */
++-			if (netdev_uses_dsa(netdev))
++-				__vlan_hwaccel_clear_tag(skb);
+++			if (port < ARRAY_SIZE(eth->dsa_meta) &&
+++			    eth->dsa_meta[port])
+++				skb_dst_set_noref(skb, &eth->dsa_meta[port]->dst);
+++
+++			__vlan_hwaccel_clear_tag(skb);
++ 		}
++ 
++ 		skb_record_rx_queue(skb, 0);
++@@ -2793,15 +2800,30 @@ static netdev_features_t mtk_fix_feature
++ 
++ static int mtk_set_features(struct net_device *dev, netdev_features_t features)
 + {
-+ 	struct advk_pcie *pcie = h->host_data;
++-	int err = 0;
+++	struct mtk_mac *mac = netdev_priv(dev);
+++	struct mtk_eth *eth = mac->hw;
+++	netdev_features_t diff = dev->features ^ features;
+++	int i;
+++
+++	if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
+++		mtk_hwlro_netdev_disable(dev);
 + 
-+ 	irq_set_status_flags(virq, IRQ_LEVEL);
-+-	irq_set_chip_and_handler(virq, &pcie->irq_chip,
-+-				 handle_level_irq);
-++	irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq);
-+ 	irq_set_chip_data(virq, pcie);
++-	if (!((dev->features ^ features) & NETIF_F_LRO))
+++	/* Set RX VLAN offloading */
+++	if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
++ 		return 0;
 + 
-+ 	return 0;
-+@@ -1396,7 +1400,6 @@ static int advk_pcie_init_irq_domain(str
-+ 	struct device *dev = &pcie->pdev->dev;
-+ 	struct device_node *node = dev->of_node;
-+ 	struct device_node *pcie_intc_node;
-+-	struct irq_chip *irq_chip;
-+ 	int ret = 0;
++-	if (!(features & NETIF_F_LRO))
++-		mtk_hwlro_netdev_disable(dev);
+++	mtk_w32(eth, !!(features & NETIF_F_HW_VLAN_CTAG_RX),
+++		MTK_CDMP_EG_CTRL);
 + 
-+ 	raw_spin_lock_init(&pcie->irq_lock);
-+@@ -1407,28 +1410,14 @@ static int advk_pcie_init_irq_domain(str
-+ 		return -ENODEV;
-+ 	}
++-	return err;
+++	/* sync features with other MAC */
+++	for (i = 0; i < MTK_MAC_COUNT; i++) {
+++		if (!eth->netdev[i] || eth->netdev[i] == dev)
+++			continue;
+++		eth->netdev[i]->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
+++		eth->netdev[i]->features |= features & NETIF_F_HW_VLAN_CTAG_RX;
+++	}
+++
+++	return 0;
++ }
 + 
-+-	irq_chip = &pcie->irq_chip;
-+-
-+-	irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
-+-					dev_name(dev));
-+-	if (!irq_chip->name) {
-+-		ret = -ENOMEM;
-+-		goto out_put_node;
-+-	}
-+-
-+-	irq_chip->irq_mask = advk_pcie_irq_mask;
-+-	irq_chip->irq_unmask = advk_pcie_irq_unmask;
-+-
-+ 	pcie->irq_domain =
-+ 		irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
-+ 				      &advk_pcie_irq_domain_ops, pcie);
-+ 	if (!pcie->irq_domain) {
-+ 		dev_err(dev, "Failed to get a INTx IRQ domain\n");
-+ 		ret = -ENOMEM;
-+-		goto out_put_node;
-+ 	}
-+ 
-+-out_put_node:
-+ 	of_node_put(pcie_intc_node);
-+ 	return ret;
++ /* wait for DMA to finish whatever it is doing before we start using it again */
++@@ -3083,11 +3105,45 @@ found:
++ 	return NOTIFY_DONE;
 + }
-diff --git a/target/linux/generic/pending-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch b/target/linux/generic/pending-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
-new file mode 100644
-index 0000000000..4a963be952
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
-@@ -0,0 +1,217 @@
-+From a719f7ba7fcba05d85801c6f0267f389a21627c1 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Fri, 24 Sep 2021 13:03:02 +0200
-+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove port from driver
-+ configuration
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+Port number is encoded into argument for SMC call. It is zero for SATA,
-+PCIe and also both USB 3.0 PHYs. It is non-zero only for Ethernet PHY
-+(incorrectly called SGMII) on lane 0. Ethernet PHY on lane 1 also uses zero
-+port number.
-+
-+So construct "port" bits for SMC call argument can be constructed directly
-+from PHY type and lane number.
-+
-+Change driver code to always pass zero port number for non-ethernet PHYs
-+and for ethernet PHYs determinate port number from lane number. This
-+simplifies the driver.
-+
-+As port number from DT PHY configuration is not used anymore, remove whole
-+driver code which parses it. This also simplifies the driver.
-+
-+Signed-off-by: Pali Rohár <pali@kernel.org>
-+Signed-off-by: Marek Behún <kabel@kernel.org>
-+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-+---
-+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 62 +++++++++-----------
-+ 1 file changed, 29 insertions(+), 33 deletions(-)
-+
-+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
-++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
-+@@ -20,7 +20,6 @@
-+ #include <linux/platform_device.h>
-+ 
-+ #define MVEBU_A3700_COMPHY_LANES		3
-+-#define MVEBU_A3700_COMPHY_PORTS		2
-+ 
-+ /* COMPHY Fast SMC function identifiers */
-+ #define COMPHY_SIP_POWER_ON			0x82000001
-+@@ -45,51 +44,47 @@
-+ #define COMPHY_FW_NET(mode, idx, speed)		(COMPHY_FW_MODE(mode) | \
-+ 						 ((idx) << 8) |	\
-+ 						 ((speed) << 2))
-+-#define COMPHY_FW_PCIE(mode, idx, speed, width)	(COMPHY_FW_NET(mode, idx, speed) | \
-++#define COMPHY_FW_PCIE(mode, speed, width)	(COMPHY_FW_NET(mode, 0, speed) | \
-+ 						 ((width) << 18))
-+ 
-+ struct mvebu_a3700_comphy_conf {
-+ 	unsigned int lane;
-+ 	enum phy_mode mode;
-+ 	int submode;
-+-	unsigned int port;
-+ 	u32 fw_mode;
-+ };
-+ 
-+-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw)	\
-++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw)		\
-+ 	{								\
-+ 		.lane = _lane,						\
-+ 		.mode = _mode,						\
-+ 		.submode = _smode,					\
-+-		.port = _port,						\
-+ 		.fw_mode = _fw,						\
-+ 	}
 + 
-+-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
-+-	MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
-++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
-++	MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
-+ 
-+-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
-+-	MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
-++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
-++	MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
-+ 
-+ static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
-+ 	/* lane 0 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
-++	MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
-+ 				    COMPHY_FW_MODE_USB3H),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
-++	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
-+ 				    COMPHY_FW_MODE_SGMII),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
-++	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
-+ 				    COMPHY_FW_MODE_2500BASEX),
-+ 	/* lane 1 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
-+-				    COMPHY_FW_MODE_PCIE),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
-++	MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
-++	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
-+ 				    COMPHY_FW_MODE_SGMII),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
-++	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
-+ 				    COMPHY_FW_MODE_2500BASEX),
-+ 	/* lane 2 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
-+-				    COMPHY_FW_MODE_SATA),
-+-	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
-++	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
-++	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
-+ 				    COMPHY_FW_MODE_USB3H),
-+ };
+++static bool mtk_uses_dsa(struct net_device *dev)
+++{
+++#if IS_ENABLED(CONFIG_NET_DSA)
+++	return netdev_uses_dsa(dev) &&
+++	       dev->dsa_ptr->tag_ops->proto == DSA_TAG_PROTO_MTK;
+++#else
+++	return false;
+++#endif
+++}
+++
++ static int mtk_open(struct net_device *dev)
++ {
++ 	struct mtk_mac *mac = netdev_priv(dev);
++ 	struct mtk_eth *eth = mac->hw;
++-	int err;
+++	int i, err;
+++
+++	if (mtk_uses_dsa(dev) && !eth->prog) {
+++		for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+++			struct metadata_dst *md_dst = eth->dsa_meta[i];
+++
+++			if (md_dst)
+++				continue;
+++
+++			md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
+++						    GFP_KERNEL);
+++			if (!md_dst)
+++				return -ENOMEM;
+++
+++			md_dst->u.port_info.port_id = i;
+++			eth->dsa_meta[i] = md_dst;
+++		}
+++	} else {
+++		/* Hardware special tag parsing needs to be disabled if at least
+++		 * one MAC does not use DSA.
+++		 */
+++		u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+++		val &= ~MTK_CDMP_STAG_EN;
+++		mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
+++	}
 + 
-+@@ -98,7 +93,6 @@ struct mvebu_a3700_comphy_lane {
-+ 	unsigned int id;
-+ 	enum phy_mode mode;
-+ 	int submode;
-+-	int port;
-+ };
++ 	err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
++ 	if (err) {
++@@ -3417,6 +3473,10 @@ static int mtk_hw_init(struct mtk_eth *e
++ 	 */
++ 	val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
++ 	mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
+++	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+++		val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+++		mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
+++	}
 + 
-+ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
-+@@ -120,7 +114,7 @@ static int mvebu_a3700_comphy_smc(unsign
++ 	/* Enable RX VLan Offloading */
++ 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
++@@ -3634,6 +3694,12 @@ static int mtk_free_dev(struct mtk_eth *
++ 		free_netdev(eth->netdev[i]);
 + 	}
++ 
+++	for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+++		if (!eth->dsa_meta[i])
+++			break;
+++		metadata_dst_free(eth->dsa_meta[i]);
+++	}
+++
++ 	return 0;
 + }
 + 
-+-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
-++static int mvebu_a3700_comphy_get_fw_mode(int lane,
-+ 					  enum phy_mode mode,
-+ 					  int submode)
-+ {
-+@@ -132,7 +126,6 @@ static int mvebu_a3700_comphy_get_fw_mod
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -22,6 +22,9 @@
++ #include <linux/bpf_trace.h>
++ #include "mtk_ppe.h"
 + 
-+ 	for (i = 0; i < n; i++) {
-+ 		if (mvebu_a3700_comphy_modes[i].lane == lane &&
-+-		    mvebu_a3700_comphy_modes[i].port == port &&
-+ 		    mvebu_a3700_comphy_modes[i].mode == mode &&
-+ 		    mvebu_a3700_comphy_modes[i].submode == submode)
-+ 			break;
-+@@ -153,7 +146,7 @@ static int mvebu_a3700_comphy_set_mode(s
-+ 	if (submode == PHY_INTERFACE_MODE_1000BASEX)
-+ 		submode = PHY_INTERFACE_MODE_SGMII;
-+ 
-+-	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
-++	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode,
-+ 						 submode);
-+ 	if (fw_mode < 0) {
-+ 		dev_err(lane->dev, "invalid COMPHY mode\n");
-+@@ -172,9 +165,10 @@ static int mvebu_a3700_comphy_power_on(s
-+ 	struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
-+ 	u32 fw_param;
-+ 	int fw_mode;
-++	int fw_port;
-+ 	int ret;
+++#define MTK_MAX_DSA_PORTS	7
+++#define MTK_DSA_PORT_MASK	GENMASK(2, 0)
+++
++ #define MTK_QDMA_NUM_QUEUES	16
++ #define MTK_QDMA_PAGE_SIZE	2048
++ #define MTK_MAX_RX_LENGTH	1536
++@@ -93,6 +96,9 @@
++ #define MTK_CDMQ_IG_CTRL	0x1400
++ #define MTK_CDMQ_STAG_EN	BIT(0)
 + 
-+-	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
-++	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id,
-+ 						 lane->mode, lane->submode);
-+ 	if (fw_mode < 0) {
-+ 		dev_err(lane->dev, "invalid COMPHY mode\n");
-+@@ -191,17 +185,18 @@ static int mvebu_a3700_comphy_power_on(s
-+ 		fw_param = COMPHY_FW_MODE(fw_mode);
-+ 		break;
-+ 	case PHY_MODE_ETHERNET:
-++		fw_port = (lane->id == 0) ? 1 : 0;
-+ 		switch (lane->submode) {
-+ 		case PHY_INTERFACE_MODE_SGMII:
-+ 			dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
-+ 				lane->id);
-+-			fw_param = COMPHY_FW_NET(fw_mode, lane->port,
-++			fw_param = COMPHY_FW_NET(fw_mode, fw_port,
-+ 						 COMPHY_FW_SPEED_1_25G);
-+ 			break;
-+ 		case PHY_INTERFACE_MODE_2500BASEX:
-+ 			dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
-+ 				lane->id);
-+-			fw_param = COMPHY_FW_NET(fw_mode, lane->port,
-++			fw_param = COMPHY_FW_NET(fw_mode, fw_port,
-+ 						 COMPHY_FW_SPEED_3_125G);
-+ 			break;
-+ 		default:
-+@@ -212,8 +207,7 @@ static int mvebu_a3700_comphy_power_on(s
-+ 		break;
-+ 	case PHY_MODE_PCIE:
-+ 		dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
-+-		fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
-+-					  COMPHY_FW_SPEED_5G,
-++		fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G,
-+ 					  phy->attrs.bus_width);
-+ 		break;
-+ 	default:
-+@@ -247,17 +241,20 @@ static struct phy *mvebu_a3700_comphy_xl
-+ 					    struct of_phandle_args *args)
-+ {
-+ 	struct mvebu_a3700_comphy_lane *lane;
-++	unsigned int port;
-+ 	struct phy *phy;
+++/* CDMQ Exgress Control Register */
+++#define MTK_CDMQ_EG_CTRL	0x1404
+++
++ /* CDMP Ingress Control Register */
++ #define MTK_CDMP_IG_CTRL	0x400
++ #define MTK_CDMP_STAG_EN	BIT(0)
++@@ -1140,6 +1146,8 @@ struct mtk_eth {
 + 
-+-	if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
-+-		return ERR_PTR(-EINVAL);
-+-
-+ 	phy = of_phy_simple_xlate(dev, args);
-+ 	if (IS_ERR(phy))
-+ 		return phy;
++ 	int				ip_align;
 + 
-+ 	lane = phy_get_drvdata(phy);
-+-	lane->port = args->args[0];
+++	struct metadata_dst		*dsa_meta[MTK_MAX_DSA_PORTS];
 ++
-++	port = args->args[0];
-++	if (port != 0 && (port != 1 || lane->id != 0)) {
-++		dev_err(lane->dev, "invalid port number %u\n", port);
-++		return ERR_PTR(-EINVAL);
-++	}
-+ 
-+ 	return phy;
-+ }
-+@@ -302,7 +299,6 @@ static int mvebu_a3700_comphy_probe(stru
-+ 		lane->mode = PHY_MODE_INVALID;
-+ 		lane->submode = PHY_INTERFACE_MODE_NA;
-+ 		lane->id = lane_id;
-+-		lane->port = -1;
-+ 		phy_set_drvdata(phy, lane);
-+ 	}
++ 	struct mtk_ppe			*ppe[2];
++ 	struct rhashtable		flow_table;
 + 
-diff --git a/target/linux/generic/pending-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch b/target/linux/generic/pending-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
+diff --git a/target/linux/generic/pending-5.15/732-10-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-5.15/732-10-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
 new file mode 100644
-index 0000000000..73ead1e16c
+index 0000000000..ba86686eeb
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
-@@ -0,0 +1,1564 @@
-+From 9d276da259cce20b2ed7a868b6e6a6a205f7bb04 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Thu, 23 Sep 2021 19:20:13 +0200
-+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Add native kernel
-+ implementation
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
++++ b/target/linux/generic/pending-5.15/732-10-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
+@@ -0,0 +1,78 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 3 Nov 2022 12:38:49 +0100
++Subject: [PATCH] net: ethernet: mtk_eth_soc: work around issue with
++ sending small fragments
 +
-+Remove old RPC implementation and add a new native kernel implementation.
-+
-+The old implementation uses ARM SMC API to issue RPC calls to ARM Trusted
-+Firmware which provides real implementation of PHY configuration.
-+
-+But older versions of ARM Trusted Firmware do not provide this PHY
-+configuration functionality, simply returning: operation not supported; or
-+worse, some versions provide the configuration functionality incorrectly.
-+
-+For example the firmware shipped in ESPRESSObin board has this older
-+version of ARM Trusted Firmware and therefore SATA, USB 3.0 and PCIe
-+functionality do not work with newer versions of Linux kernel.
-+
-+Due to the above reasons, the following commits were introduced into Linux,
-+to workaround these issues by ignoring -EOPNOTSUPP error code from
-+phy-mvebu-a3700-comphy driver function phy_power_on():
-+
-+commit 45aefe3d2251 ("ata: ahci: mvebu: Make SATA PHY optional for Armada
-+3720")
-+commit 3241929b67d2 ("usb: host: xhci: mvebu: make USB 3.0 PHY optional for
-+Armada 3720")
-+commit b0c6ae0f8948 ("PCI: aardvark: Fix initialization with old Marvell's
-+Arm Trusted Firmware")
-+
-+Replace this RPC implementation with proper native kernel implementation,
-+which is independent on the firmware. Never return -EOPNOTSUPP for proper
-+arguments.
-+
-+This should solve multiple issues with real-world boards, where it is not
-+possible or really inconvenient to change the firmware. Let's eliminate
-+these issues.
-+
-+This implementation is ported directly from Armada 3720 comphy driver found
-+in newest version of ARM Trusted Firmware source code, but with various
-+fixes of register names, some added comments, some refactoring due to the
-+original code not conforming to kernel standards. Also PCIe mode poweroff
-+support was added here, and PHY reset support. These changes are also going
-+to be sent to ARM Trusted Firmware.
-+
-+Signed-off-by: Pali Rohár <pali@kernel.org>
-+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-+[ Pali did the porting from ATF.
-+  I (Marek) then fixed some register names, some various other things,
-+  added some comments and refactored the code to kernel standards. Also
-+  fixed PHY poweroff and added PHY reset. ]
-+Signed-off-by: Marek Behún <kabel@kernel.org>
++When frames are sent with very small fragments, the DMA engine appears to
++lock up and transmit attempts time out. Fix this by detecting the presence
++of small fragments and use skb_gso_segment + skb_linearize to deal with
++them
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
 +---
-+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1351 ++++++++++++++++--
-+ 1 file changed, 1234 insertions(+), 117 deletions(-)
-+
-+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
-++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
-+@@ -5,12 +5,16 @@
-+  * Authors:
-+  *   Evan Wang <xswang@marvell.com>
-+  *   Miquèl Raynal <miquel.raynal@bootlin.com>
-++ *   Pali Rohár <pali@kernel.org>
-++ *   Marek Behún <kabel@kernel.org>
-+  *
-+  * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
-+- * SMC call initial support done by Grzegorz Jaszczyk.
-++ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org>
-++ * and Marek Behún <kabel@kernel.org>.
-+  */
-+ 
-+-#include <linux/arm-smccc.h>
-++#include <linux/bitfield.h>
-++#include <linux/clk.h>
-+ #include <linux/io.h>
-+ #include <linux/iopoll.h>
-+ #include <linux/mfd/syscon.h>
-+@@ -18,103 +22,1147 @@
-+ #include <linux/phy.h>
-+ #include <linux/phy/phy.h>
-+ #include <linux/platform_device.h>
-++#include <linux/spinlock.h>
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -1396,12 +1396,28 @@ static void mtk_wake_queue(struct mtk_et
++ 	}
++ }
 + 
-+-#define MVEBU_A3700_COMPHY_LANES		3
-++#define PLL_SET_DELAY_US		600
-++#define COMPHY_PLL_SLEEP		1000
-++#define COMPHY_PLL_TIMEOUT		150000
+++static bool mtk_skb_has_small_frag(struct sk_buff *skb)
+++{
+++	int min_size = 16;
+++	int i;
 ++
-++/* Comphy lane2 indirect access register offset */
-++#define COMPHY_LANE2_INDIR_ADDR		0x0
-++#define COMPHY_LANE2_INDIR_DATA		0x4
+++	if (skb_headlen(skb) < min_size)
+++		return true;
 ++
-++/* SATA and USB3 PHY offset compared to SATA PHY */
-++#define COMPHY_LANE2_REGS_BASE		0x200
+++	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+++		if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size)
+++			return true;
 ++
-++/*
-++ * When accessing common PHY lane registers directly, we need to shift by 1,
-++ * since the registers are 16-bit.
-++ */
-++#define COMPHY_LANE_REG_DIRECT(reg)	(((reg) & 0x7FF) << 1)
-++
-++/* COMPHY registers */
-++#define COMPHY_POWER_PLL_CTRL		0x01
-++#define PU_IVREF_BIT			BIT(15)
-++#define PU_PLL_BIT			BIT(14)
-++#define PU_RX_BIT			BIT(13)
-++#define PU_TX_BIT			BIT(12)
-++#define PU_TX_INTP_BIT			BIT(11)
-++#define PU_DFE_BIT			BIT(10)
-++#define RESET_DTL_RX_BIT		BIT(9)
-++#define PLL_LOCK_BIT			BIT(8)
-++#define REF_FREF_SEL_MASK		GENMASK(4, 0)
-++#define REF_FREF_SEL_SERDES_25MHZ	FIELD_PREP(REF_FREF_SEL_MASK, 0x1)
-++#define REF_FREF_SEL_SERDES_40MHZ	FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
-++#define REF_FREF_SEL_SERDES_50MHZ	FIELD_PREP(REF_FREF_SEL_MASK, 0x4)
-++#define REF_FREF_SEL_PCIE_USB3_25MHZ	FIELD_PREP(REF_FREF_SEL_MASK, 0x2)
-++#define REF_FREF_SEL_PCIE_USB3_40MHZ	FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
-++#define COMPHY_MODE_MASK		GENMASK(7, 5)
-++#define COMPHY_MODE_SATA		FIELD_PREP(COMPHY_MODE_MASK, 0x0)
-++#define COMPHY_MODE_PCIE		FIELD_PREP(COMPHY_MODE_MASK, 0x3)
-++#define COMPHY_MODE_SERDES		FIELD_PREP(COMPHY_MODE_MASK, 0x4)
-++#define COMPHY_MODE_USB3		FIELD_PREP(COMPHY_MODE_MASK, 0x5)
-++
-++#define COMPHY_KVCO_CAL_CTRL		0x02
-++#define USE_MAX_PLL_RATE_BIT		BIT(12)
-++#define SPEED_PLL_MASK			GENMASK(7, 2)
-++#define SPEED_PLL_VALUE_16		FIELD_PREP(SPEED_PLL_MASK, 0x10)
-++
-++#define COMPHY_DIG_LOOPBACK_EN		0x23
-++#define SEL_DATA_WIDTH_MASK		GENMASK(11, 10)
-++#define DATA_WIDTH_10BIT		FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0)
-++#define DATA_WIDTH_20BIT		FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1)
-++#define DATA_WIDTH_40BIT		FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2)
-++#define PLL_READY_TX_BIT		BIT(4)
-++
-++#define COMPHY_SYNC_PATTERN		0x24
-++#define TXD_INVERT_BIT			BIT(10)
-++#define RXD_INVERT_BIT			BIT(11)
-++
-++#define COMPHY_SYNC_MASK_GEN		0x25
-++#define PHY_GEN_MAX_MASK		GENMASK(11, 10)
-++#define PHY_GEN_MAX_USB3_5G		FIELD_PREP(PHY_GEN_MAX_MASK, 0x1)
-++
-++#define COMPHY_ISOLATION_CTRL		0x26
-++#define PHY_ISOLATE_MODE		BIT(15)
-++
-++#define COMPHY_GEN2_SET2		0x3e
-++#define GS2_TX_SSC_AMP_MASK		GENMASK(15, 9)
-++#define GS2_TX_SSC_AMP_4128		FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20)
-++#define GS2_VREG_RXTX_MAS_ISET_MASK	GENMASK(8, 7)
-++#define GS2_VREG_RXTX_MAS_ISET_60U	FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
-++						   0x0)
-++#define GS2_VREG_RXTX_MAS_ISET_80U	FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
-++						   0x1)
-++#define GS2_VREG_RXTX_MAS_ISET_100U	FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
-++						   0x2)
-++#define GS2_VREG_RXTX_MAS_ISET_120U	FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
-++						   0x3)
-++#define GS2_RSVD_6_0_MASK		GENMASK(6, 0)
-++
-++#define COMPHY_GEN3_SET2		0x3f
-++
-++#define COMPHY_IDLE_SYNC_EN		0x48
-++#define IDLE_SYNC_EN			BIT(12)
-++
-++#define COMPHY_MISC_CTRL0		0x4F
-++#define CLK100M_125M_EN			BIT(4)
-++#define TXDCLK_2X_SEL			BIT(6)
-++#define CLK500M_EN			BIT(7)
-++#define PHY_REF_CLK_SEL			BIT(10)
-++
-++#define COMPHY_SFT_RESET		0x52
-++#define SFT_RST				BIT(9)
-++#define SFT_RST_NO_REG			BIT(10)
-++
-++#define COMPHY_MISC_CTRL1		0x73
-++#define SEL_BITS_PCIE_FORCE		BIT(15)
-++
-++#define COMPHY_GEN2_SET3		0x112
-++#define GS3_FFE_CAP_SEL_MASK		GENMASK(3, 0)
-++#define GS3_FFE_CAP_SEL_VALUE		FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF)
-++
-++/* PIPE registers */
-++#define COMPHY_PIPE_LANE_CFG0		0x180
-++#define PRD_TXDEEMPH0_MASK		BIT(0)
-++#define PRD_TXMARGIN_MASK		GENMASK(3, 1)
-++#define PRD_TXSWING_MASK		BIT(4)
-++#define CFG_TX_ALIGN_POS_MASK		GENMASK(8, 5)
-++
-++#define COMPHY_PIPE_LANE_CFG1		0x181
-++#define PRD_TXDEEMPH1_MASK		BIT(15)
-++#define USE_MAX_PLL_RATE_EN		BIT(9)
-++#define TX_DET_RX_MODE			BIT(6)
-++#define GEN2_TX_DATA_DLY_MASK		GENMASK(4, 3)
-++#define GEN2_TX_DATA_DLY_DEFT		FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2)
-++#define TX_ELEC_IDLE_MODE_EN		BIT(0)
-++
-++#define COMPHY_PIPE_LANE_STAT1		0x183
-++#define TXDCLK_PCLK_EN			BIT(0)
-++
-++#define COMPHY_PIPE_LANE_CFG4		0x188
-++#define SPREAD_SPECTRUM_CLK_EN		BIT(7)
-++
-++#define COMPHY_PIPE_RST_CLK_CTRL	0x1C1
-++#define PIPE_SOFT_RESET			BIT(0)
-++#define PIPE_REG_RESET			BIT(1)
-++#define MODE_CORE_CLK_FREQ_SEL		BIT(9)
-++#define MODE_PIPE_WIDTH_32		BIT(3)
-++#define MODE_REFDIV_MASK		GENMASK(5, 4)
-++#define MODE_REFDIV_BY_4		FIELD_PREP(MODE_REFDIV_MASK, 0x2)
-++
-++#define COMPHY_PIPE_TEST_MODE_CTRL	0x1C2
-++#define MODE_MARGIN_OVERRIDE		BIT(2)
-++
-++#define COMPHY_PIPE_CLK_SRC_LO		0x1C3
-++#define MODE_CLK_SRC			BIT(0)
-++#define BUNDLE_PERIOD_SEL		BIT(1)
-++#define BUNDLE_PERIOD_SCALE_MASK	GENMASK(3, 2)
-++#define BUNDLE_SAMPLE_CTRL		BIT(4)
-++#define PLL_READY_DLY_MASK		GENMASK(7, 5)
-++#define CFG_SEL_20B			BIT(15)
-++
-++#define COMPHY_PIPE_PWR_MGM_TIM1	0x1D0
-++#define CFG_PM_OSCCLK_WAIT_MASK		GENMASK(15, 12)
-++#define CFG_PM_RXDEN_WAIT_MASK		GENMASK(11, 8)
-++#define CFG_PM_RXDEN_WAIT_1_UNIT	FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1)
-++#define CFG_PM_RXDLOZ_WAIT_MASK		GENMASK(7, 0)
-++#define CFG_PM_RXDLOZ_WAIT_7_UNIT	FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7)
-++#define CFG_PM_RXDLOZ_WAIT_12_UNIT	FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC)
+++	return false;
+++}
 ++
-++/*
-++ * This register is not from PHY lane register space. It only exists in the
-++ * indirect register space, before the actual PHY lane 2 registers. So the
-++ * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE.
-++ * It is used only for SATA PHY initialization.
-++ */
-++#define COMPHY_RESERVED_REG		0x0E
-++#define PHYCTRL_FRM_PIN_BIT		BIT(13)
++ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
++ {
++ 	struct mtk_mac *mac = netdev_priv(dev);
++ 	struct mtk_eth *eth = mac->hw;
++ 	struct mtk_tx_ring *ring = &eth->tx_ring;
++ 	struct net_device_stats *stats = &dev->stats;
+++	struct sk_buff *segs, *next;
++ 	bool gso = false;
++ 	int tx_num;
++ 
++@@ -1423,6 +1439,17 @@ static netdev_tx_t mtk_start_xmit(struct
++ 		return NETDEV_TX_BUSY;
++ 	}
 + 
-+-/* COMPHY Fast SMC function identifiers */
-+-#define COMPHY_SIP_POWER_ON			0x82000001
-+-#define COMPHY_SIP_POWER_OFF			0x82000002
-+-#define COMPHY_SIP_PLL_LOCK			0x82000003
-+-
-+-#define COMPHY_FW_MODE_SATA			0x1
-+-#define COMPHY_FW_MODE_SGMII			0x2
-+-#define COMPHY_FW_MODE_2500BASEX		0x3
-+-#define COMPHY_FW_MODE_USB3H			0x4
-+-#define COMPHY_FW_MODE_USB3D			0x5
-+-#define COMPHY_FW_MODE_PCIE			0x6
-+-#define COMPHY_FW_MODE_USB3			0xa
-+-
-+-#define COMPHY_FW_SPEED_1_25G			0 /* SGMII 1G */
-+-#define COMPHY_FW_SPEED_2_5G			1
-+-#define COMPHY_FW_SPEED_3_125G			2 /* 2500BASE-X */
-+-#define COMPHY_FW_SPEED_5G			3
-+-#define COMPHY_FW_SPEED_MAX			0x3F
-+-
-+-#define COMPHY_FW_MODE(mode)			((mode) << 12)
-+-#define COMPHY_FW_NET(mode, idx, speed)		(COMPHY_FW_MODE(mode) | \
-+-						 ((idx) << 8) |	\
-+-						 ((speed) << 2))
-+-#define COMPHY_FW_PCIE(mode, speed, width)	(COMPHY_FW_NET(mode, 0, speed) | \
-+-						 ((width) << 18))
-++/* South Bridge PHY Configuration Registers */
-++#define COMPHY_PHY_REG(lane, reg)	(((1 - (lane)) * 0x28) + ((reg) & 0x3f))
+++	if (skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) {
+++		segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO);
+++		if (IS_ERR(segs))
+++			goto drop;
 ++
-++/*
-++ * lane0: USB3/GbE1 PHY Configuration 1
-++ * lane1: PCIe/GbE0 PHY Configuration 1
-++ * (used only by SGMII code)
-++ */
-++#define COMPHY_PHY_CFG1			0x0
-++#define PIN_PU_IVREF_BIT		BIT(1)
-++#define PIN_RESET_CORE_BIT		BIT(11)
-++#define PIN_RESET_COMPHY_BIT		BIT(12)
-++#define PIN_PU_PLL_BIT			BIT(16)
-++#define PIN_PU_RX_BIT			BIT(17)
-++#define PIN_PU_TX_BIT			BIT(18)
-++#define PIN_TX_IDLE_BIT			BIT(19)
-++#define GEN_RX_SEL_MASK			GENMASK(25, 22)
-++#define GEN_RX_SEL_VALUE(val)		FIELD_PREP(GEN_RX_SEL_MASK, (val))
-++#define GEN_TX_SEL_MASK			GENMASK(29, 26)
-++#define GEN_TX_SEL_VALUE(val)		FIELD_PREP(GEN_TX_SEL_MASK, (val))
-++#define SERDES_SPEED_1_25_G		0x6
-++#define SERDES_SPEED_3_125_G		0x8
-++#define PHY_RX_INIT_BIT			BIT(30)
+++		if (segs) {
+++			consume_skb(skb);
+++			skb = segs;
+++		}
+++	}
 ++
-++/*
-++ * lane0: USB3/GbE1 PHY Status 1
-++ * lane1: PCIe/GbE0 PHY Status 1
-++ * (used only by SGMII code)
-++ */
-++#define COMPHY_PHY_STAT1		0x18
-++#define PHY_RX_INIT_DONE_BIT		BIT(0)
-++#define PHY_PLL_READY_RX_BIT		BIT(2)
-++#define PHY_PLL_READY_TX_BIT		BIT(3)
-++
-++/* PHY Selector */
-++#define COMPHY_SELECTOR_PHY_REG			0xFC
-++/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */
-++#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT	BIT(0)
-++/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */
-++#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT	BIT(4)
-++/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */
-++#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT	BIT(8)
-+ 
-+ struct mvebu_a3700_comphy_conf {
-+ 	unsigned int lane;
-+ 	enum phy_mode mode;
-+ 	int submode;
-+-	u32 fw_mode;
-+ };
-+ 
-+-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw)		\
-++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode)			\
-+ 	{								\
-+ 		.lane = _lane,						\
-+ 		.mode = _mode,						\
-+ 		.submode = _smode,					\
-+-		.fw_mode = _fw,						\
++ 	/* TSO: fill MSS info in tcp checksum field */
++ 	if (skb_is_gso(skb)) {
++ 		if (skb_cow_head(skb, 0)) {
++@@ -1438,8 +1465,13 @@ static netdev_tx_t mtk_start_xmit(struct
++ 		}
 + 	}
 + 
-+-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
-+-	MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
-++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \
-++	MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA)
-+ 
-+-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
-+-	MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
-++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \
-++	MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode)
-+ 
-+ static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
-+ 	/* lane 0 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
-+-				    COMPHY_FW_MODE_USB3H),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
-+-				    COMPHY_FW_MODE_SGMII),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
-+-				    COMPHY_FW_MODE_2500BASEX),
-++	MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS),
-++	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII),
-++	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX),
-++	MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX),
-+ 	/* lane 1 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
-+-				    COMPHY_FW_MODE_SGMII),
-+-	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
-+-				    COMPHY_FW_MODE_2500BASEX),
-++	MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE),
-++	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII),
-++	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX),
-++	MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX),
-+ 	/* lane 2 */
-+-	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
-+-	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
-+-				    COMPHY_FW_MODE_USB3H),
-++	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA),
-++	MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS),
-++};
-++
-++struct mvebu_a3700_comphy_priv {
-++	void __iomem *comphy_regs;
-++	void __iomem *lane0_phy_regs; /* USB3 and GbE1 */
-++	void __iomem *lane1_phy_regs; /* PCIe and GbE0 */
-++	void __iomem *lane2_phy_indirect; /* SATA and USB3 */
-++	spinlock_t lock; /* for PHY selector access */
-++	bool xtal_is_40m;
-+ };
++-	if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
++-		goto drop;
+++	skb_list_walk_safe(skb, skb, next) {
+++		if ((mtk_skb_has_small_frag(skb) && skb_linearize(skb)) ||
+++		    mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) {
+++			stats->tx_dropped++;
+++			dev_kfree_skb_any(skb);
+++		}
+++	}
 + 
-+ struct mvebu_a3700_comphy_lane {
-++	struct mvebu_a3700_comphy_priv *priv;
-+ 	struct device *dev;
-+ 	unsigned int id;
-+ 	enum phy_mode mode;
-+ 	int submode;
-++	bool invert_tx;
-++	bool invert_rx;
-++	bool needs_reset;
-++};
-++
-++struct gbe_phy_init_data_fix {
-++	u16 addr;
-++	u16 value;
-++};
-++
-++/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */
-++static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = {
-++	{ 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 },
-++	{ 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 },
-++	{ 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 },
-++	{ 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC },
-++	{ 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 },
-++	{ 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 },
-++	{ 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 },
-++	{ 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 },
-++	{ 0x104, 0x0C10 }
-+ };
++ 	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
++ 		netif_tx_stop_all_queues(dev);
+diff --git a/target/linux/generic/pending-5.15/732-11-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch b/target/linux/generic/pending-5.15/732-11-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch
+new file mode 100644
+index 0000000000..f89ad6adb1
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-11-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch
+@@ -0,0 +1,21 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Fri, 28 Oct 2022 12:54:48 +0200
++Subject: [PATCH] net: ethernet: mtk_eth_soc: set NETIF_F_ALL_TSO
++
++Significantly improves performance by avoiding unnecessary segmentation
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -49,8 +49,7 @@
++ 				 NETIF_F_RXCSUM | \
++ 				 NETIF_F_HW_VLAN_CTAG_TX | \
++ 				 NETIF_F_HW_VLAN_CTAG_RX | \
++-				 NETIF_F_SG | NETIF_F_TSO | \
++-				 NETIF_F_TSO6 | \
+++				 NETIF_F_SG | NETIF_F_ALL_TSO | \
++ 				 NETIF_F_IPV6_CSUM |\
++ 				 NETIF_F_HW_TC)
++ #define MTK_HW_FEATURES_MT7628	(NETIF_F_SG | NETIF_F_RXCSUM)
+diff --git a/target/linux/generic/pending-5.15/732-12-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch b/target/linux/generic/pending-5.15/732-12-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch
+new file mode 100644
+index 0000000000..9fa384e6fa
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-12-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch
+@@ -0,0 +1,37 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 3 Nov 2022 17:46:25 +0100
++Subject: [PATCH] net: ethernet: mtk_eth_soc: drop packets to WDMA if the
++ ring is full
++
++Improves handling of DMA ring overflow.
++Clarify other WDMA drop related comment.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -3529,9 +3529,12 @@ static int mtk_hw_init(struct mtk_eth *e
++ 	mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
++ 
++ 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++-		/* PSE should not drop port8 and port9 packets */
+++		/* PSE should not drop port8 and port9 packets from WDMA Tx */
++ 		mtk_w32(eth, 0x00000300, PSE_DROP_CFG);
++ 
+++		/* PSE should drop packets to port 8/9 on WDMA Rx ring full */
+++		mtk_w32(eth, 0x00000300, PSE_PPE0_DROP);
+++
++ 		/* PSE Free Queue Flow Control  */
++ 		mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2);
++ 
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -127,6 +127,7 @@
++ #define PSE_FQFC_CFG1		0x100
++ #define PSE_FQFC_CFG2		0x104
++ #define PSE_DROP_CFG		0x108
+++#define PSE_PPE0_DROP		0x110
++ 
++ /* PSE Input Queue Reservation Register*/
++ #define PSE_IQ_REV(x)		(0x140 + (((x) - 1) << 2))
+diff --git a/target/linux/generic/pending-5.15/732-13-net-ethernet-mtk_eth_soc-fix-flow_offload-related-re.patch b/target/linux/generic/pending-5.15/732-13-net-ethernet-mtk_eth_soc-fix-flow_offload-related-re.patch
+new file mode 100644
+index 0000000000..0664e0106f
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-13-net-ethernet-mtk_eth_soc-fix-flow_offload-related-re.patch
+@@ -0,0 +1,52 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Thu, 17 Nov 2022 11:58:21 +0100
++Subject: [PATCH] net: ethernet: mtk_eth_soc: fix flow_offload related refcount
++ bug
++
++Since we call flow_block_cb_decref on FLOW_BLOCK_UNBIND, we need to call
++flow_block_cb_incref unconditionally, even for a newly allocated cb.
++Fixes a use-after-free bug
++
++Fixes: 502e84e2382d ("net: ethernet: mtk_eth_soc: add flow offloading support")
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++@@ -561,6 +561,7 @@ mtk_eth_setup_tc_block(struct net_device
++ 	struct mtk_eth *eth = mac->hw;
++ 	static LIST_HEAD(block_cb_list);
++ 	struct flow_block_cb *block_cb;
+++	bool register_block = false;
++ 	flow_setup_cb_t *cb;
++ 
++ 	if (!eth->soc->offload_version)
++@@ -575,16 +576,20 @@ mtk_eth_setup_tc_block(struct net_device
++ 	switch (f->command) {
++ 	case FLOW_BLOCK_BIND:
++ 		block_cb = flow_block_cb_lookup(f->block, cb, dev);
++-		if (block_cb) {
++-			flow_block_cb_incref(block_cb);
++-			return 0;
+++		if (!block_cb) {
+++			block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
+++			if (IS_ERR(block_cb))
+++				return PTR_ERR(block_cb);
+++
+++			register_block = true;
++ 		}
++-		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
++-		if (IS_ERR(block_cb))
++-			return PTR_ERR(block_cb);
 + 
-+-static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
-+-				  unsigned long mode)
-++/* 40M1G25 mode init data */
-++static u16 gbe_phy_init[512] = {
-++	/* 0       1       2       3       4       5       6       7 */
-++	/*-----------------------------------------------------------*/
-++	/* 8       9       A       B       C       D       E       F */
-++	0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26,	/* 00 */
-++	0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52,	/* 08 */
-++	0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000,	/* 10 */
-++	0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF,	/* 18 */
-++	0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000,	/* 20 */
-++	0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,	/* 28 */
-++	0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/* 30 */
-++	0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100,	/* 38 */
-++	0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00,	/* 40 */
-++	0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A,	/* 48 */
-++	0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001,	/* 50 */
-++	0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF,	/* 58 */
-++	0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000,	/* 60 */
-++	0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002,	/* 68 */
-++	0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780,	/* 70 */
-++	0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000,	/* 78 */
-++	0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000,	/* 80 */
-++	0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210,	/* 88 */
-++	0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F,	/* 90 */
-++	0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651,	/* 98 */
-++	0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000,	/* A0 */
-++	0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/* A8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/* B0 */
-++	0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000,	/* B8 */
-++	0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003,	/* C0 */
-++	0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000,	/* C8 */
-++	0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00,	/* D0 */
-++	0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000,	/* D8 */
-++	0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541,	/* E0 */
-++	0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200,	/* E8 */
-++	0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000,	/* F0 */
-++	0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000,	/* F8 */
-++	0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000,	/*100 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*108 */
-++	0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000,	/*110 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*118 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*120 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*128 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*130 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*138 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*140 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*148 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*150 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*158 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*160 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*168 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*170 */
-++	0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000,	/*178 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*180 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*188 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*190 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*198 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1A0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1A8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1B0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1B8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1C0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1C8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1D0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1D8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1E0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1E8 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/*1F0 */
-++	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000	/*1F8 */
-++};
++-		flow_block_cb_add(block_cb, f);
++-		list_add_tail(&block_cb->driver_list, &block_cb_list);
+++		flow_block_cb_incref(block_cb);
 ++
-++static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask)
+++		if (register_block) {
+++			flow_block_cb_add(block_cb, f);
+++			list_add_tail(&block_cb->driver_list, &block_cb_list);
+++		}
++ 		return 0;
++ 	case FLOW_BLOCK_UNBIND:
++ 		block_cb = flow_block_cb_lookup(f->block, cb, dev);
+diff --git a/target/linux/generic/pending-5.15/732-14-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch b/target/linux/generic/pending-5.15/732-14-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch
+new file mode 100644
+index 0000000000..42f98e6aff
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/732-14-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch
+@@ -0,0 +1,181 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sun, 20 Nov 2022 23:01:00 +0100
++Subject: [PATCH] net: ethernet: mtk_eth_soc: drop generic vlan rx offload,
++ only use DSA untagging
++
++Through testing I found out that hardware vlan rx offload support seems to
++have some hardware issues. At least when using multiple MACs and when receiving
++tagged packets on the secondary MAC, the hardware can sometimes start to emit
++wrong tags on the first MAC as well.
++
++In order to avoid such issues, drop the feature configuration and use the
++offload feature only for DSA hardware untagging on MT7621/MT7622 devices which
++only use one MAC.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++@@ -1993,29 +1993,16 @@ static int mtk_poll_rx(struct napi_struc
++ 		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
++ 			mtk_ppe_check_skb(eth->ppe[0], skb, hash);
++ 
++-		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
++-			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++-				if (trxd.rxd3 & RX_DMA_VTAG_V2)
++-					__vlan_hwaccel_put_tag(skb,
++-						htons(RX_DMA_VPID(trxd.rxd4)),
++-						RX_DMA_VID(trxd.rxd4));
++-			} else if (trxd.rxd2 & RX_DMA_VTAG) {
++-				__vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
++-						       RX_DMA_VID(trxd.rxd3));
++-			}
++-		}
++-
++ 		/* When using VLAN untagging in combination with DSA, the
++ 		 * hardware treats the MTK special tag as a VLAN and untags it.
++ 		 */
++-		if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
++-			unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
+++		if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) &&
+++		    (trxd.rxd2 & RX_DMA_VTAG) && netdev_uses_dsa(netdev)) {
+++			unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0);
++ 
++ 			if (port < ARRAY_SIZE(eth->dsa_meta) &&
++ 			    eth->dsa_meta[port])
++ 				skb_dst_set_noref(skb, &eth->dsa_meta[port]->dst);
++-
++-			__vlan_hwaccel_clear_tag(skb);
++ 		}
++ 
++ 		skb_record_rx_queue(skb, 0);
++@@ -2832,29 +2819,11 @@ static netdev_features_t mtk_fix_feature
++ 
++ static int mtk_set_features(struct net_device *dev, netdev_features_t features)
 + {
-+-	struct arm_smccc_res res;
-+-	s32 ret;
-++	u32 val;
++-	struct mtk_mac *mac = netdev_priv(dev);
++-	struct mtk_eth *eth = mac->hw;
++ 	netdev_features_t diff = dev->features ^ features;
++-	int i;
 + 
-+-	arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
-+-	ret = res.a0;
-++	val = readl(addr);
-++	val = (val & ~mask) | (data & mask);
-++	writel(val, addr);
-++}
++ 	if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
++ 		mtk_hwlro_netdev_disable(dev);
 + 
-+-	switch (ret) {
-+-	case SMCCC_RET_SUCCESS:
++-	/* Set RX VLAN offloading */
++-	if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
 +-		return 0;
-+-	case SMCCC_RET_NOT_SUPPORTED:
-+-		return -EOPNOTSUPP;
-++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
-++{
-++	u16 val;
++-
++-	mtk_w32(eth, !!(features & NETIF_F_HW_VLAN_CTAG_RX),
++-		MTK_CDMP_EG_CTRL);
++-
++-	/* sync features with other MAC */
++-	for (i = 0; i < MTK_MAC_COUNT; i++) {
++-		if (!eth->netdev[i] || eth->netdev[i] == dev)
++-			continue;
++-		eth->netdev[i]->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
++-		eth->netdev[i]->features |= features & NETIF_F_HW_VLAN_CTAG_RX;
++-	}
++-
++ 	return 0;
++ }
++ 
++@@ -3153,30 +3122,6 @@ static int mtk_open(struct net_device *d
++ 	struct mtk_eth *eth = mac->hw;
++ 	int i, err;
++ 
++-	if (mtk_uses_dsa(dev) && !eth->prog) {
++-		for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
++-			struct metadata_dst *md_dst = eth->dsa_meta[i];
++-
++-			if (md_dst)
++-				continue;
++-
++-			md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
++-						    GFP_KERNEL);
++-			if (!md_dst)
++-				return -ENOMEM;
++-
++-			md_dst->u.port_info.port_id = i;
++-			eth->dsa_meta[i] = md_dst;
++-		}
++-	} else {
++-		/* Hardware special tag parsing needs to be disabled if at least
++-		 * one MAC does not use DSA.
++-		 */
++-		u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
++-		val &= ~MTK_CDMP_STAG_EN;
++-		mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
++-	}
++-
++ 	err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
++ 	if (err) {
++ 		netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
++@@ -3215,6 +3160,35 @@ static int mtk_open(struct net_device *d
++ 	phylink_start(mac->phylink);
++ 	netif_tx_start_all_queues(dev);
++ 
+++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+++		return 0;
 ++
-++	val = readw(addr);
-++	val = (val & ~mask) | (data & mask);
-++	writew(val, addr);
-++}
+++	if (mtk_uses_dsa(dev) && !eth->prog) {
+++		for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+++			struct metadata_dst *md_dst = eth->dsa_meta[i];
 ++
-++/* Used for accessing lane 2 registers (SATA/USB3 PHY) */
-++static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv,
-++				u32 offset, u16 data, u16 mask)
-++{
-++	writel(offset,
-++	       priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR);
-++	comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA,
-++		       data, mask);
-++}
+++			if (md_dst)
+++				continue;
 ++
-++static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane,
-++				u16 reg, u16 data, u16 mask)
-++{
-++	if (lane->id == 2) {
-++		/* lane 2 PHY registers are accessed indirectly */
-++		comphy_set_indirect(lane->priv,
-++				    reg + COMPHY_LANE2_REGS_BASE,
-++				    data, mask);
+++			md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
+++						    GFP_KERNEL);
+++			if (!md_dst)
+++				return -ENOMEM;
+++
+++			md_dst->u.port_info.port_id = i;
+++			eth->dsa_meta[i] = md_dst;
+++		}
 ++	} else {
-++		void __iomem *base = lane->id == 1 ?
-++				     lane->priv->lane1_phy_regs :
-++				     lane->priv->lane0_phy_regs;
+++		/* Hardware special tag parsing needs to be disabled if at least
+++		 * one MAC does not use DSA.
+++		 */
+++		u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+++		val &= ~MTK_CDMP_STAG_EN;
+++		mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
+++
+++		mtk_w32(eth, 0, MTK_CDMP_EG_CTRL);
+++	}
 ++
-++		comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg),
-++				 data, mask);
++ 	return 0;
++ }
++ 
++@@ -3508,10 +3482,9 @@ static int mtk_hw_init(struct mtk_eth *e
++ 	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ 		val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
++ 		mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
++-	}
++ 
++-	/* Enable RX VLan Offloading */
++-	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+++		mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
 ++	}
++ 
++ 	/* set interrupt delays based on current Net DIM sample */
++ 	mtk_dim_rx(&eth->rx_dim.work);
++@@ -4132,7 +4105,7 @@ static int mtk_add_mac(struct mtk_eth *e
++ 		eth->netdev[id]->hw_features |= NETIF_F_LRO;
++ 
++ 	eth->netdev[id]->vlan_features = eth->soc->hw_features &
++-		~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
+++		~NETIF_F_HW_VLAN_CTAG_TX;
++ 	eth->netdev[id]->features |= eth->soc->hw_features;
++ 	eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
++ 
++--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++@@ -48,7 +48,6 @@
++ #define MTK_HW_FEATURES		(NETIF_F_IP_CSUM | \
++ 				 NETIF_F_RXCSUM | \
++ 				 NETIF_F_HW_VLAN_CTAG_TX | \
++-				 NETIF_F_HW_VLAN_CTAG_RX | \
++ 				 NETIF_F_SG | NETIF_F_ALL_TSO | \
++ 				 NETIF_F_IPV6_CSUM |\
++ 				 NETIF_F_HW_TC)
+diff --git a/target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch b/target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch
+new file mode 100644
+index 0000000000..c48613929d
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch
+@@ -0,0 +1,591 @@
++From: Sujuan Chen <sujuan.chen@mediatek.com>
++Date: Sat, 5 Nov 2022 23:36:18 +0100
++Subject: [PATCH] net: ethernet: mtk_wed: introduce wed mcu support
++
++Introduce WED mcu support used to configure WED WO chip.
++This is a preliminary patch in order to add RX Wireless
++Ethernet Dispatch available on MT7986 SoC.
++
++Tested-by: Daniel Golle <daniel@makrotopia.org>
++Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: David S. Miller <davem@davemloft.net>
++---
++ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.h
++
++--- a/drivers/net/ethernet/mediatek/Makefile
+++++ b/drivers/net/ethernet/mediatek/Makefile
++@@ -5,7 +5,7 @@
++ 
++ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
++ mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
++-mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o
+++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o
++ ifdef CONFIG_DEBUG_FS
++ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
++ endif
++--- /dev/null
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++@@ -0,0 +1,359 @@
+++// SPDX-License-Identifier: GPL-2.0-only
+++/* Copyright (C) 2022 MediaTek Inc.
+++ *
+++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+++ *	   Sujuan Chen <sujuan.chen@mediatek.com>
+++ */
+++
+++#include <linux/firmware.h>
+++#include <linux/of_address.h>
+++#include <linux/of_reserved_mem.h>
+++#include <linux/mfd/syscon.h>
+++#include <linux/soc/mediatek/mtk_wed.h>
+++
+++#include "mtk_wed_regs.h"
+++#include "mtk_wed_wo.h"
+++#include "mtk_wed.h"
+++
+++static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
+++{
+++	return readl(wo->boot.addr + reg);
 ++}
 ++
-++static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane,
-++				u16 reg, u16 bits,
-++				ulong sleep_us, ulong timeout_us)
+++static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
 ++{
-++	int ret;
+++	writel(val, wo->boot.addr + reg);
+++}
 ++
-++	if (lane->id == 2) {
-++		u32 data;
+++static struct sk_buff *
+++mtk_wed_mcu_msg_alloc(const void *data, int data_len)
+++{
+++	int length = sizeof(struct mtk_wed_mcu_hdr) + data_len;
+++	struct sk_buff *skb;
 ++
-++		/* lane 2 PHY registers are accessed indirectly */
-++		writel(reg + COMPHY_LANE2_REGS_BASE,
-++		       lane->priv->lane2_phy_indirect +
-++		       COMPHY_LANE2_INDIR_ADDR);
+++	skb = alloc_skb(length, GFP_KERNEL);
+++	if (!skb)
+++		return NULL;
 ++
-++		ret = readl_poll_timeout(lane->priv->lane2_phy_indirect +
-++					 COMPHY_LANE2_INDIR_DATA,
-++					 data, (data & bits) == bits,
-++					 sleep_us, timeout_us);
-++	} else {
-++		void __iomem *base = lane->id == 1 ?
-++				     lane->priv->lane1_phy_regs :
-++				     lane->priv->lane0_phy_regs;
-++		u16 data;
-++
-++		ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg),
-++					 data, (data & bits) == bits,
-++					 sleep_us, timeout_us);
-++	}
+++	memset(skb->head, 0, length);
+++	skb_reserve(skb, sizeof(struct mtk_wed_mcu_hdr));
+++	if (data && data_len)
+++		skb_put_data(skb, data, data_len);
 ++
-++	return ret;
+++	return skb;
 ++}
 ++
-++static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane,
-++				  u8 reg, u32 data, u32 mask)
+++static struct sk_buff *
+++mtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires)
 ++{
-++	comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg),
-++		       data, mask);
+++	if (!time_is_after_jiffies(expires))
+++		return NULL;
+++
+++	wait_event_timeout(wo->mcu.wait, !skb_queue_empty(&wo->mcu.res_q),
+++			   expires - jiffies);
+++	return skb_dequeue(&wo->mcu.res_q);
 ++}
 ++
-++static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane,
-++				  u8 reg, u32 bits,
-++				  ulong sleep_us, ulong timeout_us)
+++void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb)
 ++{
-++	u32 data;
-++
-++	return readl_poll_timeout(lane->priv->comphy_regs +
-++				  COMPHY_PHY_REG(lane->id, reg),
-++				  data, (data & bits) == bits,
-++				  sleep_us, timeout_us);
+++	skb_queue_tail(&wo->mcu.res_q, skb);
+++	wake_up(&wo->mcu.wait);
 ++}
 ++
-++/* PHY selector configures with corresponding modes */
-++static int
-++mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane)
+++void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
+++				      struct sk_buff *skb)
 ++{
-++	u32 old, new, clr = 0, set = 0;
-++	unsigned long flags;
+++	struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
 ++
-++	switch (lane->mode) {
-++	case PHY_MODE_SATA:
-++		/* SATA must be in Lane2 */
-++		if (lane->id == 2)
-++			clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
-++		else
-++			goto error;
-++		break;
+++	switch (hdr->cmd) {
+++	case MTK_WED_WO_EVT_LOG_DUMP: {
+++		const char *msg = (const char *)(skb->data + sizeof(*hdr));
 ++
-++	case PHY_MODE_ETHERNET:
-++		if (lane->id == 0)
-++			clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
-++		else if (lane->id == 1)
-++			clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
-++		else
-++			goto error;
+++		dev_notice(wo->hw->dev, "%s\n", msg);
 ++		break;
-++
-++	case PHY_MODE_USB_HOST_SS:
-++		if (lane->id == 2)
-++			set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
-++		else if (lane->id == 0)
-++			set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
-++		else
-++			goto error;
+++	}
+++	case MTK_WED_WO_EVT_PROFILING: {
+++		struct mtk_wed_wo_log_info *info;
+++		u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info);
+++		int i;
+++
+++		info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr));
+++		for (i = 0 ; i < count ; i++)
+++			dev_notice(wo->hw->dev,
+++				   "SN:%u latency: total=%u, rro:%u, mod:%u\n",
+++				   le32_to_cpu(info[i].sn),
+++				   le32_to_cpu(info[i].total),
+++				   le32_to_cpu(info[i].rro),
+++				   le32_to_cpu(info[i].mod));
 ++		break;
-++
-++	case PHY_MODE_PCIE:
-++		/* PCIE must be in Lane1 */
-++		if (lane->id == 1)
-++			set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
-++		else
-++			goto error;
+++	}
+++	case MTK_WED_WO_EVT_RXCNT_INFO:
 ++		break;
-++
 ++	default:
-++		goto error;
+++		break;
 ++	}
 ++
-++	spin_lock_irqsave(&lane->priv->lock, flags);
-++
-++	old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
-++	new = (old & ~clr) | set;
-++	writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
-++
-++	spin_unlock_irqrestore(&lane->priv->lock, flags);
-++
-++	dev_dbg(lane->dev,
-++		"COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n",
-++		lane->id, lane->mode, old, new);
-++
-++	return 0;
-++error:
-++	dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id,
-++		lane->mode);
-++	return -EINVAL;
+++	dev_kfree_skb(skb);
 ++}
 ++
 ++static int
-++mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane)
+++mtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb,
+++			 int id, int cmd, u16 *wait_seq, bool wait_resp)
 ++{
-++	u32 mask, data, ref_clk;
-++	int ret;
+++	struct mtk_wed_mcu_hdr *hdr;
 ++
-++	/* Configure phy selector for SATA */
-++	ret = mvebu_a3700_comphy_set_phy_selector(lane);
-++	if (ret)
-++		return ret;
+++	/* TODO: make it dynamic based on cmd */
+++	wo->mcu.timeout = 20 * HZ;
 ++
-++	/* Clear phy isolation mode to make it work in normal mode */
-++	comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
-++			    0x0, PHY_ISOLATE_MODE);
-++
-++	/* 0. Check the Polarity invert bits */
-++	data = 0x0;
-++	if (lane->invert_tx)
-++		data |= TXD_INVERT_BIT;
-++	if (lane->invert_rx)
-++		data |= RXD_INVERT_BIT;
-++	mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
-++	comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
-++
-++	/* 1. Select 40-bit data width */
-++	comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
-++			    DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK);
-++
-++	/* 2. Select reference clock(25M) and PHY mode (SATA) */
-++	if (lane->priv->xtal_is_40m)
-++		ref_clk = REF_FREF_SEL_SERDES_40MHZ;
-++	else
-++		ref_clk = REF_FREF_SEL_SERDES_25MHZ;
-++
-++	data = ref_clk | COMPHY_MODE_SATA;
-++	mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
-++
-++	/* 3. Use maximum PLL rate (no power save) */
-++	comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
-++			    USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT);
-++
-++	/* 4. Reset reserved bit */
-++	comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG,
-++			    0x0, PHYCTRL_FRM_PIN_BIT);
-++
-++	/* 5. Set vendor-specific configuration (It is done in sata driver) */
-++	/* XXX: in U-Boot below sequence was executed in this place, in Linux
-++	 * not.  Now it is done only in U-Boot before this comphy
-++	 * initialization - tests shows that it works ok, but in case of any
-++	 * future problem it is left for reference.
-++	 *   reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff);
-++	 *   reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6));
-++	 */
+++	hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr));
+++	hdr->cmd = cmd;
+++	hdr->length = cpu_to_le16(skb->len);
 ++
-++	/* Wait for > 55 us to allow PLL be enabled */
-++	udelay(PLL_SET_DELAY_US);
+++	if (wait_resp && wait_seq) {
+++		u16 seq = ++wo->mcu.seq;
 ++
-++	/* Polling status */
-++	ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN,
-++				   PLL_READY_TX_BIT, COMPHY_PLL_SLEEP,
-++				   COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to lock SATA PLL\n");
-++		return ret;
+++		if (!seq)
+++			seq = ++wo->mcu.seq;
+++		*wait_seq = seq;
+++
+++		hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_NEED_RSP);
+++		hdr->seq = cpu_to_le16(seq);
 ++	}
+++	if (id == MTK_WED_MODULE_ID_WO)
+++		hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO);
 ++
+++	dev_kfree_skb(skb);
 ++	return 0;
 ++}
 ++
-++static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane,
-++				bool is_1gbps)
+++static int
+++mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb,
+++			   int cmd, int seq)
 ++{
-++	int addr, fix_idx;
-++	u16 val;
+++	struct mtk_wed_mcu_hdr *hdr;
 ++
-++	fix_idx = 0;
-++	for (addr = 0; addr < 512; addr++) {
-++		/*
-++		 * All PHY register values are defined in full for 3.125Gbps
-++		 * SERDES speed. The values required for 1.25 Gbps are almost
-++		 * the same and only few registers should be "fixed" in
-++		 * comparison to 3.125 Gbps values. These register values are
-++		 * stored in "gbe_phy_init_fix" array.
-++		 */
-++		if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) {
-++			/* Use new value */
-++			val = gbe_phy_init_fix[fix_idx].value;
-++			if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix))
-++				fix_idx++;
-++		} else {
-++			val = gbe_phy_init[addr];
-++		}
+++	if (!skb) {
+++		dev_err(wo->hw->dev, "Message %08x (seq %d) timeout\n",
+++			cmd, seq);
+++		return -ETIMEDOUT;
+++	}
+++
+++	hdr = (struct mtk_wed_mcu_hdr *)skb->data;
+++	if (le16_to_cpu(hdr->seq) != seq)
+++		return -EAGAIN;
 ++
-++		comphy_lane_reg_set(lane, addr, val, 0xFFFF);
+++	skb_pull(skb, sizeof(*hdr));
+++	switch (cmd) {
+++	case MTK_WED_WO_CMD_RXCNT_INFO:
+++	default:
+++		break;
 ++	}
+++
+++	return 0;
 ++}
 ++
-++static int
-++mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane)
+++int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
+++			 const void *data, int len, bool wait_resp)
 ++{
-++	u32 mask, data, speed_sel;
+++	unsigned long expires;
+++	struct sk_buff *skb;
+++	u16 seq;
 ++	int ret;
 ++
-++	/* Set selector */
-++	ret = mvebu_a3700_comphy_set_phy_selector(lane);
-++	if (ret)
-++		return ret;
+++	skb = mtk_wed_mcu_msg_alloc(data, len);
+++	if (!skb)
+++		return -ENOMEM;
 ++
-++	/*
-++	 * 1. Reset PHY by setting PHY input port PIN_RESET=1.
-++	 * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep
-++	 *    PHY TXP/TXN output to idle state during PHY initialization
-++	 * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0.
-++	 */
-++	data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT;
-++	mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT |
-++	       PIN_PU_TX_BIT | PHY_RX_INIT_BIT;
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+++	mutex_lock(&wo->mcu.mutex);
 ++
-++	/* 4. Release reset to the PHY by setting PIN_RESET=0. */
-++	data = 0x0;
-++	mask = PIN_RESET_COMPHY_BIT;
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+++	ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp);
+++	if (ret || !wait_resp)
+++		goto unlock;
 ++
-++	/*
-++	 * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY
-++	 * bit rate
-++	 */
-++	switch (lane->submode) {
-++	case PHY_INTERFACE_MODE_SGMII:
-++	case PHY_INTERFACE_MODE_1000BASEX:
-++		/* SGMII 1G, SerDes speed 1.25G */
-++		speed_sel = SERDES_SPEED_1_25_G;
-++		break;
-++	case PHY_INTERFACE_MODE_2500BASEX:
-++		/* 2500Base-X, SerDes speed 3.125G */
-++		speed_sel = SERDES_SPEED_3_125_G;
-++		break;
-+ 	default:
-++		/* Other rates are not supported */
-++		dev_err(lane->dev,
-++			"unsupported phy speed %d on comphy lane%d\n",
-++			lane->submode, lane->id);
-+ 		return -EINVAL;
-+ 	}
-++	data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel);
-++	mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK;
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+++	expires = jiffies + wo->mcu.timeout;
+++	do {
+++		skb = mtk_wed_mcu_get_response(wo, expires);
+++		ret = mtk_wed_mcu_parse_response(wo, skb, cmd, seq);
+++		dev_kfree_skb(skb);
+++	} while (ret == -EAGAIN);
 ++
-++	/*
-++	 * 6. Wait 10mS for bandgap and reference clocks to stabilize; then
-++	 * start SW programming.
-++	 */
-++	mdelay(10);
+++unlock:
+++	mutex_unlock(&wo->mcu.mutex);
 ++
-++	/* 7. Program COMPHY register PHY_MODE */
-++	data = COMPHY_MODE_SERDES;
-++	mask = COMPHY_MODE_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+++	return ret;
+++}
 ++
-++	/*
-++	 * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK
-++	 * source
-++	 */
-++	data = 0x0;
-++	mask = PHY_REF_CLK_SEL;
-++	comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
+++static int
+++mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
+++			  struct mtk_wed_wo_memory_region *region)
+++{
+++	struct reserved_mem *rmem;
+++	struct device_node *np;
+++	int index;
 ++
-++	/*
-++	 * 9. Set correct reference clock frequency in COMPHY register
-++	 * REF_FREF_SEL.
-++	 */
-++	if (lane->priv->xtal_is_40m)
-++		data = REF_FREF_SEL_SERDES_50MHZ;
-++	else
-++		data = REF_FREF_SEL_SERDES_25MHZ;
+++	index = of_property_match_string(wo->hw->node, "memory-region-names",
+++					 region->name);
+++	if (index < 0)
+++		return index;
 ++
-++	mask = REF_FREF_SEL_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+++	np = of_parse_phandle(wo->hw->node, "memory-region", index);
+++	if (!np)
+++		return -ENODEV;
 ++
-++	/* 10. Program COMPHY register PHY_GEN_MAX[1:0]
-++	 * This step is mentioned in the flow received from verification team.
-++	 * However the PHY_GEN_MAX value is only meaningful for other interfaces
-++	 * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or
-++	 * PCIe speed 2.5/5 Gbps
-++	 */
+++	rmem = of_reserved_mem_lookup(np);
+++	of_node_put(np);
 ++
-++	/*
-++	 * 11. Program COMPHY register SEL_BITS to set correct parallel data
-++	 * bus width
-++	 */
-++	data = DATA_WIDTH_10BIT;
-++	mask = SEL_DATA_WIDTH_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask);
+++	if (!rmem)
+++		return -ENODEV;
 ++
-++	/*
-++	 * 12. As long as DFE function needs to be enabled in any mode,
-++	 * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F
-++	 * for real chip during COMPHY power on.
-++	 * The step 14 exists (and empty) in the original initialization flow
-++	 * obtained from the verification team. According to the functional
-++	 * specification DFE_UPDATE_EN already has the default value 0x3F
-++	 */
+++	region->phy_addr = rmem->base;
+++	region->size = rmem->size;
+++	region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
 ++
-++	/*
-++	 * 13. Program COMPHY GEN registers.
-++	 * These registers should be programmed based on the lab testing result
-++	 * to achieve optimal performance. Please contact the CEA group to get
-++	 * the related GEN table during real chip bring-up. We only required to
-++	 * run though the entire registers programming flow defined by
-++	 * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock
-++	 * 25 MHz the default values stored in PHY registers are OK.
-++	 */
-++	dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n",
-++		lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G");
-++	if (lane->priv->xtal_is_40m)
-++		comphy_gbe_phy_init(lane,
-++				    lane->submode != PHY_INTERFACE_MODE_2500BASEX);
+++	return !region->addr ? -EINVAL : 0;
+++}
 ++
-++	/*
-++	 * 14. [Simulation Only] should not be used for real chip.
-++	 * By pass power up calibration by programming EXT_FORCE_CAL_DONE
-++	 * (R02h[9]) to 1 to shorten COMPHY simulation time.
-++	 */
+++static int
+++mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
+++			 struct mtk_wed_wo_memory_region *region)
+++{
+++	const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data;
+++	const struct mtk_wed_fw_trailer *trailer;
+++	const struct mtk_wed_fw_region *fw_region;
 ++
-++	/*
-++	 * 15. [Simulation Only: should not be used for real chip]
-++	 * Program COMPHY register FAST_DFE_TIMER_EN=1 to shorten RX training
-++	 * simulation time.
-++	 */
+++	trailer_ptr = fw->data + fw->size - sizeof(*trailer);
+++	trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
+++	region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
+++	first_region_ptr = region_ptr;
 ++
-++	/*
-++	 * 16. Check the PHY Polarity invert bit
-++	 */
-++	data = 0x0;
-++	if (lane->invert_tx)
-++		data |= TXD_INVERT_BIT;
-++	if (lane->invert_rx)
-++		data |= RXD_INVERT_BIT;
-++	mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
-++	comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+++	while (region_ptr < trailer_ptr) {
+++		u32 length;
 ++
-++	/*
-++	 * 17. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to
-++	 * start PHY power up sequence. All the PHY register programming should
-++	 * be done before PIN_PU_PLL=1. There should be no register programming
-++	 * for normal PHY operation from this point.
-++	 */
-++	data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
-++	mask = data;
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+++		fw_region = (const struct mtk_wed_fw_region *)region_ptr;
+++		length = le32_to_cpu(fw_region->len);
 ++
-++	/*
-++	 * 18. Wait for PHY power up sequence to finish by checking output ports
-++	 * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1.
-++	 */
-++	ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
-++				     PHY_PLL_READY_TX_BIT |
-++				     PHY_PLL_READY_RX_BIT,
-++				     COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
-++			lane->id);
-++		return ret;
+++		if (region->phy_addr != le32_to_cpu(fw_region->addr))
+++			goto next;
+++
+++		if (region->size < length)
+++			goto next;
+++
+++		if (first_region_ptr < ptr + length)
+++			goto next;
+++
+++		if (region->shared && region->consumed)
+++			return 0;
+++
+++		if (!region->shared || !region->consumed) {
+++			memcpy_toio(region->addr, ptr, length);
+++			region->consumed = true;
+++			return 0;
+++		}
+++next:
+++		region_ptr += sizeof(*fw_region);
+++		ptr += length;
 ++	}
 ++
-++	/*
-++	 * 19. Set COMPHY input port PIN_TX_IDLE=0
-++	 */
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
+++	return -EINVAL;
+++}
 ++
-++	/*
-++	 * 20. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To
-++	 * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the
-++	 * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to
-++	 * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please
-++	 * refer to RX initialization part for details.
-++	 */
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1,
-++			      PHY_RX_INIT_BIT, PHY_RX_INIT_BIT);
-++
-++	ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
-++				     PHY_PLL_READY_TX_BIT |
-++				     PHY_PLL_READY_RX_BIT,
-++				     COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
-++			lane->id);
-++		return ret;
+++static int
+++mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
+++{
+++	static struct mtk_wed_wo_memory_region mem_region[] = {
+++		[MTK_WED_WO_REGION_EMI] = {
+++			.name = "wo-emi",
+++		},
+++		[MTK_WED_WO_REGION_ILM] = {
+++			.name = "wo-ilm",
+++		},
+++		[MTK_WED_WO_REGION_DATA] = {
+++			.name = "wo-data",
+++			.shared = true,
+++		},
+++	};
+++	const struct mtk_wed_fw_trailer *trailer;
+++	const struct firmware *fw;
+++	const char *fw_name;
+++	u32 val, boot_cr;
+++	int ret, i;
+++
+++	/* load firmware region metadata */
+++	for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
+++		ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
+++		if (ret)
+++			return ret;
 ++	}
 ++
-++	ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
-++				     PHY_RX_INIT_DONE_BIT,
-++				     COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n",
-++			lane->id);
+++	wo->boot.name = "wo-boot";
+++	ret = mtk_wed_get_memory_region(wo, &wo->boot);
+++	if (ret)
+++		return ret;
+++
+++	/* set dummy cr */
+++	wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
+++		wo->hw->index + 1);
+++
+++	/* load firmware */
+++	fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
+++	ret = request_firmware(&fw, fw_name, wo->hw->dev);
+++	if (ret)
 ++		return ret;
+++
+++	trailer = (void *)(fw->data + fw->size -
+++			   sizeof(struct mtk_wed_fw_trailer));
+++	dev_info(wo->hw->dev,
+++		 "MTK WED WO Firmware Version: %.10s, Build Time: %.15s\n",
+++		 trailer->fw_ver, trailer->build_date);
+++	dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n",
+++		 trailer->chip_id, trailer->num_region);
+++
+++	for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
+++		ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]);
+++		if (ret)
+++			goto out;
 ++	}
 ++
+++	/* set the start address */
+++	boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR
+++				: MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
+++	wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
+++	/* wo firmware reset */
+++	wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
+++
+++	val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
+++	val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK
+++			     : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
+++	wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
+++out:
+++	release_firmware(fw);
+++
+++	return ret;
+++}
+++
+++static u32
+++mtk_wed_mcu_read_fw_dl(struct mtk_wed_wo *wo)
+++{
+++	return wed_r32(wo->hw->wed_dev,
+++		       MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL);
+++}
+++
+++int mtk_wed_mcu_init(struct mtk_wed_wo *wo)
+++{
+++	u32 val;
+++	int ret;
+++
+++	skb_queue_head_init(&wo->mcu.res_q);
+++	init_waitqueue_head(&wo->mcu.wait);
+++	mutex_init(&wo->mcu.mutex);
+++
+++	ret = mtk_wed_mcu_load_firmware(wo);
+++	if (ret)
+++		return ret;
+++
+++	return readx_poll_timeout(mtk_wed_mcu_read_fw_dl, wo, val, !val,
+++				  100, MTK_FW_DL_TIMEOUT);
+++}
+++
+++MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
+++MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
++--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++@@ -152,6 +152,7 @@ struct mtk_wdma_desc {
++ 
++ #define MTK_WED_RING_RX(_n)				(0x400 + (_n) * 0x10)
++ 
+++#define MTK_WED_SCR0					0x3c0
++ #define MTK_WED_WPDMA_INT_TRIGGER			0x504
++ #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE		BIT(1)
++ #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE		GENMASK(5, 4)
++--- /dev/null
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++@@ -0,0 +1,150 @@
+++/* SPDX-License-Identifier: GPL-2.0-only */
+++/* Copyright (C) 2022 Lorenzo Bianconi <lorenzo@kernel.org>  */
+++
+++#ifndef __MTK_WED_WO_H
+++#define __MTK_WED_WO_H
+++
+++#include <linux/skbuff.h>
+++#include <linux/netdevice.h>
+++
+++struct mtk_wed_hw;
+++
+++struct mtk_wed_mcu_hdr {
+++	/* DW0 */
+++	u8 version;
+++	u8 cmd;
+++	__le16 length;
+++
+++	/* DW1 */
+++	__le16 seq;
+++	__le16 flag;
+++
+++	/* DW2 */
+++	__le32 status;
+++
+++	/* DW3 */
+++	u8 rsv[20];
+++};
+++
+++struct mtk_wed_wo_log_info {
+++	__le32 sn;
+++	__le32 total;
+++	__le32 rro;
+++	__le32 mod;
+++};
+++
+++enum mtk_wed_wo_event {
+++	MTK_WED_WO_EVT_LOG_DUMP		= 0x1,
+++	MTK_WED_WO_EVT_PROFILING	= 0x2,
+++	MTK_WED_WO_EVT_RXCNT_INFO	= 0x3,
+++};
+++
+++#define MTK_WED_MODULE_ID_WO		1
+++#define MTK_FW_DL_TIMEOUT		4000000 /* us */
+++#define MTK_WOCPU_TIMEOUT		2000000 /* us */
+++
+++enum {
+++	MTK_WED_WARP_CMD_FLAG_RSP		= BIT(0),
+++	MTK_WED_WARP_CMD_FLAG_NEED_RSP		= BIT(1),
+++	MTK_WED_WARP_CMD_FLAG_FROM_TO_WO	= BIT(2),
+++};
+++
+++enum {
+++	MTK_WED_WO_REGION_EMI,
+++	MTK_WED_WO_REGION_ILM,
+++	MTK_WED_WO_REGION_DATA,
+++	MTK_WED_WO_REGION_BOOT,
+++	__MTK_WED_WO_REGION_MAX,
+++};
+++
+++enum mtk_wed_dummy_cr_idx {
+++	MTK_WED_DUMMY_CR_FWDL,
+++	MTK_WED_DUMMY_CR_WO_STATUS,
+++};
+++
+++#define MT7986_FIRMWARE_WO0	"mediatek/mt7986_wo_0.bin"
+++#define MT7986_FIRMWARE_WO1	"mediatek/mt7986_wo_1.bin"
+++
+++#define MTK_WO_MCU_CFG_LS_BASE				0
+++#define MTK_WO_MCU_CFG_LS_HW_VER_ADDR			(MTK_WO_MCU_CFG_LS_BASE + 0x000)
+++#define MTK_WO_MCU_CFG_LS_FW_VER_ADDR			(MTK_WO_MCU_CFG_LS_BASE + 0x004)
+++#define MTK_WO_MCU_CFG_LS_CFG_DBG1_ADDR			(MTK_WO_MCU_CFG_LS_BASE + 0x00c)
+++#define MTK_WO_MCU_CFG_LS_CFG_DBG2_ADDR			(MTK_WO_MCU_CFG_LS_BASE + 0x010)
+++#define MTK_WO_MCU_CFG_LS_WF_MCCR_ADDR			(MTK_WO_MCU_CFG_LS_BASE + 0x014)
+++#define MTK_WO_MCU_CFG_LS_WF_MCCR_SET_ADDR		(MTK_WO_MCU_CFG_LS_BASE + 0x018)
+++#define MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR		(MTK_WO_MCU_CFG_LS_BASE + 0x01c)
+++#define MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR		(MTK_WO_MCU_CFG_LS_BASE + 0x050)
+++#define MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR		(MTK_WO_MCU_CFG_LS_BASE + 0x060)
+++#define MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR		(MTK_WO_MCU_CFG_LS_BASE + 0x064)
+++
+++#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK	BIT(5)
+++#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK	BIT(0)
+++
+++struct mtk_wed_wo_memory_region {
+++	const char *name;
+++	void __iomem *addr;
+++	phys_addr_t phy_addr;
+++	u32 size;
+++	bool shared:1;
+++	bool consumed:1;
+++};
+++
+++struct mtk_wed_fw_region {
+++	__le32 decomp_crc;
+++	__le32 decomp_len;
+++	__le32 decomp_blk_sz;
+++	u8 rsv0[4];
+++	__le32 addr;
+++	__le32 len;
+++	u8 feature_set;
+++	u8 rsv1[15];
+++} __packed;
+++
+++struct mtk_wed_fw_trailer {
+++	u8 chip_id;
+++	u8 eco_code;
+++	u8 num_region;
+++	u8 format_ver;
+++	u8 format_flag;
+++	u8 rsv[2];
+++	char fw_ver[10];
+++	char build_date[15];
+++	u32 crc;
+++};
+++
+++struct mtk_wed_wo {
+++	struct mtk_wed_hw *hw;
+++	struct mtk_wed_wo_memory_region boot;
+++
+++	struct {
+++		struct mutex mutex;
+++		int timeout;
+++		u16 seq;
+++
+++		struct sk_buff_head res_q;
+++		wait_queue_head_t wait;
+++	} mcu;
+++};
+++
+++static inline int
+++mtk_wed_mcu_check_msg(struct mtk_wed_wo *wo, struct sk_buff *skb)
+++{
+++	struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
+++
+++	if (hdr->version)
+++		return -EINVAL;
+++
+++	if (skb->len < sizeof(*hdr) || skb->len != le16_to_cpu(hdr->length))
+++		return -EINVAL;
+++
 ++	return 0;
+++}
+++
+++void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb);
+++void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
+++				      struct sk_buff *skb);
+++int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
+++			 const void *data, int len, bool wait_resp);
+++int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
+++
+++#endif /* __MTK_WED_WO_H */
++--- a/include/linux/soc/mediatek/mtk_wed.h
+++++ b/include/linux/soc/mediatek/mtk_wed.h
++@@ -11,6 +11,35 @@
++ struct mtk_wed_hw;
++ struct mtk_wdma_desc;
++ 
+++enum mtk_wed_wo_cmd {
+++	MTK_WED_WO_CMD_WED_CFG,
+++	MTK_WED_WO_CMD_WED_RX_STAT,
+++	MTK_WED_WO_CMD_RRO_SER,
+++	MTK_WED_WO_CMD_DBG_INFO,
+++	MTK_WED_WO_CMD_DEV_INFO,
+++	MTK_WED_WO_CMD_BSS_INFO,
+++	MTK_WED_WO_CMD_STA_REC,
+++	MTK_WED_WO_CMD_DEV_INFO_DUMP,
+++	MTK_WED_WO_CMD_BSS_INFO_DUMP,
+++	MTK_WED_WO_CMD_STA_REC_DUMP,
+++	MTK_WED_WO_CMD_BA_INFO_DUMP,
+++	MTK_WED_WO_CMD_FBCMD_Q_DUMP,
+++	MTK_WED_WO_CMD_FW_LOG_CTRL,
+++	MTK_WED_WO_CMD_LOG_FLUSH,
+++	MTK_WED_WO_CMD_CHANGE_STATE,
+++	MTK_WED_WO_CMD_CPU_STATS_ENABLE,
+++	MTK_WED_WO_CMD_CPU_STATS_DUMP,
+++	MTK_WED_WO_CMD_EXCEPTION_INIT,
+++	MTK_WED_WO_CMD_PROF_CTRL,
+++	MTK_WED_WO_CMD_STA_BA_DUMP,
+++	MTK_WED_WO_CMD_BA_CTRL_DUMP,
+++	MTK_WED_WO_CMD_RXCNT_CTRL,
+++	MTK_WED_WO_CMD_RXCNT_INFO,
+++	MTK_WED_WO_CMD_SET_CAP,
+++	MTK_WED_WO_CMD_CCIF_RING_DUMP,
+++	MTK_WED_WO_CMD_WED_END
+++};
+++
++ enum mtk_wed_bus_tye {
++ 	MTK_WED_BUS_PCIE,
++ 	MTK_WED_BUS_AXI,
+diff --git a/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_wed-introduce-wed-wo-support.patch b/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_wed-introduce-wed-wo-support.patch
+new file mode 100644
+index 0000000000..dbd7e30fbb
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_wed-introduce-wed-wo-support.patch
+@@ -0,0 +1,737 @@
++From: Lorenzo Bianconi <lorenzo@kernel.org>
++Date: Sat, 5 Nov 2022 23:36:19 +0100
++Subject: [PATCH] net: ethernet: mtk_wed: introduce wed wo support
++
++Introduce WO chip support to mtk wed driver. MTK WED WO is used to
++implement RX Wireless Ethernet Dispatch and offload traffic received by
++wlan nic to the wired interface.
++
++Tested-by: Daniel Golle <daniel@makrotopia.org>
++Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: David S. Miller <davem@davemloft.net>
++---
++ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.c
++
++--- a/drivers/net/ethernet/mediatek/Makefile
+++++ b/drivers/net/ethernet/mediatek/Makefile
++@@ -5,7 +5,7 @@
++ 
++ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
++ mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
++-mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o
+++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o
++ ifdef CONFIG_DEBUG_FS
++ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
++ endif
++--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
++@@ -16,6 +16,7 @@
++ #include "mtk_wed_regs.h"
++ #include "mtk_wed.h"
++ #include "mtk_ppe.h"
+++#include "mtk_wed_wo.h"
++ 
++ #define MTK_PCIE_BASE(n)		(0x1a143000 + (n) * 0x2000)
++ 
++@@ -355,6 +356,8 @@ mtk_wed_detach(struct mtk_wed_device *de
++ 
++ 	mtk_wed_free_buffer(dev);
++ 	mtk_wed_free_tx_rings(dev);
+++	if (hw->version != 1)
+++		mtk_wed_wo_deinit(hw);
++ 
++ 	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
++ 		struct device_node *wlan_node;
++@@ -885,9 +888,11 @@ mtk_wed_attach(struct mtk_wed_device *de
++ 	}
++ 
++ 	mtk_wed_hw_init_early(dev);
++-	if (hw->hifsys)
+++	if (hw->version == 1)
++ 		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
++ 				   BIT(hw->index), 0);
+++	else
+++		ret = mtk_wed_wo_init(hw);
++ 
++ out:
++ 	mutex_unlock(&hw_lock);
++--- a/drivers/net/ethernet/mediatek/mtk_wed.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
++@@ -10,6 +10,7 @@
++ #include <linux/netdevice.h>
++ 
++ struct mtk_eth;
+++struct mtk_wed_wo;
++ 
++ struct mtk_wed_hw {
++ 	struct device_node *node;
++@@ -22,6 +23,7 @@ struct mtk_wed_hw {
++ 	struct regmap *mirror;
++ 	struct dentry *debugfs_dir;
++ 	struct mtk_wed_device *wed_dev;
+++	struct mtk_wed_wo *wed_wo;
++ 	u32 debugfs_reg;
++ 	u32 num_flows;
++ 	u8 version;
++--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++@@ -122,8 +122,7 @@ mtk_wed_mcu_skb_send_msg(struct mtk_wed_
++ 	if (id == MTK_WED_MODULE_ID_WO)
++ 		hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO);
++ 
++-	dev_kfree_skb(skb);
++-	return 0;
+++	return mtk_wed_wo_queue_tx_skb(wo, &wo->q_tx, skb);
 + }
 + 
-+-static int mvebu_a3700_comphy_get_fw_mode(int lane,
++ static int
++--- /dev/null
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
++@@ -0,0 +1,508 @@
+++// SPDX-License-Identifier: GPL-2.0-only
+++/* Copyright (C) 2022 MediaTek Inc.
+++ *
+++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+++ *	   Sujuan Chen <sujuan.chen@mediatek.com>
+++ */
+++
+++#include <linux/kernel.h>
+++#include <linux/dma-mapping.h>
+++#include <linux/of_platform.h>
+++#include <linux/interrupt.h>
+++#include <linux/of_address.h>
+++#include <linux/mfd/syscon.h>
+++#include <linux/of_irq.h>
+++#include <linux/bitfield.h>
+++
+++#include "mtk_wed.h"
+++#include "mtk_wed_regs.h"
+++#include "mtk_wed_wo.h"
+++
+++static u32
+++mtk_wed_mmio_r32(struct mtk_wed_wo *wo, u32 reg)
+++{
+++	u32 val;
+++
+++	if (regmap_read(wo->mmio.regs, reg, &val))
+++		val = ~0;
+++
+++	return val;
+++}
+++
+++static void
+++mtk_wed_mmio_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
+++{
+++	regmap_write(wo->mmio.regs, reg, val);
+++}
+++
+++static u32
+++mtk_wed_wo_get_isr(struct mtk_wed_wo *wo)
+++{
+++	u32 val = mtk_wed_mmio_r32(wo, MTK_WED_WO_CCIF_RCHNUM);
+++
+++	return val & MTK_WED_WO_CCIF_RCHNUM_MASK;
+++}
+++
+++static void
+++mtk_wed_wo_set_isr(struct mtk_wed_wo *wo, u32 mask)
+++{
+++	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_IRQ0_MASK, mask);
+++}
+++
+++static void
+++mtk_wed_wo_set_ack(struct mtk_wed_wo *wo, u32 mask)
+++{
+++	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_ACK, mask);
+++}
+++
+++static void
+++mtk_wed_wo_set_isr_mask(struct mtk_wed_wo *wo, u32 mask, u32 val, bool set)
+++{
+++	unsigned long flags;
+++
+++	spin_lock_irqsave(&wo->mmio.lock, flags);
+++	wo->mmio.irq_mask &= ~mask;
+++	wo->mmio.irq_mask |= val;
+++	if (set)
+++		mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
+++	spin_unlock_irqrestore(&wo->mmio.lock, flags);
+++}
+++
+++static void
+++mtk_wed_wo_irq_enable(struct mtk_wed_wo *wo, u32 mask)
+++{
+++	mtk_wed_wo_set_isr_mask(wo, 0, mask, false);
+++	tasklet_schedule(&wo->mmio.irq_tasklet);
+++}
+++
+++static void
+++mtk_wed_wo_irq_disable(struct mtk_wed_wo *wo, u32 mask)
+++{
+++	mtk_wed_wo_set_isr_mask(wo, mask, 0, true);
+++}
+++
+++static void
+++mtk_wed_wo_kickout(struct mtk_wed_wo *wo)
+++{
+++	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_BUSY, 1 << MTK_WED_WO_TXCH_NUM);
+++	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_TCHNUM, MTK_WED_WO_TXCH_NUM);
+++}
+++
+++static void
+++mtk_wed_wo_queue_kick(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
+++		      u32 val)
+++{
+++	wmb();
+++	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, val);
+++}
+++
+++static void *
+++mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len,
+++		   bool flush)
+++{
+++	int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
+++	int index = (q->tail + 1) % q->n_desc;
+++	struct mtk_wed_wo_queue_entry *entry;
+++	struct mtk_wed_wo_queue_desc *desc;
+++	void *buf;
+++
+++	if (!q->queued)
+++		return NULL;
+++
+++	if (flush)
+++		q->desc[index].ctrl |= cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE);
+++	else if (!(q->desc[index].ctrl & cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE)))
+++		return NULL;
+++
+++	q->tail = index;
+++	q->queued--;
+++
+++	desc = &q->desc[index];
+++	entry = &q->entry[index];
+++	buf = entry->buf;
+++	if (len)
+++		*len = FIELD_GET(MTK_WED_WO_CTL_SD_LEN0,
+++				 le32_to_cpu(READ_ONCE(desc->ctrl)));
+++	if (buf)
+++		dma_unmap_single(wo->hw->dev, entry->addr, buf_len,
+++				 DMA_FROM_DEVICE);
+++	entry->buf = NULL;
+++
+++	return buf;
+++}
+++
 ++static int
-++mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
+++mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
+++			gfp_t gfp, bool rx)
 ++{
-++	u32 mask, data, cfg, ref_clk;
-++	int ret;
+++	enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+++	int n_buf = 0;
 ++
-++	/* Set phy seclector */
-++	ret = mvebu_a3700_comphy_set_phy_selector(lane);
-++	if (ret)
-++		return ret;
+++	spin_lock_bh(&q->lock);
+++	while (q->queued < q->n_desc) {
+++		void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp);
+++		struct mtk_wed_wo_queue_entry *entry;
+++		dma_addr_t addr;
 ++
-++	/*
-++	 * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
-++	 * register belong to UTMI module, so it is set in UTMI phy driver.
-++	 */
+++		if (!buf)
+++			break;
 ++
-++	/*
-++	 * 1. Set PRD_TXDEEMPH (3.5db de-emph)
-++	 */
-++	data = PRD_TXDEEMPH0_MASK;
-++	mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK |
-++	       CFG_TX_ALIGN_POS_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask);
+++		addr = dma_map_single(wo->hw->dev, buf, q->buf_size, dir);
+++		if (unlikely(dma_mapping_error(wo->hw->dev, addr))) {
+++			skb_free_frag(buf);
+++			break;
+++		}
 ++
-++	/*
-++	 * 2. Set BIT0: enable transmitter in high impedance mode
-++	 *    Set BIT[3:4]: delay 2 clock cycles for HiZ off latency
-++	 *    Set BIT6: Tx detect Rx at HiZ mode
-++	 *    Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db
-++	 *            together with bit 0 of COMPHY_PIPE_LANE_CFG0 register
-++	 */
-++	data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN;
-++	mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK |
-++	       TX_ELEC_IDLE_MODE_EN;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask);
+++		q->head = (q->head + 1) % q->n_desc;
+++		entry = &q->entry[q->head];
+++		entry->addr = addr;
+++		entry->len = q->buf_size;
+++		q->entry[q->head].buf = buf;
+++
+++		if (rx) {
+++			struct mtk_wed_wo_queue_desc *desc = &q->desc[q->head];
+++			u32 ctrl = MTK_WED_WO_CTL_LAST_SEC0 |
+++				   FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0,
+++					      entry->len);
+++
+++			WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
+++			WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+++		}
+++		q->queued++;
+++		n_buf++;
+++	}
+++	spin_unlock_bh(&q->lock);
+++
+++	return n_buf;
+++}
+++
+++static void
+++mtk_wed_wo_rx_complete(struct mtk_wed_wo *wo)
+++{
+++	mtk_wed_wo_set_ack(wo, MTK_WED_WO_RXCH_INT_MASK);
+++	mtk_wed_wo_irq_enable(wo, MTK_WED_WO_RXCH_INT_MASK);
+++}
+++
+++static void
+++mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
+++{
+++	for (;;) {
+++		struct mtk_wed_mcu_hdr *hdr;
+++		struct sk_buff *skb;
+++		void *data;
+++		u32 len;
+++
+++		data = mtk_wed_wo_dequeue(wo, q, &len, false);
+++		if (!data)
+++			break;
+++
+++		skb = build_skb(data, q->buf_size);
+++		if (!skb) {
+++			skb_free_frag(data);
+++			continue;
+++		}
+++
+++		__skb_put(skb, len);
+++		if (mtk_wed_mcu_check_msg(wo, skb)) {
+++			dev_kfree_skb(skb);
+++			continue;
+++		}
+++
+++		hdr = (struct mtk_wed_mcu_hdr *)skb->data;
+++		if (hdr->flag & cpu_to_le16(MTK_WED_WARP_CMD_FLAG_RSP))
+++			mtk_wed_mcu_rx_event(wo, skb);
+++		else
+++			mtk_wed_mcu_rx_unsolicited_event(wo, skb);
+++	}
+++
+++	if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) {
+++		u32 index = (q->head - 1) % q->n_desc;
+++
+++		mtk_wed_wo_queue_kick(wo, q, index);
+++	}
+++}
+++
+++static irqreturn_t
+++mtk_wed_wo_irq_handler(int irq, void *data)
+++{
+++	struct mtk_wed_wo *wo = data;
+++
+++	mtk_wed_wo_set_isr(wo, 0);
+++	tasklet_schedule(&wo->mmio.irq_tasklet);
+++
+++	return IRQ_HANDLED;
+++}
+++
+++static void mtk_wed_wo_irq_tasklet(struct tasklet_struct *t)
+++{
+++	struct mtk_wed_wo *wo = from_tasklet(wo, t, mmio.irq_tasklet);
+++	u32 intr, mask;
+++
+++	/* disable interrupts */
+++	mtk_wed_wo_set_isr(wo, 0);
+++
+++	intr = mtk_wed_wo_get_isr(wo);
+++	intr &= wo->mmio.irq_mask;
+++	mask = intr & (MTK_WED_WO_RXCH_INT_MASK | MTK_WED_WO_EXCEPTION_INT_MASK);
+++	mtk_wed_wo_irq_disable(wo, mask);
+++
+++	if (intr & MTK_WED_WO_RXCH_INT_MASK) {
+++		mtk_wed_wo_rx_run_queue(wo, &wo->q_rx);
+++		mtk_wed_wo_rx_complete(wo);
+++	}
+++}
+++
+++/* mtk wed wo hw queues */
+++
+++static int
+++mtk_wed_wo_queue_alloc(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
+++		       int n_desc, int buf_size, int index,
+++		       struct mtk_wed_wo_queue_regs *regs)
+++{
+++	spin_lock_init(&q->lock);
+++	q->regs = *regs;
+++	q->n_desc = n_desc;
+++	q->buf_size = buf_size;
+++
+++	q->desc = dmam_alloc_coherent(wo->hw->dev, n_desc * sizeof(*q->desc),
+++				      &q->desc_dma, GFP_KERNEL);
+++	if (!q->desc)
+++		return -ENOMEM;
+++
+++	q->entry = devm_kzalloc(wo->hw->dev, n_desc * sizeof(*q->entry),
+++				GFP_KERNEL);
+++	if (!q->entry)
+++		return -ENOMEM;
+++
+++	return 0;
+++}
+++
+++static void
+++mtk_wed_wo_queue_free(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
+++{
+++	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
+++	dma_free_coherent(wo->hw->dev, q->n_desc * sizeof(*q->desc), q->desc,
+++			  q->desc_dma);
+++}
+++
+++static void
+++mtk_wed_wo_queue_tx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
+++{
+++	struct page *page;
+++	int i;
+++
+++	spin_lock_bh(&q->lock);
+++	for (i = 0; i < q->n_desc; i++) {
+++		struct mtk_wed_wo_queue_entry *entry = &q->entry[i];
+++
+++		dma_unmap_single(wo->hw->dev, entry->addr, entry->len,
+++				 DMA_TO_DEVICE);
+++		skb_free_frag(entry->buf);
+++		entry->buf = NULL;
+++	}
+++	spin_unlock_bh(&q->lock);
+++
+++	if (!q->cache.va)
+++		return;
+++
+++	page = virt_to_page(q->cache.va);
+++	__page_frag_cache_drain(page, q->cache.pagecnt_bias);
+++	memset(&q->cache, 0, sizeof(q->cache));
+++}
+++
+++static void
+++mtk_wed_wo_queue_rx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
+++{
+++	struct page *page;
+++
+++	spin_lock_bh(&q->lock);
+++	for (;;) {
+++		void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
+++
+++		if (!buf)
+++			break;
+++
+++		skb_free_frag(buf);
+++	}
+++	spin_unlock_bh(&q->lock);
+++
+++	if (!q->cache.va)
+++		return;
+++
+++	page = virt_to_page(q->cache.va);
+++	__page_frag_cache_drain(page, q->cache.pagecnt_bias);
+++	memset(&q->cache, 0, sizeof(q->cache));
+++}
+++
+++static void
+++mtk_wed_wo_queue_reset(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
+++{
+++	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
+++	mtk_wed_mmio_w32(wo, q->regs.desc_base, q->desc_dma);
+++	mtk_wed_mmio_w32(wo, q->regs.ring_size, q->n_desc);
+++}
+++
+++int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
+++			    struct sk_buff *skb)
+++{
+++	struct mtk_wed_wo_queue_entry *entry;
+++	struct mtk_wed_wo_queue_desc *desc;
+++	int ret = 0, index;
+++	u32 ctrl;
+++
+++	spin_lock_bh(&q->lock);
+++
+++	q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx);
+++	index = (q->head + 1) % q->n_desc;
+++	if (q->tail == index) {
+++		ret = -ENOMEM;
+++		goto out;
+++	}
+++
+++	entry = &q->entry[index];
+++	if (skb->len > entry->len) {
+++		ret = -ENOMEM;
+++		goto out;
+++	}
+++
+++	desc = &q->desc[index];
+++	q->head = index;
+++
+++	dma_sync_single_for_cpu(wo->hw->dev, entry->addr, skb->len,
+++				DMA_TO_DEVICE);
+++	memcpy(entry->buf, skb->data, skb->len);
+++	dma_sync_single_for_device(wo->hw->dev, entry->addr, skb->len,
+++				   DMA_TO_DEVICE);
+++
+++	ctrl = FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, skb->len) |
+++	       MTK_WED_WO_CTL_LAST_SEC0 | MTK_WED_WO_CTL_DMA_DONE;
+++	WRITE_ONCE(desc->buf0, cpu_to_le32(entry->addr));
+++	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+++
+++	mtk_wed_wo_queue_kick(wo, q, q->head);
+++	mtk_wed_wo_kickout(wo);
+++out:
+++	spin_unlock_bh(&q->lock);
+++
+++	dev_kfree_skb(skb);
+++
+++	return ret;
+++}
+++
+++static int
+++mtk_wed_wo_exception_init(struct mtk_wed_wo *wo)
+++{
+++	return 0;
+++}
+++
+++static int
+++mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
+++{
+++	struct mtk_wed_wo_queue_regs regs;
+++	struct device_node *np;
+++	int ret;
+++
+++	np = of_parse_phandle(wo->hw->node, "mediatek,wo-ccif", 0);
+++	if (!np)
+++		return -ENODEV;
+++
+++	wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL);
+++	if (IS_ERR_OR_NULL(wo->mmio.regs))
+++		return PTR_ERR(wo->mmio.regs);
+++
+++	wo->mmio.irq = irq_of_parse_and_map(np, 0);
+++	wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK;
+++	spin_lock_init(&wo->mmio.lock);
+++	tasklet_setup(&wo->mmio.irq_tasklet, mtk_wed_wo_irq_tasklet);
+++
+++	ret = devm_request_irq(wo->hw->dev, wo->mmio.irq,
+++			       mtk_wed_wo_irq_handler, IRQF_TRIGGER_HIGH,
+++			       KBUILD_MODNAME, wo);
+++	if (ret)
+++		goto error;
+++
+++	regs.desc_base = MTK_WED_WO_CCIF_DUMMY1;
+++	regs.ring_size = MTK_WED_WO_CCIF_DUMMY2;
+++	regs.dma_idx = MTK_WED_WO_CCIF_SHADOW4;
+++	regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY3;
+++
+++	ret = mtk_wed_wo_queue_alloc(wo, &wo->q_tx, MTK_WED_WO_RING_SIZE,
+++				     MTK_WED_WO_CMD_LEN, MTK_WED_WO_TXCH_NUM,
+++				     &regs);
+++	if (ret)
+++		goto error;
+++
+++	mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false);
+++	mtk_wed_wo_queue_reset(wo, &wo->q_tx);
+++
+++	regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
+++	regs.ring_size = MTK_WED_WO_CCIF_DUMMY6;
+++	regs.dma_idx = MTK_WED_WO_CCIF_SHADOW8;
+++	regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY7;
+++
+++	ret = mtk_wed_wo_queue_alloc(wo, &wo->q_rx, MTK_WED_WO_RING_SIZE,
+++				     MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM,
+++				     &regs);
+++	if (ret)
+++		goto error;
+++
+++	mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true);
+++	mtk_wed_wo_queue_reset(wo, &wo->q_rx);
+++
+++	/* rx queue irqmask */
+++	mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
+++
+++	return 0;
+++
+++error:
+++	devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
+++
+++	return ret;
+++}
+++
+++static void
+++mtk_wed_wo_hw_deinit(struct mtk_wed_wo *wo)
+++{
+++	/* disable interrupts */
+++	mtk_wed_wo_set_isr(wo, 0);
+++
+++	tasklet_disable(&wo->mmio.irq_tasklet);
+++
+++	disable_irq(wo->mmio.irq);
+++	devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
+++
+++	mtk_wed_wo_queue_tx_clean(wo, &wo->q_tx);
+++	mtk_wed_wo_queue_rx_clean(wo, &wo->q_rx);
+++	mtk_wed_wo_queue_free(wo, &wo->q_tx);
+++	mtk_wed_wo_queue_free(wo, &wo->q_rx);
+++}
+++
+++int mtk_wed_wo_init(struct mtk_wed_hw *hw)
+++{
+++	struct mtk_wed_wo *wo;
+++	int ret;
+++
+++	wo = devm_kzalloc(hw->dev, sizeof(*wo), GFP_KERNEL);
+++	if (!wo)
+++		return -ENOMEM;
+++
+++	hw->wed_wo = wo;
+++	wo->hw = hw;
+++
+++	ret = mtk_wed_wo_hardware_init(wo);
+++	if (ret)
+++		return ret;
+++
+++	ret = mtk_wed_mcu_init(wo);
+++	if (ret)
+++		return ret;
+++
+++	return mtk_wed_wo_exception_init(wo);
+++}
+++
+++void mtk_wed_wo_deinit(struct mtk_wed_hw *hw)
+++{
+++	struct mtk_wed_wo *wo = hw->wed_wo;
+++
+++	mtk_wed_wo_hw_deinit(wo);
+++}
++--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++@@ -80,6 +80,54 @@ enum mtk_wed_dummy_cr_idx {
++ #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK	BIT(5)
++ #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK	BIT(0)
++ 
+++#define MTK_WED_WO_RING_SIZE	256
+++#define MTK_WED_WO_CMD_LEN	1504
+++
+++#define MTK_WED_WO_TXCH_NUM		0
+++#define MTK_WED_WO_RXCH_NUM		1
+++#define MTK_WED_WO_RXCH_WO_EXCEPTION	7
+++
+++#define MTK_WED_WO_TXCH_INT_MASK	BIT(0)
+++#define MTK_WED_WO_RXCH_INT_MASK	BIT(1)
+++#define MTK_WED_WO_EXCEPTION_INT_MASK	BIT(7)
+++#define MTK_WED_WO_ALL_INT_MASK		(MTK_WED_WO_RXCH_INT_MASK | \
+++					 MTK_WED_WO_EXCEPTION_INT_MASK)
+++
+++#define MTK_WED_WO_CCIF_BUSY		0x004
+++#define MTK_WED_WO_CCIF_START		0x008
+++#define MTK_WED_WO_CCIF_TCHNUM		0x00c
+++#define MTK_WED_WO_CCIF_RCHNUM		0x010
+++#define MTK_WED_WO_CCIF_RCHNUM_MASK	GENMASK(7, 0)
+++
+++#define MTK_WED_WO_CCIF_ACK		0x014
+++#define MTK_WED_WO_CCIF_IRQ0_MASK	0x018
+++#define MTK_WED_WO_CCIF_IRQ1_MASK	0x01c
+++#define MTK_WED_WO_CCIF_DUMMY1		0x020
+++#define MTK_WED_WO_CCIF_DUMMY2		0x024
+++#define MTK_WED_WO_CCIF_DUMMY3		0x028
+++#define MTK_WED_WO_CCIF_DUMMY4		0x02c
+++#define MTK_WED_WO_CCIF_SHADOW1		0x030
+++#define MTK_WED_WO_CCIF_SHADOW2		0x034
+++#define MTK_WED_WO_CCIF_SHADOW3		0x038
+++#define MTK_WED_WO_CCIF_SHADOW4		0x03c
+++#define MTK_WED_WO_CCIF_DUMMY5		0x050
+++#define MTK_WED_WO_CCIF_DUMMY6		0x054
+++#define MTK_WED_WO_CCIF_DUMMY7		0x058
+++#define MTK_WED_WO_CCIF_DUMMY8		0x05c
+++#define MTK_WED_WO_CCIF_SHADOW5		0x060
+++#define MTK_WED_WO_CCIF_SHADOW6		0x064
+++#define MTK_WED_WO_CCIF_SHADOW7		0x068
+++#define MTK_WED_WO_CCIF_SHADOW8		0x06c
+++
+++#define MTK_WED_WO_CTL_SD_LEN1		GENMASK(13, 0)
+++#define MTK_WED_WO_CTL_LAST_SEC1	BIT(14)
+++#define MTK_WED_WO_CTL_BURST		BIT(15)
+++#define MTK_WED_WO_CTL_SD_LEN0_SHIFT	16
+++#define MTK_WED_WO_CTL_SD_LEN0		GENMASK(29, 16)
+++#define MTK_WED_WO_CTL_LAST_SEC0	BIT(30)
+++#define MTK_WED_WO_CTL_DMA_DONE		BIT(31)
+++#define MTK_WED_WO_INFO_WINFO		GENMASK(15, 0)
+++
++ struct mtk_wed_wo_memory_region {
++ 	const char *name;
++ 	void __iomem *addr;
++@@ -112,10 +160,53 @@ struct mtk_wed_fw_trailer {
++ 	u32 crc;
++ };
++ 
+++struct mtk_wed_wo_queue_regs {
+++	u32 desc_base;
+++	u32 ring_size;
+++	u32 cpu_idx;
+++	u32 dma_idx;
+++};
+++
+++struct mtk_wed_wo_queue_desc {
+++	__le32 buf0;
+++	__le32 ctrl;
+++	__le32 buf1;
+++	__le32 info;
+++	__le32 reserved[4];
+++} __packed __aligned(32);
+++
+++struct mtk_wed_wo_queue_entry {
+++	dma_addr_t addr;
+++	void *buf;
+++	u32 len;
+++};
+++
+++struct mtk_wed_wo_queue {
+++	struct mtk_wed_wo_queue_regs regs;
+++
+++	struct page_frag_cache cache;
+++	spinlock_t lock;
+++
+++	struct mtk_wed_wo_queue_desc *desc;
+++	dma_addr_t desc_dma;
+++
+++	struct mtk_wed_wo_queue_entry *entry;
+++
+++	u16 head;
+++	u16 tail;
+++	int n_desc;
+++	int queued;
+++	int buf_size;
+++
+++};
+++
++ struct mtk_wed_wo {
++ 	struct mtk_wed_hw *hw;
++ 	struct mtk_wed_wo_memory_region boot;
++ 
+++	struct mtk_wed_wo_queue q_tx;
+++	struct mtk_wed_wo_queue q_rx;
+++
++ 	struct {
++ 		struct mutex mutex;
++ 		int timeout;
++@@ -124,6 +215,15 @@ struct mtk_wed_wo {
++ 		struct sk_buff_head res_q;
++ 		wait_queue_head_t wait;
++ 	} mcu;
+++
+++	struct {
+++		struct regmap *regs;
+++
+++		spinlock_t lock;
+++		struct tasklet_struct irq_tasklet;
+++		int irq;
+++		u32 irq_mask;
+++	} mmio;
++ };
++ 
++ static inline int
++@@ -146,5 +246,9 @@ void mtk_wed_mcu_rx_unsolicited_event(st
++ int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
++ 			 const void *data, int len, bool wait_resp);
++ int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
+++int mtk_wed_wo_init(struct mtk_wed_hw *hw);
+++void mtk_wed_wo_deinit(struct mtk_wed_hw *hw);
+++int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *dev, struct mtk_wed_wo_queue *q,
+++			    struct sk_buff *skb);
++ 
++ #endif /* __MTK_WED_WO_H */
+diff --git a/target/linux/generic/pending-5.15/733-03-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch b/target/linux/generic/pending-5.15/733-03-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch
+new file mode 100644
+index 0000000000..ffd6bc3589
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/733-03-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch
+@@ -0,0 +1,79 @@
++From: Lorenzo Bianconi <lorenzo@kernel.org>
++Date: Sat, 5 Nov 2022 23:36:20 +0100
++Subject: [PATCH] net: ethernet: mtk_wed: rename tx_wdma array in rx_wdma
++
++Rename tx_wdma queue array in rx_wdma since this is rx side of wdma soc.
++Moreover rename mtk_wed_wdma_ring_setup routine in
++mtk_wed_wdma_rx_ring_setup()
++
++Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: David S. Miller <davem@davemloft.net>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
++@@ -253,8 +253,8 @@ mtk_wed_free_tx_rings(struct mtk_wed_dev
++ 
++ 	for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++)
++ 		mtk_wed_free_ring(dev, &dev->tx_ring[i]);
++-	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
++-		mtk_wed_free_ring(dev, &dev->tx_wdma[i]);
+++	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
+++		mtk_wed_free_ring(dev, &dev->rx_wdma[i]);
++ }
++ 
++ static void
++@@ -695,10 +695,10 @@ mtk_wed_ring_alloc(struct mtk_wed_device
++ }
++ 
++ static int
++-mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+++mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
++ {
++ 	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
++-	struct mtk_wed_ring *wdma = &dev->tx_wdma[idx];
+++	struct mtk_wed_ring *wdma = &dev->rx_wdma[idx];
++ 
++ 	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size))
++ 		return -ENOMEM;
++@@ -812,9 +812,9 @@ mtk_wed_start(struct mtk_wed_device *dev
++ {
++ 	int i;
++ 
++-	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
++-		if (!dev->tx_wdma[i].desc)
++-			mtk_wed_wdma_ring_setup(dev, i, 16);
+++	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
+++		if (!dev->rx_wdma[i].desc)
+++			mtk_wed_wdma_rx_ring_setup(dev, i, 16);
++ 
++ 	mtk_wed_hw_init(dev);
++ 	mtk_wed_configure_irq(dev, irq_mask);
++@@ -923,7 +923,7 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
++ 			       sizeof(*ring->desc)))
++ 		return -ENOMEM;
++ 
++-	if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
+++	if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
++ 		return -ENOMEM;
++ 
++ 	ring->reg_base = MTK_WED_RING_TX(idx);
++--- a/include/linux/soc/mediatek/mtk_wed.h
+++++ b/include/linux/soc/mediatek/mtk_wed.h
++@@ -7,6 +7,7 @@
++ #include <linux/pci.h>
++ 
++ #define MTK_WED_TX_QUEUES		2
+++#define MTK_WED_RX_QUEUES		2
++ 
++ struct mtk_wed_hw;
++ struct mtk_wdma_desc;
++@@ -66,7 +67,7 @@ struct mtk_wed_device {
++ 
++ 	struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
++ 	struct mtk_wed_ring txfree_ring;
++-	struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
+++	struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
++ 
++ 	struct {
++ 		int size;
+diff --git a/target/linux/generic/pending-5.15/733-04-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch b/target/linux/generic/pending-5.15/733-04-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch
+new file mode 100644
+index 0000000000..4c34d0cb33
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/733-04-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch
+@@ -0,0 +1,1521 @@
++From: Lorenzo Bianconi <lorenzo@kernel.org>
++Date: Sat, 5 Nov 2022 23:36:21 +0100
++Subject: [PATCH] net: ethernet: mtk_wed: add configure wed wo support
++
++Enable RX Wireless Ethernet Dispatch available on MT7986 Soc.
++
++Tested-by: Daniel Golle <daniel@makrotopia.org>
++Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: David S. Miller <davem@davemloft.net>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
++@@ -9,6 +9,7 @@
++ #include <linux/skbuff.h>
++ #include <linux/of_platform.h>
++ #include <linux/of_address.h>
+++#include <linux/of_reserved_mem.h>
++ #include <linux/mfd/syscon.h>
++ #include <linux/debugfs.h>
++ #include <linux/soc/mediatek/mtk_wed.h>
++@@ -23,6 +24,7 @@
++ #define MTK_WED_PKT_SIZE		1900
++ #define MTK_WED_BUF_SIZE		2048
++ #define MTK_WED_BUF_PER_PAGE		(PAGE_SIZE / 2048)
+++#define MTK_WED_RX_RING_SIZE		1536
++ 
++ #define MTK_WED_TX_RING_SIZE		2048
++ #define MTK_WED_WDMA_RING_SIZE		1024
++@@ -31,6 +33,10 @@
++ #define MTK_WED_PER_GROUP_PKT		128
++ 
++ #define MTK_WED_FBUF_SIZE		128
+++#define MTK_WED_MIOD_CNT		16
+++#define MTK_WED_FB_CMD_CNT		1024
+++#define MTK_WED_RRO_QUE_CNT		8192
+++#define MTK_WED_MIOD_ENTRY_CNT		128
++ 
++ static struct mtk_wed_hw *hw_list[2];
++ static DEFINE_MUTEX(hw_lock);
++@@ -65,12 +71,76 @@ wdma_set(struct mtk_wed_device *dev, u32
++ 	wdma_m32(dev, reg, 0, mask);
++ }
++ 
+++static void
+++wdma_clr(struct mtk_wed_device *dev, u32 reg, u32 mask)
+++{
+++	wdma_m32(dev, reg, mask, 0);
+++}
+++
+++static u32
+++wifi_r32(struct mtk_wed_device *dev, u32 reg)
+++{
+++	return readl(dev->wlan.base + reg);
+++}
+++
+++static void
+++wifi_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
+++{
+++	writel(val, dev->wlan.base + reg);
+++}
+++
++ static u32
++ mtk_wed_read_reset(struct mtk_wed_device *dev)
++ {
++ 	return wed_r32(dev, MTK_WED_RESET);
++ }
++ 
+++static u32
+++mtk_wdma_read_reset(struct mtk_wed_device *dev)
+++{
+++	return wdma_r32(dev, MTK_WDMA_GLO_CFG);
+++}
+++
+++static void
+++mtk_wdma_rx_reset(struct mtk_wed_device *dev)
+++{
+++	u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
+++	int i;
+++
+++	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN);
+++	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
+++			       !(status & mask), 0, 1000))
+++		dev_err(dev->hw->dev, "rx reset failed\n");
+++
+++	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) {
+++		if (dev->rx_wdma[i].desc)
+++			continue;
+++
+++		wdma_w32(dev,
+++			 MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
+++	}
+++}
+++
+++static void
+++mtk_wdma_tx_reset(struct mtk_wed_device *dev)
+++{
+++	u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
+++	int i;
+++
+++	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
+++	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
+++			       !(status & mask), 0, 1000))
+++		dev_err(dev->hw->dev, "tx reset failed\n");
+++
+++	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) {
+++		if (dev->tx_wdma[i].desc)
+++			continue;
+++
+++		wdma_w32(dev,
+++			 MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
+++	}
+++}
+++
++ static void
++ mtk_wed_reset(struct mtk_wed_device *dev, u32 mask)
++ {
++@@ -82,6 +152,54 @@ mtk_wed_reset(struct mtk_wed_device *dev
++ 		WARN_ON_ONCE(1);
++ }
++ 
+++static u32
+++mtk_wed_wo_read_status(struct mtk_wed_device *dev)
+++{
+++	return wed_r32(dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_WO_STATUS);
+++}
+++
+++static void
+++mtk_wed_wo_reset(struct mtk_wed_device *dev)
+++{
+++	struct mtk_wed_wo *wo = dev->hw->wed_wo;
+++	u8 state = MTK_WED_WO_STATE_DISABLE;
+++	void __iomem *reg;
+++	u32 val;
+++
+++	mtk_wdma_tx_reset(dev);
+++	mtk_wed_reset(dev, MTK_WED_RESET_WED);
+++
+++	mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
+++			     MTK_WED_WO_CMD_CHANGE_STATE, &state,
+++			     sizeof(state), false);
+++
+++	if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
+++			       val == MTK_WED_WOIF_DISABLE_DONE,
+++			       100, MTK_WOCPU_TIMEOUT))
+++		dev_err(dev->hw->dev, "failed to disable wed-wo\n");
+++
+++	reg = ioremap(MTK_WED_WO_CPU_MCUSYS_RESET_ADDR, 4);
+++
+++	val = readl(reg);
+++	switch (dev->hw->index) {
+++	case 0:
+++		val |= MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK;
+++		writel(val, reg);
+++		val &= ~MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK;
+++		writel(val, reg);
+++		break;
+++	case 1:
+++		val |= MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK;
+++		writel(val, reg);
+++		val &= ~MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK;
+++		writel(val, reg);
+++		break;
+++	default:
+++		break;
+++	}
+++	iounmap(reg);
+++}
+++
++ static struct mtk_wed_hw *
++ mtk_wed_assign(struct mtk_wed_device *dev)
++ {
++@@ -116,7 +234,7 @@ out:
++ }
++ 
++ static int
++-mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
+++mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
++ {
++ 	struct mtk_wdma_desc *desc;
++ 	dma_addr_t desc_phys;
++@@ -133,16 +251,16 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
++ 	if (!page_list)
++ 		return -ENOMEM;
++ 
++-	dev->buf_ring.size = ring_size;
++-	dev->buf_ring.pages = page_list;
+++	dev->tx_buf_ring.size = ring_size;
+++	dev->tx_buf_ring.pages = page_list;
++ 
++ 	desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
++ 				  &desc_phys, GFP_KERNEL);
++ 	if (!desc)
++ 		return -ENOMEM;
++ 
++-	dev->buf_ring.desc = desc;
++-	dev->buf_ring.desc_phys = desc_phys;
+++	dev->tx_buf_ring.desc = desc;
+++	dev->tx_buf_ring.desc_phys = desc_phys;
++ 
++ 	for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
++ 		dma_addr_t page_phys, buf_phys;
++@@ -203,10 +321,10 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
++ }
++ 
++ static void
++-mtk_wed_free_buffer(struct mtk_wed_device *dev)
+++mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
++ {
++-	struct mtk_wdma_desc *desc = dev->buf_ring.desc;
++-	void **page_list = dev->buf_ring.pages;
+++	struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
+++	void **page_list = dev->tx_buf_ring.pages;
++ 	int page_idx;
++ 	int i;
++ 
++@@ -216,7 +334,8 @@ mtk_wed_free_buffer(struct mtk_wed_devic
++ 	if (!desc)
++ 		goto free_pagelist;
++ 
++-	for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
+++	for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
+++	     i += MTK_WED_BUF_PER_PAGE) {
++ 		void *page = page_list[page_idx++];
++ 		dma_addr_t buf_addr;
++ 
++@@ -229,13 +348,59 @@ mtk_wed_free_buffer(struct mtk_wed_devic
++ 		__free_page(page);
++ 	}
++ 
++-	dma_free_coherent(dev->hw->dev, dev->buf_ring.size * sizeof(*desc),
++-			  desc, dev->buf_ring.desc_phys);
+++	dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc),
+++			  desc, dev->tx_buf_ring.desc_phys);
++ 
++ free_pagelist:
++ 	kfree(page_list);
++ }
++ 
+++static int
+++mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
+++{
+++	struct mtk_rxbm_desc *desc;
+++	dma_addr_t desc_phys;
+++
+++	dev->rx_buf_ring.size = dev->wlan.rx_nbuf;
+++	desc = dma_alloc_coherent(dev->hw->dev,
+++				  dev->wlan.rx_nbuf * sizeof(*desc),
+++				  &desc_phys, GFP_KERNEL);
+++	if (!desc)
+++		return -ENOMEM;
+++
+++	dev->rx_buf_ring.desc = desc;
+++	dev->rx_buf_ring.desc_phys = desc_phys;
+++	dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt);
+++
+++	return 0;
+++}
+++
+++static void
+++mtk_wed_free_rx_buffer(struct mtk_wed_device *dev)
+++{
+++	struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
+++
+++	if (!desc)
+++		return;
+++
+++	dev->wlan.release_rx_buf(dev);
+++	dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc),
+++			  desc, dev->rx_buf_ring.desc_phys);
+++}
+++
+++static void
+++mtk_wed_rx_buffer_hw_init(struct mtk_wed_device *dev)
+++{
+++	wed_w32(dev, MTK_WED_RX_BM_RX_DMAD,
+++		FIELD_PREP(MTK_WED_RX_BM_RX_DMAD_SDL0, dev->wlan.rx_size));
+++	wed_w32(dev, MTK_WED_RX_BM_BASE, dev->rx_buf_ring.desc_phys);
+++	wed_w32(dev, MTK_WED_RX_BM_INIT_PTR, MTK_WED_RX_BM_INIT_SW_TAIL |
+++		FIELD_PREP(MTK_WED_RX_BM_SW_TAIL, dev->wlan.rx_npkt));
+++	wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH,
+++		FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff));
+++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
+++}
+++
++ static void
++ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring)
++ {
++@@ -247,6 +412,13 @@ mtk_wed_free_ring(struct mtk_wed_device
++ }
++ 
++ static void
+++mtk_wed_free_rx_rings(struct mtk_wed_device *dev)
+++{
+++	mtk_wed_free_rx_buffer(dev);
+++	mtk_wed_free_ring(dev, &dev->rro.ring);
+++}
+++
+++static void
++ mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
++ {
++ 	int i;
++@@ -291,6 +463,38 @@ mtk_wed_set_512_support(struct mtk_wed_d
++ 	}
++ }
++ 
+++#define MTK_WFMDA_RX_DMA_EN	BIT(2)
+++static void
+++mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
+++{
+++	u32 val;
+++	int i;
+++
+++	if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED))
+++		return; /* queue is not configured by mt76 */
+++
+++	for (i = 0; i < 3; i++) {
+++		u32 cur_idx;
+++
+++		cur_idx = wed_r32(dev,
+++				  MTK_WED_WPDMA_RING_RX_DATA(idx) +
+++				  MTK_WED_RING_OFS_CPU_IDX);
+++		if (cur_idx == MTK_WED_RX_RING_SIZE - 1)
+++			break;
+++
+++		usleep_range(100000, 200000);
+++	}
+++
+++	if (i == 3) {
+++		dev_err(dev->hw->dev, "rx dma enable failed\n");
+++		return;
+++	}
+++
+++	val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) |
+++	      MTK_WFMDA_RX_DMA_EN;
+++	wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val);
+++}
+++
++ static void
++ mtk_wed_dma_disable(struct mtk_wed_device *dev)
++ {
++@@ -304,20 +508,25 @@ mtk_wed_dma_disable(struct mtk_wed_devic
++ 		MTK_WED_GLO_CFG_TX_DMA_EN |
++ 		MTK_WED_GLO_CFG_RX_DMA_EN);
++ 
++-	wdma_m32(dev, MTK_WDMA_GLO_CFG,
+++	wdma_clr(dev, MTK_WDMA_GLO_CFG,
++ 		 MTK_WDMA_GLO_CFG_TX_DMA_EN |
++ 		 MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
++-		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0);
+++		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
++ 
++ 	if (dev->hw->version == 1) {
++ 		regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
++-		wdma_m32(dev, MTK_WDMA_GLO_CFG,
++-			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0);
+++		wdma_clr(dev, MTK_WDMA_GLO_CFG,
+++			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++ 	} else {
++ 		wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
++ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
++ 
+++		wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+++			MTK_WED_WPDMA_RX_D_RX_DRV_EN);
+++		wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
+++			MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
+++
++ 		mtk_wed_set_512_support(dev, false);
++ 	}
++ }
++@@ -338,6 +547,13 @@ mtk_wed_stop(struct mtk_wed_device *dev)
++ 	wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
++ 	wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
++ 	wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0);
+++
+++	if (dev->hw->version == 1)
+++		return;
+++
+++	wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
+++	wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
+++	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
++ }
++ 
++ static void
++@@ -353,11 +569,21 @@ mtk_wed_detach(struct mtk_wed_device *de
++ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
++ 
++ 	mtk_wed_reset(dev, MTK_WED_RESET_WED);
+++	if (mtk_wed_get_rx_capa(dev)) {
+++		wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
+++		wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
+++		wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+++	}
++ 
++-	mtk_wed_free_buffer(dev);
+++	mtk_wed_free_tx_buffer(dev);
++ 	mtk_wed_free_tx_rings(dev);
++-	if (hw->version != 1)
+++
+++	if (mtk_wed_get_rx_capa(dev)) {
+++		mtk_wed_wo_reset(dev);
+++		mtk_wed_free_rx_rings(dev);
++ 		mtk_wed_wo_deinit(hw);
+++		mtk_wdma_rx_reset(dev);
+++	}
++ 
++ 	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
++ 		struct device_node *wlan_node;
++@@ -441,10 +667,12 @@ mtk_wed_set_wpdma(struct mtk_wed_device
++ 	} else {
++ 		mtk_wed_bus_init(dev);
++ 
++-		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_int);
++-		wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK,  dev->wlan.wpdma_mask);
++-		wed_w32(dev, MTK_WED_WPDMA_CFG_TX,  dev->wlan.wpdma_tx);
++-		wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE,  dev->wlan.wpdma_txfree);
+++		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
+++		wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
+++		wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
+++		wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
+++		wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
+++		wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
++ 	}
++ }
++ 
++@@ -494,6 +722,132 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
++ 	}
++ }
++ 
+++static int
+++mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
+++		       int size)
+++{
+++	ring->desc = dma_alloc_coherent(dev->hw->dev,
+++					size * sizeof(*ring->desc),
+++					&ring->desc_phys, GFP_KERNEL);
+++	if (!ring->desc)
+++		return -ENOMEM;
+++
+++	ring->desc_size = sizeof(*ring->desc);
+++	ring->size = size;
+++	memset(ring->desc, 0, size);
+++
+++	return 0;
+++}
+++
+++#define MTK_WED_MIOD_COUNT	(MTK_WED_MIOD_ENTRY_CNT * MTK_WED_MIOD_CNT)
+++static int
+++mtk_wed_rro_alloc(struct mtk_wed_device *dev)
+++{
+++	struct reserved_mem *rmem;
+++	struct device_node *np;
+++	int index;
+++
+++	index = of_property_match_string(dev->hw->node, "memory-region-names",
+++					 "wo-dlm");
+++	if (index < 0)
+++		return index;
+++
+++	np = of_parse_phandle(dev->hw->node, "memory-region", index);
+++	if (!np)
+++		return -ENODEV;
+++
+++	rmem = of_reserved_mem_lookup(np);
+++	of_node_put(np);
+++
+++	if (!rmem)
+++		return -ENODEV;
+++
+++	dev->rro.miod_phys = rmem->base;
+++	dev->rro.fdbk_phys = MTK_WED_MIOD_COUNT + dev->rro.miod_phys;
+++
+++	return mtk_wed_rro_ring_alloc(dev, &dev->rro.ring,
+++				      MTK_WED_RRO_QUE_CNT);
+++}
+++
+++static int
+++mtk_wed_rro_cfg(struct mtk_wed_device *dev)
+++{
+++	struct mtk_wed_wo *wo = dev->hw->wed_wo;
+++	struct {
+++		struct {
+++			__le32 base;
+++			__le32 cnt;
+++			__le32 unit;
+++		} ring[2];
+++		__le32 wed;
+++		u8 version;
+++	} req = {
+++		.ring[0] = {
+++			.base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE),
+++			.cnt = cpu_to_le32(MTK_WED_MIOD_CNT),
+++			.unit = cpu_to_le32(MTK_WED_MIOD_ENTRY_CNT),
+++		},
+++		.ring[1] = {
+++			.base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE +
+++					    MTK_WED_MIOD_COUNT),
+++			.cnt = cpu_to_le32(MTK_WED_FB_CMD_CNT),
+++			.unit = cpu_to_le32(4),
+++		},
+++	};
+++
+++	return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
+++				    MTK_WED_WO_CMD_WED_CFG,
+++				    &req, sizeof(req), true);
+++}
+++
+++static void
+++mtk_wed_rro_hw_init(struct mtk_wed_device *dev)
+++{
+++	wed_w32(dev, MTK_WED_RROQM_MIOD_CFG,
+++		FIELD_PREP(MTK_WED_RROQM_MIOD_MID_DW, 0x70 >> 2) |
+++		FIELD_PREP(MTK_WED_RROQM_MIOD_MOD_DW, 0x10 >> 2) |
+++		FIELD_PREP(MTK_WED_RROQM_MIOD_ENTRY_DW,
+++			   MTK_WED_MIOD_ENTRY_CNT >> 2));
+++
+++	wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL0, dev->rro.miod_phys);
+++	wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL1,
+++		FIELD_PREP(MTK_WED_RROQM_MIOD_CNT, MTK_WED_MIOD_CNT));
+++	wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL0, dev->rro.fdbk_phys);
+++	wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL1,
+++		FIELD_PREP(MTK_WED_RROQM_FDBK_CNT, MTK_WED_FB_CMD_CNT));
+++	wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL2, 0);
+++	wed_w32(dev, MTK_WED_RROQ_BASE_L, dev->rro.ring.desc_phys);
+++
+++	wed_set(dev, MTK_WED_RROQM_RST_IDX,
+++		MTK_WED_RROQM_RST_IDX_MIOD |
+++		MTK_WED_RROQM_RST_IDX_FDBK);
+++
+++	wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
+++	wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL2, MTK_WED_MIOD_CNT - 1);
+++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN);
+++}
+++
+++static void
+++mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
+++{
+++	wed_w32(dev, MTK_WED_RESET, MTK_WED_RESET_RX_ROUTE_QM);
+++
+++	for (;;) {
+++		usleep_range(100, 200);
+++		if (!(wed_r32(dev, MTK_WED_RESET) & MTK_WED_RESET_RX_ROUTE_QM))
+++			break;
+++	}
+++
+++	/* configure RX_ROUTE_QM */
+++	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
+++	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
+++	wed_set(dev, MTK_WED_RTQM_GLO_CFG,
+++		FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
+++	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
+++	/* enable RX_ROUTE_QM */
+++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
+++}
+++
++ static void
++ mtk_wed_hw_init(struct mtk_wed_device *dev)
++ {
++@@ -505,11 +859,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d
++ 	wed_w32(dev, MTK_WED_TX_BM_CTRL,
++ 		MTK_WED_TX_BM_CTRL_PAUSE |
++ 		FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
++-			   dev->buf_ring.size / 128) |
+++			   dev->tx_buf_ring.size / 128) |
++ 		FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
++ 			   MTK_WED_TX_RING_SIZE / 256));
++ 
++-	wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys);
+++	wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys);
++ 
++ 	wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
++ 
++@@ -536,9 +890,9 @@ mtk_wed_hw_init(struct mtk_wed_device *d
++ 		wed_w32(dev, MTK_WED_TX_TKID_CTRL,
++ 			MTK_WED_TX_TKID_CTRL_PAUSE |
++ 			FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM,
++-				   dev->buf_ring.size / 128) |
+++				   dev->tx_buf_ring.size / 128) |
++ 			FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
++-				   dev->buf_ring.size / 128));
+++				   dev->tx_buf_ring.size / 128));
++ 		wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
++ 			FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
++ 			MTK_WED_TX_TKID_DYN_THR_HI);
++@@ -546,18 +900,28 @@ mtk_wed_hw_init(struct mtk_wed_device *d
++ 
++ 	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
++ 
++-	if (dev->hw->version == 1)
+++	if (dev->hw->version == 1) {
++ 		wed_set(dev, MTK_WED_CTRL,
++ 			MTK_WED_CTRL_WED_TX_BM_EN |
++ 			MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++-	else
+++	} else {
++ 		wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+++		/* rx hw init */
+++		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
+++			MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
+++			MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
+++		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
+++
+++		mtk_wed_rx_buffer_hw_init(dev);
+++		mtk_wed_rro_hw_init(dev);
+++		mtk_wed_route_qm_hw_init(dev);
+++	}
++ 
++ 	wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
++ }
++ 
++ static void
++-mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size)
+++mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx)
++ {
++ 	void *head = (void *)ring->desc;
++ 	int i;
++@@ -567,7 +931,10 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
++ 
++ 		desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size);
++ 		desc->buf0 = 0;
++-		desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
+++		if (tx)
+++			desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
+++		else
+++			desc->ctrl = cpu_to_le32(MTK_WFDMA_DESC_CTRL_TO_HOST);
++ 		desc->buf1 = 0;
++ 		desc->info = 0;
++ 	}
++@@ -623,7 +990,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
++ 		if (!dev->tx_ring[i].desc)
++ 			continue;
++ 
++-		mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE);
+++		mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE,
+++				   true);
++ 	}
++ 
++ 	if (mtk_wed_poll_busy(dev))
++@@ -641,6 +1009,9 @@ mtk_wed_reset_dma(struct mtk_wed_device
++ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
++ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
++ 
+++	if (mtk_wed_get_rx_capa(dev))
+++		mtk_wdma_rx_reset(dev);
+++
++ 	if (busy) {
++ 		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
++ 		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
++@@ -675,12 +1046,11 @@ mtk_wed_reset_dma(struct mtk_wed_device
++ 			MTK_WED_WPDMA_RESET_IDX_RX);
++ 		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0);
++ 	}
++-
++ }
++ 
++ static int
++ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
++-		   int size, u32 desc_size)
+++		   int size, u32 desc_size, bool tx)
++ {
++ 	ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size,
++ 					&ring->desc_phys, GFP_KERNEL);
++@@ -689,7 +1059,7 @@ mtk_wed_ring_alloc(struct mtk_wed_device
++ 
++ 	ring->desc_size = desc_size;
++ 	ring->size = size;
++-	mtk_wed_ring_reset(ring, size);
+++	mtk_wed_ring_reset(ring, size, tx);
++ 
++ 	return 0;
++ }
++@@ -698,9 +1068,14 @@ static int
++ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
++ {
++ 	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
++-	struct mtk_wed_ring *wdma = &dev->rx_wdma[idx];
+++	struct mtk_wed_ring *wdma;
++ 
++-	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size))
+++	if (idx >= ARRAY_SIZE(dev->rx_wdma))
+++		return -EINVAL;
+++
+++	wdma = &dev->rx_wdma[idx];
+++	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
+++			       true))
++ 		return -ENOMEM;
++ 
++ 	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
++@@ -717,6 +1092,60 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
++ 	return 0;
++ }
++ 
+++static int
+++mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+++{
+++	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+++	struct mtk_wed_ring *wdma;
+++
+++	if (idx >= ARRAY_SIZE(dev->tx_wdma))
+++		return -EINVAL;
+++
+++	wdma = &dev->tx_wdma[idx];
+++	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
+++			       true))
+++		return -ENOMEM;
+++
+++	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
+++		 wdma->desc_phys);
+++	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
+++		 size);
+++	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
+++	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
+++
+++	if (!idx)  {
+++		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE,
+++			wdma->desc_phys);
+++		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_COUNT,
+++			size);
+++		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_CPU_IDX,
+++			0);
+++		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_DMA_IDX,
+++			0);
+++	}
+++
+++	return 0;
+++}
+++
+++static void
+++mtk_wed_ppe_check(struct mtk_wed_device *dev, struct sk_buff *skb,
+++		  u32 reason, u32 hash)
+++{
+++	struct mtk_eth *eth = dev->hw->eth;
+++	struct ethhdr *eh;
+++
+++	if (!skb)
+++		return;
+++
+++	if (reason != MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+++		return;
+++
+++	skb_set_mac_header(skb, 0);
+++	eh = eth_hdr(skb);
+++	skb->protocol = eh->h_proto;
+++	mtk_ppe_check_skb(eth->ppe[dev->hw->index], skb, hash);
+++}
+++
++ static void
++ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
++ {
++@@ -739,6 +1168,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev
++ 
++ 		wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
++ 	} else {
+++		wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
+++					GENMASK(1, 0));
++ 		/* initail tx interrupt trigger */
++ 		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
++ 			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
++@@ -757,6 +1188,16 @@ mtk_wed_configure_irq(struct mtk_wed_dev
++ 			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
++ 				   dev->wlan.txfree_tbit));
++ 
+++		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
+++			MTK_WED_WPDMA_INT_CTRL_RX0_EN |
+++			MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
+++			MTK_WED_WPDMA_INT_CTRL_RX1_EN |
+++			MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
+++			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
+++				   dev->wlan.rx_tbit[0]) |
+++			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
+++				   dev->wlan.rx_tbit[1]));
+++
++ 		wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask);
++ 		wed_set(dev, MTK_WED_WDMA_INT_CTRL,
++ 			FIELD_PREP(MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL,
++@@ -794,9 +1235,15 @@ mtk_wed_dma_enable(struct mtk_wed_device
++ 		wdma_set(dev, MTK_WDMA_GLO_CFG,
++ 			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++ 	} else {
+++		int i;
+++
++ 		wed_set(dev, MTK_WED_WPDMA_CTRL,
++ 			MTK_WED_WPDMA_CTRL_SDL1_FIXED);
++ 
+++		wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+++			MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
+++			MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
+++
++ 		wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
++ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
++@@ -804,6 +1251,15 @@ mtk_wed_dma_enable(struct mtk_wed_device
++ 		wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++ 			MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
++ 			MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
+++
+++		wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+++			MTK_WED_WPDMA_RX_D_RX_DRV_EN |
+++			FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
+++			FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
+++				   0x2));
+++
+++		for (i = 0; i < MTK_WED_RX_QUEUES; i++)
+++			mtk_wed_check_wfdma_rx_fill(dev, i);
++ 	}
++ }
++ 
++@@ -829,7 +1285,19 @@ mtk_wed_start(struct mtk_wed_device *dev
++ 		val |= BIT(0) | (BIT(1) * !!dev->hw->index);
++ 		regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
++ 	} else {
++-		mtk_wed_set_512_support(dev, true);
+++		/* driver set mid ready and only once */
+++		wed_w32(dev, MTK_WED_EXT_INT_MASK1,
+++			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
+++		wed_w32(dev, MTK_WED_EXT_INT_MASK2,
+++			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
+++
+++		wed_r32(dev, MTK_WED_EXT_INT_MASK1);
+++		wed_r32(dev, MTK_WED_EXT_INT_MASK2);
+++
+++		if (mtk_wed_rro_cfg(dev))
+++			return;
+++
+++		mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
++ 	}
++ 
++ 	mtk_wed_dma_enable(dev);
++@@ -863,7 +1331,7 @@ mtk_wed_attach(struct mtk_wed_device *de
++ 	if (!hw) {
++ 		module_put(THIS_MODULE);
++ 		ret = -ENODEV;
++-		goto out;
+++		goto unlock;
++ 	}
++ 
++ 	device = dev->wlan.bus_type == MTK_WED_BUS_PCIE
++@@ -876,15 +1344,24 @@ mtk_wed_attach(struct mtk_wed_device *de
++ 	dev->dev = hw->dev;
++ 	dev->irq = hw->irq;
++ 	dev->wdma_idx = hw->index;
+++	dev->version = hw->version;
++ 
++ 	if (hw->eth->dma_dev == hw->eth->dev &&
++ 	    of_dma_is_coherent(hw->eth->dev->of_node))
++ 		mtk_eth_set_dma_device(hw->eth, hw->dev);
++ 
++-	ret = mtk_wed_buffer_alloc(dev);
++-	if (ret) {
++-		mtk_wed_detach(dev);
+++	ret = mtk_wed_tx_buffer_alloc(dev);
+++	if (ret)
++ 		goto out;
+++
+++	if (mtk_wed_get_rx_capa(dev)) {
+++		ret = mtk_wed_rx_buffer_alloc(dev);
+++		if (ret)
+++			goto out;
+++
+++		ret = mtk_wed_rro_alloc(dev);
+++		if (ret)
+++			goto out;
++ 	}
++ 
++ 	mtk_wed_hw_init_early(dev);
++@@ -893,8 +1370,10 @@ mtk_wed_attach(struct mtk_wed_device *de
++ 				   BIT(hw->index), 0);
++ 	else
++ 		ret = mtk_wed_wo_init(hw);
++-
++ out:
+++	if (ret)
+++		mtk_wed_detach(dev);
+++unlock:
++ 	mutex_unlock(&hw_lock);
++ 
++ 	return ret;
++@@ -917,10 +1396,11 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
++ 	 * WDMA RX.
++ 	 */
++ 
++-	BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring));
+++	if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring)))
+++		return -EINVAL;
++ 
++ 	if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
++-			       sizeof(*ring->desc)))
+++			       sizeof(*ring->desc), true))
++ 		return -ENOMEM;
++ 
++ 	if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
++@@ -967,6 +1447,37 @@ mtk_wed_txfree_ring_setup(struct mtk_wed
++ 	return 0;
++ }
++ 
+++static int
+++mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
+++{
+++	struct mtk_wed_ring *ring = &dev->rx_ring[idx];
+++
+++	if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring)))
+++		return -EINVAL;
+++
+++	if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE,
+++			       sizeof(*ring->desc), false))
+++		return -ENOMEM;
+++
+++	if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
+++		return -ENOMEM;
+++
+++	ring->reg_base = MTK_WED_RING_RX_DATA(idx);
+++	ring->wpdma = regs;
+++	ring->flags |= MTK_WED_RING_CONFIGURED;
+++
+++	/* WPDMA ->  WED */
+++	wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
+++	wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_RX_RING_SIZE);
+++
+++	wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_BASE,
+++		ring->desc_phys);
+++	wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_COUNT,
+++		MTK_WED_RX_RING_SIZE);
+++
+++	return 0;
+++}
+++
++ static u32
++ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
++ {
++@@ -1063,7 +1574,9 @@ void mtk_wed_add_hw(struct device_node *
++ 	static const struct mtk_wed_ops wed_ops = {
++ 		.attach = mtk_wed_attach,
++ 		.tx_ring_setup = mtk_wed_tx_ring_setup,
+++		.rx_ring_setup = mtk_wed_rx_ring_setup,
++ 		.txfree_ring_setup = mtk_wed_txfree_ring_setup,
+++		.msg_update = mtk_wed_mcu_msg_update,
++ 		.start = mtk_wed_start,
++ 		.stop = mtk_wed_stop,
++ 		.reset_dma = mtk_wed_reset_dma,
++@@ -1072,6 +1585,7 @@ void mtk_wed_add_hw(struct device_node *
++ 		.irq_get = mtk_wed_irq_get,
++ 		.irq_set_mask = mtk_wed_irq_set_mask,
++ 		.detach = mtk_wed_detach,
+++		.ppe_check = mtk_wed_ppe_check,
++ 	};
++ 	struct device_node *eth_np = eth->dev->of_node;
++ 	struct platform_device *pdev;
++--- a/drivers/net/ethernet/mediatek/mtk_wed.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
++@@ -87,6 +87,24 @@ wpdma_tx_w32(struct mtk_wed_device *dev,
++ }
++ 
++ static inline u32
+++wpdma_rx_r32(struct mtk_wed_device *dev, int ring, u32 reg)
+++{
+++	if (!dev->rx_ring[ring].wpdma)
+++		return 0;
+++
+++	return readl(dev->rx_ring[ring].wpdma + reg);
+++}
+++
+++static inline void
+++wpdma_rx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val)
+++{
+++	if (!dev->rx_ring[ring].wpdma)
+++		return;
+++
+++	writel(val, dev->rx_ring[ring].wpdma + reg);
+++}
+++
+++static inline u32
++ wpdma_txfree_r32(struct mtk_wed_device *dev, u32 reg)
++ {
++ 	if (!dev->txfree_ring.wpdma)
++@@ -128,6 +146,7 @@ static inline int mtk_wed_flow_add(int i
++ static inline void mtk_wed_flow_remove(int index)
++ {
++ }
+++
++ #endif
++ 
++ #ifdef CONFIG_DEBUG_FS
++--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++@@ -10,6 +10,7 @@
++ #include <linux/of_reserved_mem.h>
++ #include <linux/mfd/syscon.h>
++ #include <linux/soc/mediatek/mtk_wed.h>
+++#include <asm/unaligned.h>
++ 
++ #include "mtk_wed_regs.h"
++ #include "mtk_wed_wo.h"
++@@ -60,24 +61,37 @@ void mtk_wed_mcu_rx_event(struct mtk_wed
++ 	wake_up(&wo->mcu.wait);
++ }
++ 
+++static void
+++mtk_wed_update_rx_stats(struct mtk_wed_device *wed, struct sk_buff *skb)
+++{
+++	u32 count = get_unaligned_le32(skb->data);
+++	struct mtk_wed_wo_rx_stats *stats;
+++	int i;
+++
+++	if (count * sizeof(*stats) > skb->len - sizeof(u32))
+++		return;
+++
+++	stats = (struct mtk_wed_wo_rx_stats *)(skb->data + sizeof(u32));
+++	for (i = 0 ; i < count ; i++)
+++		wed->wlan.update_wo_rx_stats(wed, &stats[i]);
+++}
+++
++ void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
++ 				      struct sk_buff *skb)
++ {
++ 	struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
++ 
++-	switch (hdr->cmd) {
++-	case MTK_WED_WO_EVT_LOG_DUMP: {
++-		const char *msg = (const char *)(skb->data + sizeof(*hdr));
+++	skb_pull(skb, sizeof(*hdr));
++ 
++-		dev_notice(wo->hw->dev, "%s\n", msg);
+++	switch (hdr->cmd) {
+++	case MTK_WED_WO_EVT_LOG_DUMP:
+++		dev_notice(wo->hw->dev, "%s\n", skb->data);
++ 		break;
++-	}
++ 	case MTK_WED_WO_EVT_PROFILING: {
++-		struct mtk_wed_wo_log_info *info;
++-		u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info);
+++		struct mtk_wed_wo_log_info *info = (void *)skb->data;
+++		u32 count = skb->len / sizeof(*info);
++ 		int i;
++ 
++-		info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr));
++ 		for (i = 0 ; i < count ; i++)
++ 			dev_notice(wo->hw->dev,
++ 				   "SN:%u latency: total=%u, rro:%u, mod:%u\n",
++@@ -88,6 +102,7 @@ void mtk_wed_mcu_rx_unsolicited_event(st
++ 		break;
++ 	}
++ 	case MTK_WED_WO_EVT_RXCNT_INFO:
+++		mtk_wed_update_rx_stats(wo->hw->wed_dev, skb);
++ 		break;
++ 	default:
++ 		break;
++@@ -144,6 +159,8 @@ mtk_wed_mcu_parse_response(struct mtk_we
++ 	skb_pull(skb, sizeof(*hdr));
++ 	switch (cmd) {
++ 	case MTK_WED_WO_CMD_RXCNT_INFO:
+++		mtk_wed_update_rx_stats(wo->hw->wed_dev, skb);
+++		break;
++ 	default:
++ 		break;
++ 	}
++@@ -182,6 +199,18 @@ unlock:
++ 	return ret;
++ }
++ 
+++int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data,
+++			   int len)
+++{
+++	struct mtk_wed_wo *wo = dev->hw->wed_wo;
+++
+++	if (dev->hw->version == 1)
+++		return 0;
+++
+++	return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len,
+++				    true);
+++}
+++
++ static int
++ mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
++ 			  struct mtk_wed_wo_memory_region *region)
++--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++@@ -4,6 +4,7 @@
++ #ifndef __MTK_WED_REGS_H
++ #define __MTK_WED_REGS_H
++ 
+++#define MTK_WFDMA_DESC_CTRL_TO_HOST		BIT(8)
++ #define MTK_WDMA_DESC_CTRL_LEN1			GENMASK(14, 0)
++ #define MTK_WDMA_DESC_CTRL_LEN1_V2		GENMASK(13, 0)
++ #define MTK_WDMA_DESC_CTRL_LAST_SEG1		BIT(15)
++@@ -28,6 +29,8 @@ struct mtk_wdma_desc {
++ #define MTK_WED_RESET_WED_TX_DMA			BIT(12)
++ #define MTK_WED_RESET_WDMA_RX_DRV			BIT(17)
++ #define MTK_WED_RESET_WDMA_INT_AGENT			BIT(19)
+++#define MTK_WED_RESET_RX_RRO_QM				BIT(20)
+++#define MTK_WED_RESET_RX_ROUTE_QM			BIT(21)
++ #define MTK_WED_RESET_WED				BIT(31)
++ 
++ #define MTK_WED_CTRL					0x00c
++@@ -39,8 +42,12 @@ struct mtk_wdma_desc {
++ #define MTK_WED_CTRL_WED_TX_BM_BUSY			BIT(9)
++ #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN		BIT(10)
++ #define MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY		BIT(11)
++-#define MTK_WED_CTRL_RESERVE_EN				BIT(12)
++-#define MTK_WED_CTRL_RESERVE_BUSY			BIT(13)
+++#define MTK_WED_CTRL_WED_RX_BM_EN			BIT(12)
+++#define MTK_WED_CTRL_WED_RX_BM_BUSY			BIT(13)
+++#define MTK_WED_CTRL_RX_RRO_QM_EN			BIT(14)
+++#define MTK_WED_CTRL_RX_RRO_QM_BUSY			BIT(15)
+++#define MTK_WED_CTRL_RX_ROUTE_QM_EN			BIT(16)
+++#define MTK_WED_CTRL_RX_ROUTE_QM_BUSY			BIT(17)
++ #define MTK_WED_CTRL_FINAL_DIDX_READ			BIT(24)
++ #define MTK_WED_CTRL_ETH_DMAD_FMT			BIT(25)
++ #define MTK_WED_CTRL_MIB_READ_CLEAR			BIT(28)
++@@ -62,6 +69,9 @@ struct mtk_wdma_desc {
++ #define MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR	BIT(22)
++ #define MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR	BIT(23)
++ #define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE	BIT(24)
+++#define MTK_WED_EXT_INT_STATUS_RX_DRV_GET_BM_DMAD_SKIP	BIT(25)
+++#define MTK_WED_EXT_INT_STATUS_WPDMA_RX_D_DRV_ERR	BIT(26)
+++#define MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY		BIT(27)
++ #define MTK_WED_EXT_INT_STATUS_ERROR_MASK		(MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \
++ 							 MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \
++ 							 MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \
++@@ -71,6 +81,8 @@ struct mtk_wdma_desc {
++ 							 MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR)
++ 
++ #define MTK_WED_EXT_INT_MASK				0x028
+++#define MTK_WED_EXT_INT_MASK1				0x02c
+++#define MTK_WED_EXT_INT_MASK2				0x030
++ 
++ #define MTK_WED_STATUS					0x060
++ #define MTK_WED_STATUS_TX				GENMASK(15, 8)
++@@ -151,6 +163,7 @@ struct mtk_wdma_desc {
++ #define MTK_WED_RING_TX(_n)				(0x300 + (_n) * 0x10)
++ 
++ #define MTK_WED_RING_RX(_n)				(0x400 + (_n) * 0x10)
+++#define MTK_WED_RING_RX_DATA(_n)			(0x420 + (_n) * 0x10)
++ 
++ #define MTK_WED_SCR0					0x3c0
++ #define MTK_WED_WPDMA_INT_TRIGGER			0x504
++@@ -213,6 +226,12 @@ struct mtk_wdma_desc {
++ #define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG		GENMASK(14, 10)
++ 
++ #define MTK_WED_WPDMA_INT_CTRL_RX			0x534
+++#define MTK_WED_WPDMA_INT_CTRL_RX0_EN			BIT(0)
+++#define MTK_WED_WPDMA_INT_CTRL_RX0_CLR			BIT(1)
+++#define MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG		GENMASK(6, 2)
+++#define MTK_WED_WPDMA_INT_CTRL_RX1_EN			BIT(8)
+++#define MTK_WED_WPDMA_INT_CTRL_RX1_CLR			BIT(9)
+++#define MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG		GENMASK(14, 10)
++ 
++ #define MTK_WED_WPDMA_INT_CTRL_TX_FREE			0x538
++ #define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN		BIT(0)
++@@ -242,11 +261,34 @@ struct mtk_wdma_desc {
++ 
++ #define MTK_WED_WPDMA_RING_TX(_n)			(0x600 + (_n) * 0x10)
++ #define MTK_WED_WPDMA_RING_RX(_n)			(0x700 + (_n) * 0x10)
+++#define MTK_WED_WPDMA_RING_RX_DATA(_n)			(0x730 + (_n) * 0x10)
+++
+++#define MTK_WED_WPDMA_RX_D_GLO_CFG			0x75c
+++#define MTK_WED_WPDMA_RX_D_RX_DRV_EN			BIT(0)
+++#define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL		GENMASK(11, 7)
+++#define MTK_WED_WPDMA_RX_D_RXD_READ_LEN			GENMASK(31, 24)
+++
+++#define MTK_WED_WPDMA_RX_D_RST_IDX			0x760
+++#define MTK_WED_WPDMA_RX_D_RST_CRX_IDX			GENMASK(17, 16)
+++#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24)
+++
+++#define MTK_WED_WPDMA_RX_GLO_CFG			0x76c
+++#define MTK_WED_WPDMA_RX_RING				0x770
+++
+++#define MTK_WED_WPDMA_RX_D_MIB(_n)			(0x774 + (_n) * 4)
+++#define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n)		(0x784 + (_n) * 4)
+++#define MTK_WED_WPDMA_RX_D_COHERENT_MIB			0x78c
+++
+++#define MTK_WED_WDMA_RING_TX				0x800
+++
+++#define MTK_WED_WDMA_TX_MIB				0x810
+++
++ #define MTK_WED_WDMA_RING_RX(_n)			(0x900 + (_n) * 0x10)
++ #define MTK_WED_WDMA_RX_THRES(_n)			(0x940 + (_n) * 0x4)
++ 
++ #define MTK_WED_WDMA_GLO_CFG				0xa04
++ #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN			BIT(0)
+++#define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK		BIT(1)
++ #define MTK_WED_WDMA_GLO_CFG_RX_DRV_EN			BIT(2)
++ #define MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY		BIT(3)
++ #define MTK_WED_WDMA_GLO_CFG_BT_SIZE			GENMASK(5, 4)
++@@ -291,6 +333,20 @@ struct mtk_wdma_desc {
++ #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n)			(0xae8 + (_n) * 4)
++ #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n)		(0xaf0 + (_n) * 4)
++ 
+++#define MTK_WED_RX_BM_RX_DMAD				0xd80
+++#define MTK_WED_RX_BM_RX_DMAD_SDL0			GENMASK(13, 0)
+++
+++#define MTK_WED_RX_BM_BASE				0xd84
+++#define MTK_WED_RX_BM_INIT_PTR				0xd88
+++#define MTK_WED_RX_BM_SW_TAIL				GENMASK(15, 0)
+++#define MTK_WED_RX_BM_INIT_SW_TAIL			BIT(16)
+++
+++#define MTK_WED_RX_PTR					0xd8c
+++
+++#define MTK_WED_RX_BM_DYN_ALLOC_TH			0xdb4
+++#define MTK_WED_RX_BM_DYN_ALLOC_TH_H			GENMASK(31, 16)
+++#define MTK_WED_RX_BM_DYN_ALLOC_TH_L			GENMASK(15, 0)
+++
++ #define MTK_WED_RING_OFS_BASE				0x00
++ #define MTK_WED_RING_OFS_COUNT				0x04
++ #define MTK_WED_RING_OFS_CPU_IDX			0x08
++@@ -301,7 +357,9 @@ struct mtk_wdma_desc {
++ 
++ #define MTK_WDMA_GLO_CFG				0x204
++ #define MTK_WDMA_GLO_CFG_TX_DMA_EN			BIT(0)
+++#define MTK_WDMA_GLO_CFG_TX_DMA_BUSY			BIT(1)
++ #define MTK_WDMA_GLO_CFG_RX_DMA_EN			BIT(2)
+++#define MTK_WDMA_GLO_CFG_RX_DMA_BUSY			BIT(3)
++ #define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES		BIT(26)
++ #define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES		BIT(27)
++ #define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES		BIT(28)
++@@ -330,4 +388,70 @@ struct mtk_wdma_desc {
++ /* DMA channel mapping */
++ #define HIFSYS_DMA_AG_MAP				0x008
++ 
+++#define MTK_WED_RTQM_GLO_CFG				0xb00
+++#define MTK_WED_RTQM_BUSY				BIT(1)
+++#define MTK_WED_RTQM_Q_RST				BIT(2)
+++#define MTK_WED_RTQM_Q_DBG_BYPASS			BIT(5)
+++#define MTK_WED_RTQM_TXDMAD_FPORT			GENMASK(23, 20)
+++
+++#define MTK_WED_RTQM_R2H_MIB(_n)			(0xb70 + (_n) * 0x4)
+++#define MTK_WED_RTQM_R2Q_MIB(_n)			(0xb78 + (_n) * 0x4)
+++#define MTK_WED_RTQM_Q2N_MIB				0xb80
+++#define MTK_WED_RTQM_Q2H_MIB(_n)			(0xb84 + (_n) * 0x4)
+++
+++#define MTK_WED_RTQM_Q2B_MIB				0xb8c
+++#define MTK_WED_RTQM_PFDBK_MIB				0xb90
+++
+++#define MTK_WED_RROQM_GLO_CFG				0xc04
+++#define MTK_WED_RROQM_RST_IDX				0xc08
+++#define MTK_WED_RROQM_RST_IDX_MIOD			BIT(0)
+++#define MTK_WED_RROQM_RST_IDX_FDBK			BIT(4)
+++
+++#define MTK_WED_RROQM_MIOD_CTRL0			0xc40
+++#define MTK_WED_RROQM_MIOD_CTRL1			0xc44
+++#define MTK_WED_RROQM_MIOD_CNT				GENMASK(11, 0)
+++
+++#define MTK_WED_RROQM_MIOD_CTRL2			0xc48
+++#define MTK_WED_RROQM_MIOD_CTRL3			0xc4c
+++
+++#define MTK_WED_RROQM_FDBK_CTRL0			0xc50
+++#define MTK_WED_RROQM_FDBK_CTRL1			0xc54
+++#define MTK_WED_RROQM_FDBK_CNT				GENMASK(11, 0)
+++
+++#define MTK_WED_RROQM_FDBK_CTRL2			0xc58
+++
+++#define MTK_WED_RROQ_BASE_L				0xc80
+++#define MTK_WED_RROQ_BASE_H				0xc84
+++
+++#define MTK_WED_RROQM_MIOD_CFG				0xc8c
+++#define MTK_WED_RROQM_MIOD_MID_DW			GENMASK(5, 0)
+++#define MTK_WED_RROQM_MIOD_MOD_DW			GENMASK(13, 8)
+++#define MTK_WED_RROQM_MIOD_ENTRY_DW			GENMASK(22, 16)
+++
+++#define MTK_WED_RROQM_MID_MIB				0xcc0
+++#define MTK_WED_RROQM_MOD_MIB				0xcc4
+++#define MTK_WED_RROQM_MOD_COHERENT_MIB			0xcc8
+++#define MTK_WED_RROQM_FDBK_MIB				0xcd0
+++#define MTK_WED_RROQM_FDBK_COHERENT_MIB			0xcd4
+++#define MTK_WED_RROQM_FDBK_IND_MIB			0xce0
+++#define MTK_WED_RROQM_FDBK_ENQ_MIB			0xce4
+++#define MTK_WED_RROQM_FDBK_ANC_MIB			0xce8
+++#define MTK_WED_RROQM_FDBK_ANC2H_MIB			0xcec
+++
+++#define MTK_WED_RX_BM_RX_DMAD				0xd80
+++#define MTK_WED_RX_BM_BASE				0xd84
+++#define MTK_WED_RX_BM_INIT_PTR				0xd88
+++#define MTK_WED_RX_BM_PTR				0xd8c
+++#define MTK_WED_RX_BM_PTR_HEAD				GENMASK(32, 16)
+++#define MTK_WED_RX_BM_PTR_TAIL				GENMASK(15, 0)
+++
+++#define MTK_WED_RX_BM_BLEN				0xd90
+++#define MTK_WED_RX_BM_STS				0xd94
+++#define MTK_WED_RX_BM_INTF2				0xd98
+++#define MTK_WED_RX_BM_INTF				0xd9c
+++#define MTK_WED_RX_BM_ERR_STS				0xda8
+++
+++#define MTK_WED_WOCPU_VIEW_MIOD_BASE			0x8000
+++#define MTK_WED_PCIE_INT_MASK				0x0
+++
++ #endif
++--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++@@ -49,6 +49,10 @@ enum {
++ 	MTK_WED_WARP_CMD_FLAG_FROM_TO_WO	= BIT(2),
++ };
++ 
+++#define MTK_WED_WO_CPU_MCUSYS_RESET_ADDR	0x15194050
+++#define MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK	0x20
+++#define MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK	0x1
+++
++ enum {
++ 	MTK_WED_WO_REGION_EMI,
++ 	MTK_WED_WO_REGION_ILM,
++@@ -57,6 +61,28 @@ enum {
++ 	__MTK_WED_WO_REGION_MAX,
++ };
++ 
+++enum mtk_wed_wo_state {
+++	MTK_WED_WO_STATE_UNDEFINED,
+++	MTK_WED_WO_STATE_INIT,
+++	MTK_WED_WO_STATE_ENABLE,
+++	MTK_WED_WO_STATE_DISABLE,
+++	MTK_WED_WO_STATE_HALT,
+++	MTK_WED_WO_STATE_GATING,
+++	MTK_WED_WO_STATE_SER_RESET,
+++	MTK_WED_WO_STATE_WF_RESET,
+++};
+++
+++enum mtk_wed_wo_done_state {
+++	MTK_WED_WOIF_UNDEFINED,
+++	MTK_WED_WOIF_DISABLE_DONE,
+++	MTK_WED_WOIF_TRIGGER_ENABLE,
+++	MTK_WED_WOIF_ENABLE_DONE,
+++	MTK_WED_WOIF_TRIGGER_GATING,
+++	MTK_WED_WOIF_GATING_DONE,
+++	MTK_WED_WOIF_TRIGGER_HALT,
+++	MTK_WED_WOIF_HALT_DONE,
+++};
+++
++ enum mtk_wed_dummy_cr_idx {
++ 	MTK_WED_DUMMY_CR_FWDL,
++ 	MTK_WED_DUMMY_CR_WO_STATUS,
++@@ -245,6 +271,8 @@ void mtk_wed_mcu_rx_unsolicited_event(st
++ 				      struct sk_buff *skb);
++ int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
++ 			 const void *data, int len, bool wait_resp);
+++int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data,
+++			   int len);
++ int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
++ int mtk_wed_wo_init(struct mtk_wed_hw *hw);
++ void mtk_wed_wo_deinit(struct mtk_wed_hw *hw);
++--- a/include/linux/soc/mediatek/mtk_wed.h
+++++ b/include/linux/soc/mediatek/mtk_wed.h
++@@ -5,10 +5,13 @@
++ #include <linux/rcupdate.h>
++ #include <linux/regmap.h>
++ #include <linux/pci.h>
+++#include <linux/skbuff.h>
++ 
++ #define MTK_WED_TX_QUEUES		2
++ #define MTK_WED_RX_QUEUES		2
++ 
+++#define WED_WO_STA_REC			0x6
+++
++ struct mtk_wed_hw;
++ struct mtk_wdma_desc;
++ 
++@@ -41,21 +44,37 @@ enum mtk_wed_wo_cmd {
++ 	MTK_WED_WO_CMD_WED_END
++ };
++ 
+++struct mtk_rxbm_desc {
+++	__le32 buf0;
+++	__le32 token;
+++} __packed __aligned(4);
+++
++ enum mtk_wed_bus_tye {
++ 	MTK_WED_BUS_PCIE,
++ 	MTK_WED_BUS_AXI,
++ };
++ 
+++#define MTK_WED_RING_CONFIGURED		BIT(0)
++ struct mtk_wed_ring {
++ 	struct mtk_wdma_desc *desc;
++ 	dma_addr_t desc_phys;
++ 	u32 desc_size;
++ 	int size;
+++	u32 flags;
++ 
++ 	u32 reg_base;
++ 	void __iomem *wpdma;
++ };
++ 
+++struct mtk_wed_wo_rx_stats {
+++	__le16 wlan_idx;
+++	__le16 tid;
+++	__le32 rx_pkt_cnt;
+++	__le32 rx_byte_cnt;
+++	__le32 rx_err_cnt;
+++	__le32 rx_drop_cnt;
+++};
+++
++ struct mtk_wed_device {
++ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ 	const struct mtk_wed_ops *ops;
++@@ -64,9 +83,12 @@ struct mtk_wed_device {
++ 	bool init_done, running;
++ 	int wdma_idx;
++ 	int irq;
+++	u8 version;
++ 
++ 	struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
+++	struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES];
++ 	struct mtk_wed_ring txfree_ring;
+++	struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
++ 	struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
++ 
++ 	struct {
++@@ -74,7 +96,20 @@ struct mtk_wed_device {
++ 		void **pages;
++ 		struct mtk_wdma_desc *desc;
++ 		dma_addr_t desc_phys;
++-	} buf_ring;
+++	} tx_buf_ring;
+++
+++	struct {
+++		int size;
+++		struct page_frag_cache rx_page;
+++		struct mtk_rxbm_desc *desc;
+++		dma_addr_t desc_phys;
+++	} rx_buf_ring;
+++
+++	struct {
+++		struct mtk_wed_ring ring;
+++		dma_addr_t miod_phys;
+++		dma_addr_t fdbk_phys;
+++	} rro;
++ 
++ 	/* filled by driver: */
++ 	struct {
++@@ -83,22 +118,36 @@ struct mtk_wed_device {
++ 			struct pci_dev *pci_dev;
++ 		};
++ 		enum mtk_wed_bus_tye bus_type;
+++		void __iomem *base;
+++		u32 phy_base;
++ 
++ 		u32 wpdma_phys;
++ 		u32 wpdma_int;
++ 		u32 wpdma_mask;
++ 		u32 wpdma_tx;
++ 		u32 wpdma_txfree;
+++		u32 wpdma_rx_glo;
+++		u32 wpdma_rx;
+++
+++		bool wcid_512;
++ 
++ 		u16 token_start;
++ 		unsigned int nbuf;
+++		unsigned int rx_nbuf;
+++		unsigned int rx_npkt;
+++		unsigned int rx_size;
++ 
++ 		u8 tx_tbit[MTK_WED_TX_QUEUES];
+++		u8 rx_tbit[MTK_WED_RX_QUEUES];
++ 		u8 txfree_tbit;
++ 
++ 		u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
++ 		int (*offload_enable)(struct mtk_wed_device *wed);
++ 		void (*offload_disable)(struct mtk_wed_device *wed);
+++		u32 (*init_rx_buf)(struct mtk_wed_device *wed, int size);
+++		void (*release_rx_buf)(struct mtk_wed_device *wed);
+++		void (*update_wo_rx_stats)(struct mtk_wed_device *wed,
+++					   struct mtk_wed_wo_rx_stats *stats);
++ 	} wlan;
++ #endif
++ };
++@@ -107,9 +156,15 @@ struct mtk_wed_ops {
++ 	int (*attach)(struct mtk_wed_device *dev);
++ 	int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
++ 			     void __iomem *regs);
+++	int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
+++			     void __iomem *regs);
++ 	int (*txfree_ring_setup)(struct mtk_wed_device *dev,
++ 				 void __iomem *regs);
+++	int (*msg_update)(struct mtk_wed_device *dev, int cmd_id,
+++			  void *data, int len);
++ 	void (*detach)(struct mtk_wed_device *dev);
+++	void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb,
+++			  u32 reason, u32 hash);
++ 
++ 	void (*stop)(struct mtk_wed_device *dev);
++ 	void (*start)(struct mtk_wed_device *dev, u32 irq_mask);
++@@ -144,6 +199,16 @@ mtk_wed_device_attach(struct mtk_wed_dev
++ 	return ret;
++ }
++ 
+++static inline bool
+++mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
+++{
+++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+++	return dev->version != 1;
+++#else
+++	return false;
+++#endif
+++}
+++
++ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ #define mtk_wed_device_active(_dev) !!(_dev)->ops
++ #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
++@@ -160,6 +225,12 @@ mtk_wed_device_attach(struct mtk_wed_dev
++ 	(_dev)->ops->irq_get(_dev, _mask)
++ #define mtk_wed_device_irq_set_mask(_dev, _mask) \
++ 	(_dev)->ops->irq_set_mask(_dev, _mask)
+++#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \
+++	(_dev)->ops->rx_ring_setup(_dev, _ring, _regs)
+++#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \
+++	(_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
+++#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \
+++	(_dev)->ops->msg_update(_dev, _id, _msg, _len)
++ #else
++ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
++ {
++@@ -173,6 +244,9 @@ static inline bool mtk_wed_device_active
++ #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
++ #define mtk_wed_device_irq_get(_dev, _mask) 0
++ #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0)
+++#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV
+++#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash)  do {} while (0)
+++#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
++ #endif
++ 
++ #endif
+diff --git a/target/linux/generic/pending-5.15/733-05-net-ethernet-mtk_wed-add-rx-mib-counters.patch b/target/linux/generic/pending-5.15/733-05-net-ethernet-mtk_wed-add-rx-mib-counters.patch
+new file mode 100644
+index 0000000000..bb1066dece
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/733-05-net-ethernet-mtk_wed-add-rx-mib-counters.patch
+@@ -0,0 +1,149 @@
++From: Lorenzo Bianconi <lorenzo@kernel.org>
++Date: Sat, 5 Nov 2022 23:36:22 +0100
++Subject: [PATCH] net: ethernet: mtk_wed: add rx mib counters
++
++Introduce WED RX MIB counters support available on MT7986a SoC.
++
++Tested-by: Daniel Golle <daniel@makrotopia.org>
++Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
++Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
++Signed-off-by: David S. Miller <davem@davemloft.net>
++---
++
++--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++@@ -2,6 +2,7 @@
++ /* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
++ 
++ #include <linux/seq_file.h>
+++#include <linux/soc/mediatek/mtk_wed.h>
++ #include "mtk_wed.h"
++ #include "mtk_wed_regs.h"
++ 
++@@ -18,6 +19,8 @@ enum {
++ 	DUMP_TYPE_WDMA,
++ 	DUMP_TYPE_WPDMA_TX,
++ 	DUMP_TYPE_WPDMA_TXFREE,
+++	DUMP_TYPE_WPDMA_RX,
+++	DUMP_TYPE_WED_RRO,
++ };
++ 
++ #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
++@@ -36,6 +39,9 @@ enum {
++ 
++ #define DUMP_WPDMA_TX_RING(_n) DUMP_RING("WPDMA_TX" #_n, 0, DUMP_TYPE_WPDMA_TX, _n)
++ #define DUMP_WPDMA_TXFREE_RING DUMP_RING("WPDMA_RX1", 0, DUMP_TYPE_WPDMA_TXFREE)
+++#define DUMP_WPDMA_RX_RING(_n)	DUMP_RING("WPDMA_RX" #_n, 0, DUMP_TYPE_WPDMA_RX, _n)
+++#define DUMP_WED_RRO_RING(_base)DUMP_RING("WED_RRO_MIOD", MTK_##_base, DUMP_TYPE_WED_RRO)
+++#define DUMP_WED_RRO_FDBK(_base)DUMP_RING("WED_RRO_FDBK", MTK_##_base, DUMP_TYPE_WED_RRO)
++ 
++ static void
++ print_reg_val(struct seq_file *s, const char *name, u32 val)
++@@ -57,6 +63,7 @@ dump_wed_regs(struct seq_file *s, struct
++ 				   cur > regs ? "\n" : "",
++ 				   cur->name);
++ 			continue;
+++		case DUMP_TYPE_WED_RRO:
++ 		case DUMP_TYPE_WED:
++ 			val = wed_r32(dev, cur->offset);
++ 			break;
++@@ -69,6 +76,9 @@ dump_wed_regs(struct seq_file *s, struct
++ 		case DUMP_TYPE_WPDMA_TXFREE:
++ 			val = wpdma_txfree_r32(dev, cur->offset);
++ 			break;
+++		case DUMP_TYPE_WPDMA_RX:
+++			val = wpdma_rx_r32(dev, cur->base, cur->offset);
+++			break;
++ 		}
++ 		print_reg_val(s, cur->name, val);
++ 	}
++@@ -132,6 +142,80 @@ wed_txinfo_show(struct seq_file *s, void
++ }
++ DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
++ 
+++static int
+++wed_rxinfo_show(struct seq_file *s, void *data)
+++{
+++	static const struct reg_dump regs[] = {
+++		DUMP_STR("WPDMA RX"),
+++		DUMP_WPDMA_RX_RING(0),
+++		DUMP_WPDMA_RX_RING(1),
+++
+++		DUMP_STR("WPDMA RX"),
+++		DUMP_WED(WED_WPDMA_RX_D_MIB(0)),
+++		DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(0)),
+++		DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(0)),
+++		DUMP_WED(WED_WPDMA_RX_D_MIB(1)),
+++		DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(1)),
+++		DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(1)),
+++		DUMP_WED(WED_WPDMA_RX_D_COHERENT_MIB),
+++
+++		DUMP_STR("WED RX"),
+++		DUMP_WED_RING(WED_RING_RX_DATA(0)),
+++		DUMP_WED_RING(WED_RING_RX_DATA(1)),
+++
+++		DUMP_STR("WED RRO"),
+++		DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0),
+++		DUMP_WED(WED_RROQM_MID_MIB),
+++		DUMP_WED(WED_RROQM_MOD_MIB),
+++		DUMP_WED(WED_RROQM_MOD_COHERENT_MIB),
+++		DUMP_WED_RRO_FDBK(WED_RROQM_FDBK_CTRL0),
+++		DUMP_WED(WED_RROQM_FDBK_IND_MIB),
+++		DUMP_WED(WED_RROQM_FDBK_ENQ_MIB),
+++		DUMP_WED(WED_RROQM_FDBK_ANC_MIB),
+++		DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB),
+++
+++		DUMP_STR("WED Route QM"),
+++		DUMP_WED(WED_RTQM_R2H_MIB(0)),
+++		DUMP_WED(WED_RTQM_R2Q_MIB(0)),
+++		DUMP_WED(WED_RTQM_Q2H_MIB(0)),
+++		DUMP_WED(WED_RTQM_R2H_MIB(1)),
+++		DUMP_WED(WED_RTQM_R2Q_MIB(1)),
+++		DUMP_WED(WED_RTQM_Q2H_MIB(1)),
+++		DUMP_WED(WED_RTQM_Q2N_MIB),
+++		DUMP_WED(WED_RTQM_Q2B_MIB),
+++		DUMP_WED(WED_RTQM_PFDBK_MIB),
+++
+++		DUMP_STR("WED WDMA TX"),
+++		DUMP_WED(WED_WDMA_TX_MIB),
+++		DUMP_WED_RING(WED_WDMA_RING_TX),
+++
+++		DUMP_STR("WDMA TX"),
+++		DUMP_WDMA(WDMA_GLO_CFG),
+++		DUMP_WDMA_RING(WDMA_RING_TX(0)),
+++		DUMP_WDMA_RING(WDMA_RING_TX(1)),
+++
+++		DUMP_STR("WED RX BM"),
+++		DUMP_WED(WED_RX_BM_BASE),
+++		DUMP_WED(WED_RX_BM_RX_DMAD),
+++		DUMP_WED(WED_RX_BM_PTR),
+++		DUMP_WED(WED_RX_BM_TKID_MIB),
+++		DUMP_WED(WED_RX_BM_BLEN),
+++		DUMP_WED(WED_RX_BM_STS),
+++		DUMP_WED(WED_RX_BM_INTF2),
+++		DUMP_WED(WED_RX_BM_INTF),
+++		DUMP_WED(WED_RX_BM_ERR_STS),
+++	};
+++	struct mtk_wed_hw *hw = s->private;
+++	struct mtk_wed_device *dev = hw->wed_dev;
+++
+++	if (!dev)
+++		return 0;
+++
+++	dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
+++
+++	return 0;
+++}
+++DEFINE_SHOW_ATTRIBUTE(wed_rxinfo);
++ 
++ static int
++ mtk_wed_reg_set(void *data, u64 val)
++@@ -175,4 +259,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
++ 	debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
++ 	debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
++ 	debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
+++	if (hw->version != 1)
+++		debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
+++					   &wed_rxinfo_fops);
++ }
+diff --git a/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
+new file mode 100644
+index 0000000000..8a718a02f2
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
+@@ -0,0 +1,27 @@
++From:   Tobias Waldekranz <tobias@waldekranz.com>
++Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port
++Date:   Sat, 16 Jan 2021 02:25:15 +0100
++Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/>
++
++While the hardware is capable of performing learning on the CPU port,
++it requires alot of additions to the bridge's forwarding path in order
++to handle multi-destination traffic correctly.
++
++Until that is in place, opt for the next best thing and let DSA sync
++the relevant addresses down to the hardware FDB.
++
++Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
++---
++ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
++ 1 file changed, 1 insertion(+)
++
++--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++++ b/drivers/net/dsa/mv88e6xxx/chip.c
++@@ -6319,6 +6319,7 @@ static int mv88e6xxx_register_switch(str
++ 	ds->ops = &mv88e6xxx_switch_ops;
++ 	ds->ageing_time_min = chip->info->age_time_coeff;
++ 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
+++	ds->assisted_learning_on_cpu_port = true;
++ 
++ 	/* Some chips support up to 32, but that requires enabling the
++ 	 * 5-bit port mode, which we do not support. 640k^W16 ought to
+diff --git a/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
+new file mode 100644
+index 0000000000..fcf7892c04
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
+@@ -0,0 +1,61 @@
++From patchwork Thu Aug  5 22:23:30 2021
++Content-Type: text/plain; charset="utf-8"
++MIME-Version: 1.0
++Content-Transfer-Encoding: 7bit
++X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
++X-Patchwork-Id: 12422209
++Date: Thu, 5 Aug 2021 23:23:30 +0100
++From: Daniel Golle <daniel@makrotopia.org>
++To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org,
++ linux-kernel@vger.kernel.org
++Cc: "David S. Miller" <davem@davemloft.net>, Andrew Lunn <andrew@lunn.ch>,
++ Michael Walle <michael@walle.cc>
++Subject: [PATCH] ARM: kirkwood: add missing <linux/if_ether.h> for ETH_ALEN
++Message-ID: <YQxk4jrbm31NM1US@makrotopia.org>
++MIME-Version: 1.0
++Content-Disposition: inline
++X-BeenThere: linux-arm-kernel@lists.infradead.org
++X-Mailman-Version: 2.1.34
++Precedence: list
++List-Id: <linux-arm-kernel.lists.infradead.org>
++List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
++Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
++
++After commit 83216e3988cd1 ("of: net: pass the dst buffer to
++of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not
++defined.
++
++arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup':
++arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'?
++   u8 tmpmac[ETH_ALEN];
++             ^~~~~~~~
++             ESTALE
++arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in
++arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable]
++   u8 tmpmac[ETH_ALEN];
++      ^~~~~~
++make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1
++make[5]: *** Waiting for unfinished jobs....
++
++Add missing #include <linux/if_ether.h> to fix this.
++
++Cc: David S. Miller <davem@davemloft.net>
++Cc: Andrew Lunn <andrew@lunn.ch>
++Cc: Michael Walle <michael@walle.cc>
++Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio
++Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()")
++Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++---
++ arch/arm/mach-mvebu/kirkwood.c | 1 +
++ 1 file changed, 1 insertion(+)
++
++--- a/arch/arm/mach-mvebu/kirkwood.c
+++++ b/arch/arm/mach-mvebu/kirkwood.c
++@@ -14,6 +14,7 @@
++ #include <linux/kernel.h>
++ #include <linux/init.h>
++ #include <linux/mbus.h>
+++#include <linux/if_ether.h>
++ #include <linux/of.h>
++ #include <linux/of_address.h>
++ #include <linux/of_net.h>
+diff --git a/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
+new file mode 100644
+index 0000000000..478a2cb27d
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
+@@ -0,0 +1,73 @@
++From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
++Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the
++ subdevices
++MIME-Version: 1.0
++Content-Type: text/plain; charset=UTF-8
++Content-Transfer-Encoding: 8bit
++
++For bus devices to be fully usable it's required to set their DMA
++parameters.
++
++For years it has been missing and remained unnoticed because of
++mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask.
++Kernel 4.19 came with a lot of DMA changes and caused a regression on
++the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic
++dma noncoherent ops for simple noncoherent platforms") DMA coherent
++allocations just fail. Example:
++[    1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed
++[    1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA
++[    1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12
++[    1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded
++
++This change fixes above regression in addition to the MIPS bcm47xx
++commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC").
++
++It also fixes another *old* GPIO regression caused by a parent pointing
++to the NULL:
++[    0.157054] missing gpiochip .dev parent pointer
++[    0.157287] bcma: bus0: Error registering GPIO driver: -22
++introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to
++use GPIOLIB_IRQCHIP").
++
++Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms")
++Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP")
++Cc: linux-mips@linux-mips.org
++Cc: Christoph Hellwig <hch@lst.de>
++Cc: Linus Walleij <linus.walleij@linaro.org>
++Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
++---
++
++--- a/drivers/bcma/host_soc.c
+++++ b/drivers/bcma/host_soc.c
++@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm
++ 	struct bcma_bus *bus = &soc->bus;
++ 	int err;
++ 
+++	bus->dev = soc->dev;
+++
++ 	/* Scan bus and initialize it */
++ 	err = bcma_bus_early_register(bus);
++ 	if (err)
++--- a/drivers/bcma/main.c
+++++ b/drivers/bcma/main.c
++@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq);
++ 
++ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
++ {
++-	device_initialize(&core->dev);
+++	struct device *dev = &core->dev;
+++
+++	device_initialize(dev);
++ 	core->dev.release = bcma_release_core_dev;
++ 	core->dev.bus = &bcma_bus_type;
++-	dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
+++	dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index);
++ 	core->dev.parent = bus->dev;
++-	if (bus->dev)
+++	if (bus->dev) {
++ 		bcma_of_fill_device(bus->dev, core);
+++		dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask);
+++	}
++ 
++ 	switch (bus->hosttype) {
++ 	case BCMA_HOSTTYPE_PCI:
+diff --git a/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
+new file mode 100644
+index 0000000000..c1e14b9271
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
+@@ -0,0 +1,222 @@
++From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001
++From: Mauri Sandberg <maukka@ext.kapsi.fi>
++Date: Thu, 25 Mar 2021 11:48:05 +0200
++Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade
++
++Adds support for building cascades of GPIO lines. That is, it allows
++setups when there is one upstream line and multiple cascaded lines, out
++of which one can be chosen at a time. The status of the upstream line
++can be conveyed to the selected cascaded line or, vice versa, the status
++of the cascaded line can be conveyed to the upstream line.
++
++A multiplexer is being used to select, which cascaded GPIO line is being
++used at any given time.
++
++At the moment only input direction is supported. In future it should be
++possible to add support for output direction, too.
++
++Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
++Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
++Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
++---
++v7 -> v8:
++ - rearrange members in struct gpio_cascade
++ - cosmetic changes in file header and in one function declaration
++ - added Reviewed-by tags by Linus and Andy
++v6 -> v7:
++ - In Kconfig add info about module name
++ - adhere to new convention that allows lines longer than 80 chars
++ - use dev_probe_err with upstream gpio line too
++ - refactor for cleaner exit of probe function.
++v5 -> v6:
++ - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER
++ - refactor code preferring one-liners
++ - clean up prints, removing them from success-path.
++ - don't explicitly set gpio_chip.of_node as it's done in the GPIO library
++ - use devm_gpiochip_add_data instead of gpiochip_add
++v4 -> v5:
++ - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly
++   here and there and changed to use new bindings and compatible string
++   - ambigious and vague 'pin' was rename to 'upstream_line'
++ - dropped Tested-by and Reviewed-by due to changes in bindings
++ - dropped Reported-by suggested by an automatic bot as it was not really
++   appropriate to begin with
++ - functionally it's the same as v4
++v3 -> v4:
++ - Changed author email
++ - Included Tested-by and Reviewed-by from Drew
++v2 -> v3:
++ - use managed device resources
++ - update Kconfig description
++v1 -> v2:
++ - removed .owner from platform_driver as per test bot's instruction
++ - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
++ - added gpio_mux_input_get_direction as it's recommended for all chips
++ - removed because this is input only chip: gpio_mux_input_set_value
++ - removed because they are not needed for input/output only chips:
++     gpio_mux_input_direction_input
++     gpio_mux_input_direction_output
++ - fixed typo in an error message
++ - added info message about successful registration
++ - removed can_sleep flag as this does not sleep while getting GPIO value
++   like I2C or SPI do
++ - Updated description in Kconfig
++---
++ drivers/gpio/Kconfig        |  15 +++++
++ drivers/gpio/Makefile       |   1 +
++ drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
++ 3 files changed, 133 insertions(+)
++ create mode 100644 drivers/gpio/gpio-cascade.c
++
++--- a/drivers/gpio/Kconfig
+++++ b/drivers/gpio/Kconfig
++@@ -1683,4 +1683,19 @@ config GPIO_VIRTIO
++ 
++ endmenu
++ 
+++comment "Other GPIO expanders"
 ++
-++	/*
-++	 * 3. Set Spread Spectrum Clock Enabled
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4,
-++			    SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN);
+++config GPIO_CASCADE
+++	tristate "General GPIO cascade"
+++	select MULTIPLEXER
+++	help
+++	  Say yes here to enable support for generic GPIO cascade.
 ++
-++	/*
-++	 * 4. Set Override Margining Controls From the MAC:
-++	 *    Use margining signals from lane configuration
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL,
-++			    MODE_MARGIN_OVERRIDE, 0xFFFF);
+++	  This allows building one-to-many cascades of GPIO lines using
+++	  different types of multiplexers readily available. At the
+++	  moment only input lines are supported.
 ++
-++	/*
-++	 * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles
-++	 *    set Mode Clock Source = PCLK is generated from REFCLK
-++	 */
-++	data = 0x0;
-++	mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK |
-++	       BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask);
+++	  To build the driver as a module choose 'm' and the resulting module
+++	  will be called 'gpio-cascade'.
 ++
-++	/*
-++	 * 6. Set G2 Spread Spectrum Clock Amplitude at 4K
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_GEN2_SET2,
-++			    GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK);
++ endif
++--- a/drivers/gpio/Makefile
+++++ b/drivers/gpio/Makefile
++@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd
++ obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
++ obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o
++ obj-$(CONFIG_GPIO_CADENCE)		+= gpio-cadence.o
+++obj-$(CONFIG_GPIO_CASCADE)		+= gpio-cascade.o
++ obj-$(CONFIG_GPIO_CLPS711X)		+= gpio-clps711x.o
++ obj-$(CONFIG_GPIO_SNPS_CREG)		+= gpio-creg-snps.o
++ obj-$(CONFIG_GPIO_CRYSTAL_COVE)		+= gpio-crystalcove.o
++--- /dev/null
+++++ b/drivers/gpio/gpio-cascade.c
++@@ -0,0 +1,117 @@
+++// SPDX-License-Identifier: GPL-2.0-only
+++/*
+++ *  A generic GPIO cascade driver
+++ *
+++ *  Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi>
+++ *
+++ * This allows building cascades of GPIO lines in a manner illustrated
+++ * below:
+++ *
+++ *                 /|---- Cascaded GPIO line 0
+++ *  Upstream      | |---- Cascaded GPIO line 1
+++ *  GPIO line ----+ | .
+++ *                | | .
+++ *                 \|---- Cascaded GPIO line n
+++ *
+++ * A multiplexer is being used to select, which cascaded line is being
+++ * addressed at any given time.
+++ *
+++ * At the moment only input mode is supported due to lack of means for
+++ * testing output functionality. At least theoretically output should be
+++ * possible with open drain constructions.
+++ */
 ++
-++	/*
-++	 * 7. Unset G3 Spread Spectrum Clock Amplitude
-++	 *    set G3 TX and RX Register Master Current Select
-++	 */
-++	data = GS2_VREG_RXTX_MAS_ISET_60U;
-++	mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK |
-++	       GS2_RSVD_6_0_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask);
+++#include <linux/module.h>
+++#include <linux/slab.h>
+++#include <linux/platform_device.h>
+++#include <linux/mux/consumer.h>
 ++
-++	/*
-++	 * 8. Check crystal jumper setting and program the Power and PLL Control
-++	 * accordingly Change RX wait
-++	 */
-++	if (lane->priv->xtal_is_40m) {
-++		ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
-++		cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT;
-++	} else {
-++		ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
-++		cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT;
-++	}
+++#include <linux/gpio/consumer.h>
+++#include <linux/gpio/driver.h>
 ++
-++	data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
-++	       PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk;
-++	mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
-++	       PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK |
-++	       REF_FREF_SEL_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+++struct gpio_cascade {
+++	struct gpio_chip	gpio_chip;
+++	struct device		*parent;
+++	struct mux_control	*mux_control;
+++	struct gpio_desc	*upstream_line;
+++};
 ++
-++	data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg;
-++	mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
-++	       CFG_PM_RXDLOZ_WAIT_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
+++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
+++{
+++	return container_of(gc, struct gpio_cascade, gpio_chip);
+++}
 ++
-++	/*
-++	 * 9. Enable idle sync
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
-++			    IDLE_SYNC_EN, IDLE_SYNC_EN);
+++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
+++{
+++	return GPIO_LINE_DIRECTION_IN;
+++}
 ++
-++	/*
-++	 * 10. Enable the output of 500M clock
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN);
+++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
+++{
+++	struct gpio_cascade *cas = chip_to_cascade(gc);
+++	int ret;
 ++
-++	/*
-++	 * 11. Set 20-bit data width
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
-++			    DATA_WIDTH_20BIT, 0xFFFF);
+++	ret = mux_control_select(cas->mux_control, offset);
+++	if (ret)
+++		return ret;
 ++
-++	/*
-++	 * 12. Override Speed_PLL value and use MAC PLL
-++	 */
-++	data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT;
-++	mask = 0xFFFF;
-++	comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask);
+++	ret = gpiod_get_value(cas->upstream_line);
+++	mux_control_deselect(cas->mux_control);
+++	return ret;
+++}
 ++
-++	/*
-++	 * 13. Check the Polarity invert bit
-++	 */
-++	data = 0x0;
-++	if (lane->invert_tx)
-++		data |= TXD_INVERT_BIT;
-++	if (lane->invert_rx)
-++		data |= RXD_INVERT_BIT;
-++	mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
-++	comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+++static int gpio_cascade_probe(struct platform_device *pdev)
+++{
+++	struct device *dev = &pdev->dev;
+++	struct gpio_cascade *cas;
+++	struct mux_control *mc;
+++	struct gpio_desc *upstream;
+++	struct gpio_chip *gc;
 ++
-++	/*
-++	 * 14. Set max speed generation to USB3.0 5Gbps
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN,
-++			    PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK);
+++	cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
+++	if (!cas)
+++		return -ENOMEM;
 ++
-++	/*
-++	 * 15. Set capacitor value for FFE gain peaking to 0xF
-++	 */
-++	comphy_lane_reg_set(lane, COMPHY_GEN2_SET3,
-++			    GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK);
+++	mc = devm_mux_control_get(dev, NULL);
+++	if (IS_ERR(mc))
+++		return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n");
 ++
-++	/*
-++	 * 16. Release SW reset
-++	 */
-++	data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4;
-++	mask = 0xFFFF;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+++	cas->mux_control = mc;
+++	upstream = devm_gpiod_get(dev, "upstream",  GPIOD_IN);
+++	if (IS_ERR(upstream))
+++		return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n");
 ++
-++	/* Wait for > 55 us to allow PCLK be enabled */
-++	udelay(PLL_SET_DELAY_US);
+++	cas->upstream_line = upstream;
+++	cas->parent = dev;
 ++
-++	ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
-++				   COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to lock USB3 PLL\n");
-++		return ret;
-++	}
+++	gc = &cas->gpio_chip;
+++	gc->get = gpio_cascade_get_value;
+++	gc->get_direction = gpio_cascade_get_direction;
+++	gc->base = -1;
+++	gc->ngpio = mux_control_states(mc);
+++	gc->label = dev_name(cas->parent);
+++	gc->parent = cas->parent;
+++	gc->owner = THIS_MODULE;
 ++
-++	return 0;
+++	platform_set_drvdata(pdev, cas);
+++	return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
 ++}
 ++
-++static int
-++mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane)
-++{
-++	u32 mask, data, ref_clk;
-++	int ret;
-++
-++	/* Configure phy selector for PCIe */
-++	ret = mvebu_a3700_comphy_set_phy_selector(lane);
-++	if (ret)
-++		return ret;
-++
-++	/* 1. Enable max PLL. */
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1,
-++			    USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN);
+++static const struct of_device_id gpio_cascade_id[] = {
+++	{ .compatible = "gpio-cascade" },
+++	{ /* sentinel */ }
+++};
+++MODULE_DEVICE_TABLE(of, gpio_cascade_id);
 ++
-++	/* 2. Select 20 bit SERDES interface. */
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO,
-++			    CFG_SEL_20B, CFG_SEL_20B);
+++static struct platform_driver gpio_cascade_driver = {
+++	.driver	= {
+++		.name		= "gpio-cascade",
+++		.of_match_table = gpio_cascade_id,
+++	},
+++	.probe	= gpio_cascade_probe,
+++};
+++module_platform_driver(gpio_cascade_driver);
 ++
-++	/* 3. Force to use reg setting for PCIe mode */
-++	comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1,
-++			    SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE);
+++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
+++MODULE_DESCRIPTION("Generic GPIO cascade");
+++MODULE_LICENSE("GPL");
+diff --git a/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch
+new file mode 100644
+index 0000000000..7edbd94f76
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/810-pci_disable_common_quirks.patch
+@@ -0,0 +1,62 @@
++From: Gabor Juhos <juhosg@openwrt.org>
++Subject: debloat: add kernel config option to disabling common PCI quirks
++
++Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
++---
++ drivers/pci/Kconfig  | 6 ++++++
++ drivers/pci/quirks.c | 6 ++++++
++ 2 files changed, 12 insertions(+)
++
++--- a/drivers/pci/Kconfig
+++++ b/drivers/pci/Kconfig
++@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND
++ 	  The PCI device frontend driver allows the kernel to import arbitrary
++ 	  PCI devices from a PCI backend to support PCI driver domains.
++ 
+++config PCI_DISABLE_COMMON_QUIRKS
+++	bool "PCI disable common quirks"
+++	depends on PCI
+++	help
+++	  If you don't know what to do here, say N.
 ++
-++	/* 4. Change RX wait */
-++	data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT;
-++	mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
-++	       CFG_PM_RXDLOZ_WAIT_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
 ++
-++	/* 5. Enable idle sync */
-++	comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
-++			    IDLE_SYNC_EN, IDLE_SYNC_EN);
++ config PCI_ATS
++ 	bool
++ 
++--- a/drivers/pci/quirks.c
+++++ b/drivers/pci/quirks.c
++@@ -206,6 +206,7 @@ static void quirk_mmio_always_on(struct
++ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
++ 				PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
++ 
+++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++ /*
++  * The Mellanox Tavor device gives false positive parity errors.  Disable
++  * parity error reporting.
++@@ -3363,6 +3364,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
++ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
++ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
++ 
+++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
 ++
-++	/* 6. Enable the output of 100M/125M/500M clock */
-++	data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN;
-++	mask = data;
-++	comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
++ /*
++  * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum.
++  * To work around this, query the size it should be configured to by the
++@@ -3388,6 +3391,8 @@ static void quirk_intel_ntb(struct pci_d
++ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
++ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
++ 
+++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
 ++
-++	/*
-++	 * 7. Enable TX, PCIE global register, 0xd0074814, it is done in
-++	 * PCI-E driver
-++	 */
++ /*
++  * Some BIOS implementations leave the Intel GPU interrupts enabled, even
++  * though no one is handling them (e.g., if the i915 driver is never
++@@ -3426,6 +3431,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
++ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
++ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
++ 
+++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
 ++
-++	/*
-++	 * 8. Check crystal jumper setting and program the Power and PLL
-++	 * Control accordingly
-++	 */
++ /*
++  * PCI devices which are on Intel chips can skip the 10ms delay
++  * before entering D3 mode.
+diff --git a/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch
+new file mode 100644
+index 0000000000..b498d9f700
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/811-pci_disable_usb_common_quirks.patch
+@@ -0,0 +1,115 @@
++From: Felix Fietkau <nbd@nbd.name>
++Subject: debloat: disable common USB quirks
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++ drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
++ drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
++ include/linux/usb/hcd.h       |  7 +++++++
++ 3 files changed, 40 insertions(+), 1 deletion(-)
++
++--- a/drivers/usb/host/pci-quirks.c
+++++ b/drivers/usb/host/pci-quirks.c
++@@ -128,6 +128,8 @@ struct amd_chipset_type {
++ 	u8 rev;
++ };
++ 
+++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
 ++
-++	if (lane->priv->xtal_is_40m)
-++		ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
-++	else
-++		ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
-++
-++	data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
-++	       PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk;
-++	mask = 0xFFFF;
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
-++
-++	/* 9. Override Speed_PLL value and use MAC PLL */
-++	comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
-++			    SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT,
-++			    0xFFFF);
-++
-++	/* 10. Check the Polarity invert bit */
-++	data = 0x0;
-++	if (lane->invert_tx)
-++		data |= TXD_INVERT_BIT;
-++	if (lane->invert_rx)
-++		data |= RXD_INVERT_BIT;
-++	mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
-++	comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
-++
-++	/* 11. Release SW reset */
-++	data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32;
-++	mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
-++
-++	/* Wait for > 55 us to allow PCLK be enabled */
-++	udelay(PLL_SET_DELAY_US);
-++
-++	ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
-++				   COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
-++	if (ret) {
-++		dev_err(lane->dev, "Failed to lock PCIE PLL\n");
-++		return ret;
-++	}
++ static struct amd_chipset_info {
++ 	struct pci_dev	*nb_dev;
++ 	struct pci_dev	*smbus_dev;
++@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device
++ }
++ EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
++ 
+++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */
 ++
-++	return 0;
-++}
+++#if IS_ENABLED(CONFIG_USB_UHCI_HCD)
 ++
-++static void
-++mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane)
++ /*
++  * Make sure the controller is completely inactive, unable to
++  * generate interrupts or do DMA.
++@@ -712,8 +718,17 @@ reset_needed:
++ 	uhci_reset_hc(pdev, base);
++ 	return 1;
++ }
+++#else
+++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
 ++{
-++	/*
-++	 * Currently the USB3 MAC sets the USB3 PHY to low state, so we do not
-++	 * need to power off USB3 PHY again.
-++	 */
+++	return 0;
 ++}
 ++
-++static void
-++mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane)
-++{
-++	/* Set phy isolation mode */
-++	comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
-++			    PHY_ISOLATE_MODE, PHY_ISOLATE_MODE);
+++#endif
++ EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
++ 
+++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
 ++
-++	/* Power off PLL, Tx, Rx */
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
-++			    0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
-++}
++ static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
++ {
++ 	u16 cmd;
++@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(stru
++ }
++ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
++ 			PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
+++#endif
++--- a/drivers/usb/host/pci-quirks.h
+++++ b/drivers/usb/host/pci-quirks.h
++@@ -5,6 +5,9 @@
++ #ifdef CONFIG_USB_PCI
++ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
++ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+++#endif  /* CONFIG_USB_PCI */
 ++
-++static void
-++mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane)
+++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS)
++ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
++ bool usb_amd_hang_symptom_quirk(void);
++ bool usb_amd_prefetch_quirk(void);
++@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev,
++ bool usb_amd_pt_check_port(struct device *device, int port);
++ #else
++ struct pci_dev;
+++static inline int usb_amd_quirk_pll_check(void)
 ++{
-++	u32 mask, data;
-++
-++	data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT |
-++	       PHY_RX_INIT_BIT;
-++	mask = data;
-++	comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+++	return 0;
 ++}
-++
-++static void
-++mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane)
+++static inline bool usb_amd_hang_symptom_quirk(void)
 ++{
-++	/* Power off PLL, Tx, Rx */
-++	comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
-++			    0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+++	return false;
 ++}
-++
-++static int mvebu_a3700_comphy_reset(struct phy *phy)
+++static inline bool usb_amd_prefetch_quirk(void)
 ++{
-++	struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
-++	u16 mask, data;
-++
-++	dev_dbg(lane->dev, "resetting lane %d\n", lane->id);
-++
-++	/* COMPHY reset for internal logic */
-++	comphy_lane_reg_set(lane, COMPHY_SFT_RESET,
-++			    SFT_RST_NO_REG, SFT_RST_NO_REG);
-++
-++	/* COMPHY register reset (cleared automatically) */
-++	comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST);
-++
-++	/* PIPE soft and register reset */
-++	data = PIPE_SOFT_RESET | PIPE_REG_RESET;
-++	mask = data;
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
-++
-++	/* Release PIPE register reset */
-++	comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL,
-++			    0x0, PIPE_REG_RESET);
-++
-++	/* Reset SB configuration register (only for lanes 0 and 1) */
-++	if (lane->id == 0 || lane->id == 1) {
-++		u32 mask, data;
-++
-++		data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT |
-++		       PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
-++		mask = data | PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT;
-++		comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
-++	}
-++
-++	return 0;
+++	return false;
 ++}
-++
-++static bool mvebu_a3700_comphy_check_mode(int lane,
-+ 					  enum phy_mode mode,
-+ 					  int submode)
++ static inline void usb_amd_quirk_pll_disable(void) {}
++ static inline void usb_amd_quirk_pll_enable(void) {}
++ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
++@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port
 + {
-+@@ -122,7 +1170,7 @@ static int mvebu_a3700_comphy_get_fw_mod
++ 	return false;
++ }
+++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {}
+++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
+++{
+++	return false;
+++}
++ #endif  /* CONFIG_USB_PCI */
 + 
-+ 	/* Unused PHY mux value is 0x0 */
-+ 	if (mode == PHY_MODE_INVALID)
-+-		return -EINVAL;
-++		return false;
++ #endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
++--- a/include/linux/usb/hcd.h
+++++ b/include/linux/usb/hcd.h
++@@ -498,7 +498,14 @@ extern int usb_hcd_pci_probe(struct pci_
++ extern void usb_hcd_pci_remove(struct pci_dev *dev);
++ extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 + 
-+ 	for (i = 0; i < n; i++) {
-+ 		if (mvebu_a3700_comphy_modes[i].lane == lane &&
-+@@ -132,27 +1180,30 @@ static int mvebu_a3700_comphy_get_fw_mod
-+ 	}
+++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++ extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
+++#else
+++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev)
+++{
+++	return 0;
+++}
+++#endif
 + 
-+ 	if (i == n)
-+-		return -EINVAL;
-++		return false;
++ #ifdef CONFIG_PM
++ extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
+diff --git a/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
+new file mode 100644
+index 0000000000..33eb34c913
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
+@@ -0,0 +1,26 @@
++From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
++From: Pawel Dembicki <paweldembicki@gmail.com>
++Date: Sun, 18 Feb 2018 17:08:04 +0100
++Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
++
++In devices, where fdt is used, is impossible to apply platform data
++without proper fdt node.
++
++This patch allow to use platform data in devices with fdt.
++
++Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
++---
++ drivers/w1/masters/w1-gpio.c | 7 +++----
++ 1 file changed, 3 insertions(+), 4 deletions(-)
++
++--- a/drivers/w1/masters/w1-gpio.c
+++++ b/drivers/w1/masters/w1-gpio.c
++@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
++ 	enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
++ 	int err;
 + 
-+-	return mvebu_a3700_comphy_modes[i].fw_mode;
-++	return true;
-+ }
++-	if (of_have_populated_dt()) {
+++	if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
++ 		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
++ 		if (!pdata)
++ 			return -ENOMEM;
+diff --git a/target/linux/generic/pending-5.15/834-ledtrig-libata.patch b/target/linux/generic/pending-5.15/834-ledtrig-libata.patch
+new file mode 100644
+index 0000000000..58b837c084
+--- /dev/null
++++ b/target/linux/generic/pending-5.15/834-ledtrig-libata.patch
+@@ -0,0 +1,149 @@
++From: Daniel Golle <daniel@makrotopia.org>
++Subject: libata: add ledtrig support
++
++This adds a LED trigger for each ATA port indicating disk activity.
++
++As this is needed only on specific platforms (NAS SoCs and such),
++these platforms should define ARCH_WANTS_LIBATA_LEDS if there
++are boards with LED(s) intended to indicate ATA disk activity and
++need the OS to take care of that.
++In that way, if not selected, LED trigger support not will be
++included in libata-core and both, codepaths and structures remain
++untouched.
++
++Signed-off-by: Daniel Golle <daniel@makrotopia.org>
++---
++ drivers/ata/Kconfig       | 16 ++++++++++++++++
++ drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++
++ include/linux/libata.h    |  9 +++++++++
++ 3 files changed, 66 insertions(+)
++
++--- a/drivers/ata/Kconfig
+++++ b/drivers/ata/Kconfig
++@@ -67,6 +67,22 @@ config ATA_FORCE
 + 
-+ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
-+ 				       int submode)
-+ {
-+ 	struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
-+-	int fw_mode;
-+-
-+-	if (submode == PHY_INTERFACE_MODE_1000BASEX)
-+-		submode = PHY_INTERFACE_MODE_SGMII;
-+ 
-+-	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode,
-+-						 submode);
-+-	if (fw_mode < 0) {
-++	if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) {
-+ 		dev_err(lane->dev, "invalid COMPHY mode\n");
-+-		return fw_mode;
-++		return -EINVAL;
-+ 	}
++ 	  If unsure, say Y.
 + 
-++	/* Mode cannot be changed while the PHY is powered on */
-++	if (phy->power_count &&
-++	    (lane->mode != mode || lane->submode != submode))
-++		return -EBUSY;
+++config ARCH_WANT_LIBATA_LEDS
+++	bool
+++
+++config ATA_LEDS
+++	bool "support ATA port LED triggers"
+++	depends on ARCH_WANT_LIBATA_LEDS
+++	select NEW_LEDS
+++	select LEDS_CLASS
+++	select LEDS_TRIGGERS
+++	default y
+++	help
+++	  This option adds a LED trigger for each registered ATA port.
+++	  It is used to drive disk activity leds connected via GPIO.
 ++
-++	/* If changing mode, ensure reset is called */
-++	if (lane->mode != PHY_MODE_INVALID && lane->mode != mode)
-++		lane->needs_reset = true;
+++	  If unsure, say N.
 ++
-+ 	/* Just remember the mode, ->power_on() will do the real setup */
-+ 	lane->mode = mode;
-+ 	lane->submode = submode;
-+@@ -163,76 +1214,68 @@ static int mvebu_a3700_comphy_set_mode(s
-+ static int mvebu_a3700_comphy_power_on(struct phy *phy)
-+ {
-+ 	struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
-+-	u32 fw_param;
-+-	int fw_mode;
-+-	int fw_port;
-+ 	int ret;
++ config ATA_ACPI
++ 	bool "ATA ACPI Support"
++ 	depends on ACPI
++--- a/drivers/ata/libata-core.c
+++++ b/drivers/ata/libata-core.c
++@@ -656,6 +656,19 @@ u64 ata_tf_read_block(const struct ata_t
++ 	return block;
++ }
 + 
-+-	fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id,
-+-						 lane->mode, lane->submode);
-+-	if (fw_mode < 0) {
-++	if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode,
-++					   lane->submode)) {
-+ 		dev_err(lane->dev, "invalid COMPHY mode\n");
-+-		return fw_mode;
-++		return -EINVAL;
-++	}
+++#ifdef CONFIG_ATA_LEDS
+++#define LIBATA_BLINK_DELAY 20 /* ms */
+++static inline void ata_led_act(struct ata_port *ap)
+++{
+++	unsigned long led_delay = LIBATA_BLINK_DELAY;
 ++
-++	if (lane->needs_reset) {
-++		ret = mvebu_a3700_comphy_reset(phy);
-++		if (ret)
-++			return ret;
+++	if (unlikely(!ap->ledtrig))
+++		return;
 ++
-++		lane->needs_reset = false;
-+ 	}
-+ 
-+ 	switch (lane->mode) {
-+ 	case PHY_MODE_USB_HOST_SS:
-+ 		dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
-+-		fw_param = COMPHY_FW_MODE(fw_mode);
-+-		break;
-++		return mvebu_a3700_comphy_usb3_power_on(lane);
-+ 	case PHY_MODE_SATA:
-+ 		dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
-+-		fw_param = COMPHY_FW_MODE(fw_mode);
-+-		break;
-++		return mvebu_a3700_comphy_sata_power_on(lane);
-+ 	case PHY_MODE_ETHERNET:
-+-		fw_port = (lane->id == 0) ? 1 : 0;
-+-		switch (lane->submode) {
-+-		case PHY_INTERFACE_MODE_SGMII:
-+-			dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
-+-				lane->id);
-+-			fw_param = COMPHY_FW_NET(fw_mode, fw_port,
-+-						 COMPHY_FW_SPEED_1_25G);
-+-			break;
-+-		case PHY_INTERFACE_MODE_2500BASEX:
-+-			dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
-+-				lane->id);
-+-			fw_param = COMPHY_FW_NET(fw_mode, fw_port,
-+-						 COMPHY_FW_SPEED_3_125G);
-+-			break;
-+-		default:
-+-			dev_err(lane->dev, "unsupported PHY submode (%d)\n",
-+-				lane->submode);
-+-			return -ENOTSUPP;
-+-		}
-+-		break;
-++		dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id);
-++		return mvebu_a3700_comphy_ethernet_power_on(lane);
-+ 	case PHY_MODE_PCIE:
-+ 		dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
-+-		fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G,
-+-					  phy->attrs.bus_width);
-+-		break;
-++		return mvebu_a3700_comphy_pcie_power_on(lane);
-+ 	default:
-+ 		dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
-+-		return -ENOTSUPP;
-++		return -EOPNOTSUPP;
+++	led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0);
+++}
+++#endif
+++
++ /**
++  *	ata_build_rw_tf - Build ATA taskfile for given read/write request
++  *	@tf: Target ATA taskfile
++@@ -4580,6 +4593,9 @@ struct ata_queued_cmd *ata_qc_new_init(s
++ 		if (tag < 0)
++ 			return NULL;
 + 	}
-+-
-+-	ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
-+-	if (ret == -EOPNOTSUPP)
-+-		dev_err(lane->dev,
-+-			"unsupported SMC call, try updating your firmware\n");
-+-
-+-	return ret;
-+ }
-+ 
-+ static int mvebu_a3700_comphy_power_off(struct phy *phy)
-+ {
-+ 	struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+++#ifdef CONFIG_ATA_LEDS
+++	ata_led_act(ap);
+++#endif
 + 
-+-	return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
-++	switch (lane->mode) {
-++	case PHY_MODE_USB_HOST_SS:
-++		mvebu_a3700_comphy_usb3_power_off(lane);
-++		return 0;
-++	case PHY_MODE_SATA:
-++		mvebu_a3700_comphy_sata_power_off(lane);
-++		return 0;
-++	case PHY_MODE_ETHERNET:
-++		mvebu_a3700_comphy_ethernet_power_off(lane);
-++		return 0;
-++	case PHY_MODE_PCIE:
-++		mvebu_a3700_comphy_pcie_power_off(lane);
-++		return 0;
-++	default:
-++		dev_err(lane->dev, "invalid COMPHY mode\n");
-++		return -EINVAL;
-++	}
-+ }
++ 	qc = __ata_qc_from_tag(ap, tag);
++ 	qc->tag = qc->hw_tag = tag;
++@@ -5358,6 +5374,9 @@ struct ata_port *ata_port_alloc(struct a
++ 	ap->stats.unhandled_irq = 1;
++ 	ap->stats.idle_irq = 1;
++ #endif
+++#ifdef CONFIG_ATA_LEDS
+++	ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+++#endif
++ 	ata_sff_port_init(ap);
 + 
-+ static const struct phy_ops mvebu_a3700_comphy_ops = {
-+ 	.power_on	= mvebu_a3700_comphy_power_on,
-+ 	.power_off	= mvebu_a3700_comphy_power_off,
-++	.reset		= mvebu_a3700_comphy_reset,
-+ 	.set_mode	= mvebu_a3700_comphy_set_mode,
-+ 	.owner		= THIS_MODULE,
-+ };
-+@@ -256,13 +1299,75 @@ static struct phy *mvebu_a3700_comphy_xl
-+ 		return ERR_PTR(-EINVAL);
-+ 	}
++ 	return ap;
++@@ -5393,6 +5412,12 @@ static void ata_host_release(struct kref
 + 
-++	lane->invert_tx = args->args[1] & BIT(0);
-++	lane->invert_rx = args->args[1] & BIT(1);
-++
-+ 	return phy;
-+ }
++ 		kfree(ap->pmp_link);
++ 		kfree(ap->slave_link);
+++#ifdef CONFIG_ATA_LEDS
+++		if (ap->ledtrig) {
+++			led_trigger_unregister(ap->ledtrig);
+++			kfree(ap->ledtrig);
+++		};
+++#endif
++ 		kfree(ap);
++ 		host->ports[i] = NULL;
++ 	}
++@@ -5799,7 +5824,23 @@ int ata_host_register(struct ata_host *h
++ 		host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
++ 		host->ports[i]->local_port_no = i + 1;
++ 	}
+++#ifdef CONFIG_ATA_LEDS
+++	for (i = 0; i < host->n_ports; i++) {
+++		if (unlikely(!host->ports[i]->ledtrig))
+++			continue;
 + 
-+ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
-+ {
-++	struct mvebu_a3700_comphy_priv *priv;
-+ 	struct phy_provider *provider;
-+ 	struct device_node *child;
-++	struct resource *res;
-++	struct clk *clk;
-++	int ret;
-++
-++	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-++	if (!priv)
-++		return -ENOMEM;
-++
-++	spin_lock_init(&priv->lock);
-++
-++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy");
-++	priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res);
-++	if (IS_ERR(priv->comphy_regs))
-++		return PTR_ERR(priv->comphy_regs);
-++
-++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-++					   "lane1_pcie_gbe");
-++	priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res);
-++	if (IS_ERR(priv->lane1_phy_regs))
-++		return PTR_ERR(priv->lane1_phy_regs);
-++
-++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-++					   "lane0_usb3_gbe");
-++	priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res);
-++	if (IS_ERR(priv->lane0_phy_regs))
-++		return PTR_ERR(priv->lane0_phy_regs);
+++		snprintf(host->ports[i]->ledtrig_name,
+++			sizeof(host->ports[i]->ledtrig_name), "ata%u",
+++			host->ports[i]->print_id);
 ++
-++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-++					   "lane2_sata_usb3");
-++	priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res);
-++	if (IS_ERR(priv->lane2_phy_indirect))
-++		return PTR_ERR(priv->lane2_phy_indirect);
+++		host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name;
 ++
-++	/*
-++	 * Driver needs to know if reference xtal clock is 40MHz or 25MHz.
-++	 * Old DT bindings do not have xtal clk present. So do not fail here
-++	 * and expects that default 25MHz reference clock is used.
-++	 */
-++	clk = clk_get(&pdev->dev, "xtal");
-++	if (IS_ERR(clk)) {
-++		if (PTR_ERR(clk) == -EPROBE_DEFER)
-++			return -EPROBE_DEFER;
-++		dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n",
-++			 PTR_ERR(clk));
-++	} else {
-++		ret = clk_prepare_enable(clk);
-++		if (ret) {
-++			dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n",
-++				 ret);
-++		} else {
-++			if (clk_get_rate(clk) == 40000000)
-++				priv->xtal_is_40m = true;
-++			clk_disable_unprepare(clk);
+++		if (led_trigger_register(host->ports[i]->ledtrig)) {
+++			kfree(host->ports[i]->ledtrig);
+++			host->ports[i]->ledtrig = NULL;
 ++		}
-++		clk_put(clk);
 ++	}
-++
-++	dev_set_drvdata(&pdev->dev, priv);
-+ 
-+ 	for_each_available_child_of_node(pdev->dev.of_node, child) {
-+ 		struct mvebu_a3700_comphy_lane *lane;
-+@@ -277,7 +1382,7 @@ static int mvebu_a3700_comphy_probe(stru
-+ 			continue;
-+ 		}
-+ 
-+-		if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
-++		if (lane_id >= 3) {
-+ 			dev_err(&pdev->dev, "invalid 'reg' property\n");
-+ 			continue;
-+ 		}
-+@@ -295,11 +1400,21 @@ static int mvebu_a3700_comphy_probe(stru
-+ 			return PTR_ERR(phy);
-+ 		}
-+ 
-++		lane->priv = priv;
-+ 		lane->dev = &pdev->dev;
-+ 		lane->mode = PHY_MODE_INVALID;
-+ 		lane->submode = PHY_INTERFACE_MODE_NA;
-+ 		lane->id = lane_id;
-++		lane->invert_tx = false;
-++		lane->invert_rx = false;
-+ 		phy_set_drvdata(phy, lane);
-++
-++		/*
-++		 * To avoid relying on the bootloader/firmware configuration,
-++		 * power off all comphys.
-++		 */
-++		mvebu_a3700_comphy_reset(phy);
-++		lane->needs_reset = false;
-+ 	}
-+ 
-+ 	provider = devm_of_phy_provider_register(&pdev->dev,
-+@@ -323,5 +1438,7 @@ static struct platform_driver mvebu_a370
-+ module_platform_driver(mvebu_a3700_comphy_driver);
+++#endif
++ 	/* Create associated sysfs transport objects  */
++ 	for (i = 0; i < host->n_ports; i++) {
++ 		rc = ata_tport_add(host->dev,host->ports[i]);
++--- a/include/linux/libata.h
+++++ b/include/linux/libata.h
++@@ -23,6 +23,9 @@
++ #include <linux/cdrom.h>
++ #include <linux/sched.h>
++ #include <linux/async.h>
+++#ifdef CONFIG_ATA_LEDS
+++#include <linux/leds.h>
+++#endif
 + 
-+ MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
-++MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
-++MODULE_AUTHOR("Marek Behún <kabel@kernel.org>");
-+ MODULE_DESCRIPTION("Common PHY driver for A3700");
-+ MODULE_LICENSE("GPL v2");
-diff --git a/target/linux/generic/pending-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch b/target/linux/generic/pending-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
++ /*
++  * Define if arch has non-standard setup.  This is a _PCI_ standard
++@@ -888,6 +891,12 @@ struct ata_port {
++ #ifdef CONFIG_ATA_ACPI
++ 	struct ata_acpi_gtm	__acpi_init_gtm; /* use ata_acpi_init_gtm() */
++ #endif
+++
+++#ifdef CONFIG_ATA_LEDS
+++	struct led_trigger	*ledtrig;
+++	char			ledtrig_name[8];
+++#endif
+++
++ 	/* owned by EH */
++ 	u8			sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
++ };
+diff --git a/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
 new file mode 100644
-index 0000000000..33203a154d
+index 0000000000..5ca8933d6f
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
-@@ -0,0 +1,31 @@
-+From 66c51c39fd4bf05e99debf0e71de5704231c57dc Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Thu, 23 Sep 2021 19:26:26 +0200
-+Subject: [PATCH] arm64: dts: marvell: armada-37xx: Add xtal clock to comphy
-+ node
++++ b/target/linux/generic/pending-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
+@@ -0,0 +1,26 @@
++From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001
++From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
++Date: Sat, 20 Feb 2021 18:36:38 +0100
++Subject: [PATCH] hwrng: bcm2835: set quality to 1000
 +MIME-Version: 1.0
 +Content-Type: text/plain; charset=UTF-8
 +Content-Transfer-Encoding: 8bit
 +
-+Kernel driver phy-mvebu-a3700-comphy.c needs to know the rate of the
-+reference xtal clock. So add missing xtal clock source into comphy device
-+tree node. If the property is not present, the driver defaults to 25 MHz
-+xtal rate (which, as far as we know, is used by all the existing boards).
++This allows devices without a high precission timer to reduce boot from >100s
++to <30s.
 +
-+Signed-off-by: Pali Rohár <pali@kernel.org>
-+Signed-off-by: Marek Behún <kabel@kernel.org>
++Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
 +---
-+ arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 2 ++
-+ 1 file changed, 2 insertions(+)
-+
-+--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
-++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
-+@@ -265,6 +265,8 @@
-+ 					    "lane2_sata_usb3";
-+ 				#address-cells = <1>;
-+ 				#size-cells = <0>;
-++				clocks = <&xtalclk>;
-++				clock-names = "xtal";
-+ 
-+ 				comphy0: phy@0 {
-+ 					reg = <0>;
-diff --git a/target/linux/generic/pending-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch b/target/linux/generic/pending-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
-new file mode 100644
-index 0000000000..3c994d2548
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
-@@ -0,0 +1,61 @@
-+From 750bb44dbbe9dfb4ba3e1f8a746b831b39ba3cd9 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Thu, 23 Sep 2021 19:35:57 +0200
-+Subject: [PATCH] Revert "ata: ahci: mvebu: Make SATA PHY optional for Armada
-+ 3720"
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+This reverts commit 45aefe3d2251e4e229d7662052739f96ad1d08d9.
-+
-+Armada 3720 PHY driver (phy-mvebu-a3700-comphy.c) does not return
-+-EOPNOTSUPP from phy_power_on() callback anymore.
-+
-+So remove AHCI_HFLAG_IGN_NOTSUPP_POWER_ON flag from Armada 3720 plat data.
-+
-+AHCI_HFLAG_IGN_NOTSUPP_POWER_ON is not used by any other ahci driver, so
-+remove this flag completely.
++ drivers/char/hw_random/bcm2835-rng.c | 1 +
++ 1 file changed, 1 insertion(+)
 +
-+Signed-off-by: Pali Rohár <pali@kernel.org>
-+Signed-off-by: Marek Behún <kabel@kernel.org>
-+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-+---
-+ drivers/ata/ahci.h             | 2 --
-+ drivers/ata/ahci_mvebu.c       | 2 +-
-+ drivers/ata/libahci_platform.c | 2 +-
-+ 3 files changed, 2 insertions(+), 4 deletions(-)
-+
-+--- a/drivers/ata/ahci.h
-++++ b/drivers/ata/ahci.h
-+@@ -240,8 +240,6 @@ enum {
-+ 							as default lpm_policy */
-+ 	AHCI_HFLAG_SUSPEND_PHYS		= (1 << 26), /* handle PHYs during
-+ 							suspend/resume */
-+-	AHCI_HFLAG_IGN_NOTSUPP_POWER_ON	= (1 << 27), /* ignore -EOPNOTSUPP
-+-							from phy_power_on() */
-+ 	AHCI_HFLAG_NO_SXS		= (1 << 28), /* SXS not supported */
-+ 
-+ 	/* ap->flags bits */
-+--- a/drivers/ata/ahci_mvebu.c
-++++ b/drivers/ata/ahci_mvebu.c
-+@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data
-+ 
-+ static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
-+ 	.plat_config = ahci_mvebu_armada_3700_config,
-+-	.flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON,
-++	.flags = AHCI_HFLAG_SUSPEND_PHYS,
-+ };
-+ 
-+ static const struct of_device_id ahci_mvebu_of_match[] = {
-+--- a/drivers/ata/libahci_platform.c
-++++ b/drivers/ata/libahci_platform.c
-+@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahc
-+ 		}
++--- a/drivers/char/hw_random/bcm2835-rng.c
+++++ b/drivers/char/hw_random/bcm2835-rng.c
++@@ -170,6 +170,7 @@ static int bcm2835_rng_probe(struct plat
++ 	priv->rng.init = bcm2835_rng_init;
++ 	priv->rng.read = bcm2835_rng_read;
++ 	priv->rng.cleanup = bcm2835_rng_cleanup;
+++	priv->rng.quality = 1000;
 + 
-+ 		rc = phy_power_on(hpriv->phys[i]);
-+-		if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) {
-++		if (rc) {
-+ 			phy_exit(hpriv->phys[i]);
-+ 			goto disable_phys;
-+ 		}
-diff --git a/target/linux/generic/pending-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch b/target/linux/generic/pending-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
++ 	if (dev_of_node(dev)) {
++ 		rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
+diff --git a/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
 new file mode 100644
-index 0000000000..b8a3e880ce
+index 0000000000..e180a385e1
 --- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
-@@ -0,0 +1,163 @@
-+From 9f0dfb279b1dd505d5e10b10e4a78a62030978d8 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Thu, 23 Sep 2021 19:40:06 +0200
-+Subject: [PATCH] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional for
-+ Armada 3720"
++++ b/target/linux/generic/pending-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
+@@ -0,0 +1,102 @@
++From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001
++From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
++Date: Mon, 10 Jan 2022 02:02:00 +0100
++Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver
++ structure
 +MIME-Version: 1.0
 +Content-Type: text/plain; charset=UTF-8
 +Content-Transfer-Encoding: 8bit
 +
-+This reverts commit 3241929b67d28c83945d3191c6816a3271fd6b85.
++Marc Zyngier says [1] that we should use struct irq_chip as a global
++static struct in the driver. Even though the structure currently
++contains a dynamic member (parent_device), Marc says [2] that he plans
++to kill it and make the structure completely static.
 +
-+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
-+-EOPNOTSUPP from phy_power_on() callback anymore.
++We have already converted others irq_chip structures in this driver in
++this way, but we omitted this one because the .name member is
++dynamically created from device's name, and the name is displayed in
++sysfs, so changing it would break sysfs ABI.
 +
-+So remove XHCI_SKIP_PHY_INIT flag from xhci_mvebu_a3700_plat_setup() and
-+then also whole xhci_mvebu_a3700_plat_setup() function which is there just
-+to handle -EOPNOTSUPP for XHCI_SKIP_PHY_INIT.
++The rationale for changing the name (to "advk-INT") in spite of sysfs
++ABI, and thus allowing to convert to a static structure, is that after
++the other changes we made in this series, the IRQ chip is basically
++something different: it no logner generates ERR and PME interrupts (they
++are generated by emulated bridge's rp_irq_chip).
 +
-+xhci plat_setup callback is not used by any other xhci plat driver, so
-+remove this callback completely.
++[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
++[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
 +
-+Signed-off-by: Pali Rohár <pali@kernel.org>
 +Signed-off-by: Marek Behún <kabel@kernel.org>
-+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
 +---
-+ drivers/usb/host/xhci-mvebu.c | 42 -----------------------------------
-+ drivers/usb/host/xhci-mvebu.h |  6 -----
-+ drivers/usb/host/xhci-plat.c  | 20 +----------------
-+ drivers/usb/host/xhci-plat.h  |  1 -
-+ 4 files changed, 1 insertion(+), 68 deletions(-)
-+
-+--- a/drivers/usb/host/xhci-mvebu.c
-++++ b/drivers/usb/host/xhci-mvebu.c
-+@@ -8,7 +8,6 @@
-+ #include <linux/mbus.h>
-+ #include <linux/of.h>
-+ #include <linux/platform_device.h>
-+-#include <linux/phy/phy.h>
-+ 
-+ #include <linux/usb.h>
-+ #include <linux/usb/hcd.h>
-+@@ -74,47 +73,6 @@ int xhci_mvebu_mbus_init_quirk(struct us
-+ 
-+ 	return 0;
-+ }
-+-
-+-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-+-{
-+-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-+-	struct device *dev = hcd->self.controller;
-+-	struct phy *phy;
-+-	int ret;
-+-
-+-	/* Old bindings miss the PHY handle */
-+-	phy = of_phy_get(dev->of_node, "usb3-phy");
-+-	if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER)
-+-		return -EPROBE_DEFER;
-+-	else if (IS_ERR(phy))
-+-		goto phy_out;
-+-
-+-	ret = phy_init(phy);
-+-	if (ret)
-+-		goto phy_put;
-+-
-+-	ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS);
-+-	if (ret)
-+-		goto phy_exit;
-+-
-+-	ret = phy_power_on(phy);
-+-	if (ret == -EOPNOTSUPP) {
-+-		/* Skip initializatin of XHCI PHY when it is unsupported by firmware */
-+-		dev_warn(dev, "PHY unsupported by firmware\n");
-+-		xhci->quirks |= XHCI_SKIP_PHY_INIT;
-+-	}
-+-	if (ret)
-+-		goto phy_exit;
-+-
-+-	phy_power_off(phy);
-+-phy_exit:
-+-	phy_exit(phy);
-+-phy_put:
-+-	of_phy_put(phy);
-+-phy_out:
-+-
-+-	return 0;
-+-}
-+ 
-+ int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
-+ {
-+--- a/drivers/usb/host/xhci-mvebu.h
-++++ b/drivers/usb/host/xhci-mvebu.h
-+@@ -12,18 +12,12 @@ struct usb_hcd;
-+ 
-+ #if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
-+ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
-+-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd);
-+ int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
-+ #else
-+ static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
-+ {
-+ 	return 0;
++ drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------
++ 1 file changed, 7 insertions(+), 18 deletions(-)
++
++--- a/drivers/pci/controller/pci-aardvark.c
+++++ b/drivers/pci/controller/pci-aardvark.c
++@@ -275,7 +275,6 @@ struct advk_pcie {
++ 	u8 wins_count;
++ 	struct irq_domain *rp_irq_domain;
++ 	struct irq_domain *irq_domain;
++-	struct irq_chip irq_chip;
++ 	raw_spinlock_t irq_lock;
++ 	struct irq_domain *msi_domain;
++ 	struct irq_domain *msi_inner_domain;
++@@ -1345,14 +1344,19 @@ static void advk_pcie_irq_unmask(struct
++ 	raw_spin_unlock_irqrestore(&pcie->irq_lock, flags);
 + }
-+-
-+-static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-+-{
-+-	return 0;
-+-}
 + 
-+ static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
+++static struct irq_chip advk_irq_chip = {
+++	.name		= "advk-INT",
+++	.irq_mask	= advk_pcie_irq_mask,
+++	.irq_unmask	= advk_pcie_irq_unmask,
+++};
+++
++ static int advk_pcie_irq_map(struct irq_domain *h,
++ 			     unsigned int virq, irq_hw_number_t hwirq)
 + {
-+--- a/drivers/usb/host/xhci-plat.c
-++++ b/drivers/usb/host/xhci-plat.c
-+@@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct
-+ 		priv->plat_start(hcd);
-+ }
++ 	struct advk_pcie *pcie = h->host_data;
 + 
-+-static int xhci_priv_plat_setup(struct usb_hcd *hcd)
-+-{
-+-	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
-+-
-+-	if (!priv->plat_setup)
-+-		return 0;
-+-
-+-	return priv->plat_setup(hcd);
-+-}
-+-
-+ static int xhci_priv_init_quirk(struct usb_hcd *hcd)
-+ {
-+ 	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
-+@@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_
-+ };
++ 	irq_set_status_flags(virq, IRQ_LEVEL);
++-	irq_set_chip_and_handler(virq, &pcie->irq_chip,
++-				 handle_level_irq);
+++	irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq);
++ 	irq_set_chip_data(virq, pcie);
 + 
-+ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
-+-	.plat_setup = xhci_mvebu_a3700_plat_setup,
-+ 	.init_quirk = xhci_mvebu_a3700_init_quirk,
-+ };
++ 	return 0;
++@@ -1411,7 +1415,6 @@ static int advk_pcie_init_irq_domain(str
++ 	struct device *dev = &pcie->pdev->dev;
++ 	struct device_node *node = dev->of_node;
++ 	struct device_node *pcie_intc_node;
++-	struct irq_chip *irq_chip;
++ 	int ret = 0;
 + 
-+@@ -341,14 +330,7 @@ static int xhci_plat_probe(struct platfo
++ 	raw_spin_lock_init(&pcie->irq_lock);
++@@ -1422,28 +1425,14 @@ static int advk_pcie_init_irq_domain(str
++ 		return -ENODEV;
++ 	}
 + 
-+ 	hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
-+ 	xhci->shared_hcd->tpl_support = hcd->tpl_support;
++-	irq_chip = &pcie->irq_chip;
 +-
-+-	if (priv) {
-+-		ret = xhci_priv_plat_setup(hcd);
-+-		if (ret)
-+-			goto disable_usb_phy;
++-	irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
++-					dev_name(dev));
++-	if (!irq_chip->name) {
++-		ret = -ENOMEM;
++-		goto out_put_node;
 +-	}
 +-
-+-	if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)))
-++	if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
-+ 		hcd->skip_phy_initialization = 1;
-+ 
-+ 	if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
-+--- a/drivers/usb/host/xhci-plat.h
-++++ b/drivers/usb/host/xhci-plat.h
-+@@ -13,7 +13,6 @@
-+ struct xhci_plat_priv {
-+ 	const char *firmware_name;
-+ 	unsigned long long quirks;
-+-	int (*plat_setup)(struct usb_hcd *);
-+ 	void (*plat_start)(struct usb_hcd *);
-+ 	int (*init_quirk)(struct usb_hcd *);
-+ 	int (*suspend_quirk)(struct usb_hcd *);
-diff --git a/target/linux/generic/pending-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch b/target/linux/generic/pending-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
-new file mode 100644
-index 0000000000..4425e3f86b
---- /dev/null
-+++ b/target/linux/generic/pending-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
-@@ -0,0 +1,36 @@
-+From 9a352062b7e3857742389dff6f64393481dc755e Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
-+Date: Thu, 23 Sep 2021 19:37:05 +0200
-+Subject: [PATCH] Revert "PCI: aardvark: Fix initialization with old Marvell's
-+ Arm Trusted Firmware"
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+This reverts commit b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6.
-+
-+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
-+-EOPNOTSUPP from phy_power_on() callback anymore.
-+
-+So remove dead code which handles -EOPNOTSUPP return value.
-+
-+Signed-off-by: Pali Rohár <pali@kernel.org>
-+Signed-off-by: Marek Behún <kabel@kernel.org>
-+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-+---
-+ drivers/pci/controller/pci-aardvark.c | 4 +---
-+ 1 file changed, 1 insertion(+), 3 deletions(-)
-+
-+--- a/drivers/pci/controller/pci-aardvark.c
-++++ b/drivers/pci/controller/pci-aardvark.c
-+@@ -1616,9 +1616,7 @@ static int advk_pcie_enable_phy(struct a
++-	irq_chip->irq_mask = advk_pcie_irq_mask;
++-	irq_chip->irq_unmask = advk_pcie_irq_unmask;
++-
++ 	pcie->irq_domain =
++ 		irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++ 				      &advk_pcie_irq_domain_ops, pcie);
++ 	if (!pcie->irq_domain) {
++ 		dev_err(dev, "Failed to get a INTx IRQ domain\n");
++ 		ret = -ENOMEM;
++-		goto out_put_node;
 + 	}
 + 
-+ 	ret = phy_power_on(pcie->phy);
-+-	if (ret == -EOPNOTSUPP) {
-+-		dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n");
-+-	} else if (ret) {
-++	if (ret) {
-+ 		phy_exit(pcie->phy);
-+ 		return ret;
-+ 	}
++-out_put_node:
++ 	of_node_put(pcie_intc_node);
++ 	return ret;
++ }
 diff --git a/target/linux/generic/pending-5.15/920-mangle_bootargs.patch b/target/linux/generic/pending-5.15/920-mangle_bootargs.patch
 new file mode 100644
-index 0000000000..dbcd6a5b32
+index 0000000000..2a02efe0aa
 --- /dev/null
 +++ b/target/linux/generic/pending-5.15/920-mangle_bootargs.patch
 @@ -0,0 +1,71 @@
@@ -15364,7 +18257,7 @@ index 0000000000..dbcd6a5b32
 + 	help
 +--- a/init/main.c
 ++++ b/init/main.c
-+@@ -615,6 +615,29 @@ static inline void setup_nr_cpu_ids(void
++@@ -616,6 +616,29 @@ static inline void setup_nr_cpu_ids(void
 + static inline void smp_prepare_cpus(unsigned int maxcpus) { }
 + #endif
 + 
@@ -15394,7 +18287,7 @@ index 0000000000..dbcd6a5b32
 + /*
 +  * We need to store the untouched command line for future reference.
 +  * We also need to store the touched command line since the parameter
-+@@ -955,6 +978,7 @@ asmlinkage __visible void __init __no_sa
++@@ -956,6 +979,7 @@ asmlinkage __visible void __init __no_sa
 + 	pr_notice("%s", linux_banner);
 + 	early_security_init();
 + 	setup_arch(&command_line);
-- 
GitLab